从类方法创建新的类实例

59

我希望能够通过调用已经实例化对象的方法来创建一个新的对象实例。例如,我有以下对象:

organism = Organism()

我希望能够调用organism.reproduce()方法,并获得两个类型为Organism的对象。目前我的方法看起来像这样:

class Organism(object):
    def reproduce():
        organism = Organism()

我很确定它不起作用(我甚至不知道如何测试它。我在这个帖子中尝试了gc方法)。那么,我该如何让我的对象创建一个可访问的副本,就像我创建的第一个对象一样(使用organism = Organism())?


我猜当你说“reproduce”时,实际上你是指“clone”,对吗?如果是这样的话,告诉我们你的“Organism”的定义特征/属性将会极大地帮助我们正确地进行操作。 - rantanplan
6个回答

100
class Organism(object):
    def reproduce(self):
        #use self here to customize the new organism ...
        return Organism()

如果方法中没有使用实例 (self),则另一种选择是:

class Organism(object):
    @classmethod
    def reproduce(cls):
        return cls()

这确保生物会产生更多的生物,而从生物派生出来的假设性博格也会产生更多的博格。

不需要使用self的一个附带好处是,现在可以直接从类中调用它,而不仅仅是从实例中调用:

new_organism0 = Organism.reproduce()  # Creates a new organism
new_organism1 = new_organism0.reproduce()  # Also creates a new organism

最后,如果方法内同时使用了实例(self)和类(Organism或其子类(如果是从子类中调用)):


class Organism(object):
    def reproduce(self):
        #use self here to customize the new organism ...
        return self.__class__()  # same as cls = type(self); return cls()
在每种情况下,您将使用它如下:
organism = Organism()
new_organism = organism.reproduce()

我发现这个解释非常有帮助,但是你能详细说明一下你所说的“如果实例不重要”与“如果实例和类都很重要”的含义吗?在我的情况下,@classmethod方法和self.__class__()方法都可以工作:我有一个基类方法get_copy,它创建了子类的新实例(?),然后我使用__dict__将新的子类转换为原始类的“副本”。在这种情况下,哪种方法更合适? - isosceleswheel
@isosceleswheel -- 我稍微更新了一下文本。基本上我在谈论函数需要哪些数据来创建一个新实例。换句话说,“如果实例不重要”就等同于说“如果函数中没有使用self”。希望更新后的文本更加清晰明了。 - mgilson

3
为什么不直接使用复制模块?
import copy
organism = Organism()
replica = copy.deepcopy(organism)

3

这个东西怎么样:

class Organism(object):

    population = []

    def __init__(self, name):
        self.name = name
        self.population.append(self)
    def have_one_child(self, name):
        return Organism(name)
    def reproduce(self, names):
        return [self.have_one_child(name) for name in names]

结果:

>>> a = Organism('a')
>>> len(Organism.population)
1
>>> a.reproduce(['x', 'y', 'z']) # when one organism reproduces, children are added
                                 # to the total population
                                 # organism produces as many children as you state
[<__main__.Organism object at 0x05F23190>, <__main__.Organism object at 0x05F230F0>, <__main__.Organism object at 0x05F23230>]
>>> for ele in Organism.population:
...     print ele.name
... 
a
x
y
z
>>> Organism.population[3].reproduce(['f', 'g'])
[<__main__.Organism object at 0x05F231D0>, <__main__.Organism object at 0x05F23290>]
>>> for ele in Organism.population:
...     print ele.name
... 
a
x
y
z
f
g

1

你需要像最初一样,但是接下来你需要对它进行一些操作!

organism = Organism() 调用了类 Organism (名称后面的括号是“调用”操作)。这将创建并返回该类的一个新实例,然后将其绑定到名称 organism

当你在解释器中执行该行时,现在你有一个变量 organism,它引用了你刚刚创建的新的 Organism 实例。

当你在函数内部编写该行(包括方法,因为从内部来看方法和函数没有区别),它做的事情相同,但变量 organism 是一个局部变量。局部变量在函数完成后被丢弃,因此这确实创建了一个新的 Organism 实例,但它并没有实现任何目标,因为你无法访问它。

你的函数应该返回它想要传达给调用者的任何信息。你不返回的任何局部变量只有在你使用这些变量创建了你返回的内容时才有用。

请注意,这与您在方法内创建实例的特定问题无关;这只是函数/方法通常的工作方式。在成功编写使用类和实例的面向对象程序之前,您需要了解函数的工作原理;我强烈建议您通过一些教程来学习。

1
我相信您正在询问如何复制一个对象。 令人惊讶的是(也许),这几乎没有标准方法,这是有意而为之的。问题在于复制的内在歧义,即:当您复制对象属性时,您是要将其作为引用(copy)还是作为值(deepcopy)进行复制?
然而,在大多数情况下,您希望获得一致的行为(对所有属性进行深拷贝或浅拷贝),在这种情况下,您可以使用copy模块。
import copy
new_obj = copy.copy(old_obj)

或者

new_obj = copy.deepcopy(old_obj)

在一般情况下,如果您想要更加定制化的行为,可以使用相同的命令,但覆盖对象的__copy____deepcopy__方法。
有关详细信息和示例,请参阅更多答案,例如: 如何覆盖Python对象的复制/深度复制操作?

好的回答,但附加在错误的问题上了。 - TheGrimmScientist
谢谢!但是你为什么这么说呢?我理解这就是在问“那么,如何让我的对象创建一个可像第一个对象一样访问的副本..”(也来自问题的第一条评论,我不是唯一一个持相同观点的人)。 - Vincenzooo
你的解决方案是否展示了如何通过“调用已实例化对象上的方法”来制作副本?不是这样的。你自己制作了副本,而不是在/在old_obj中定义并调用函数。 - TheGrimmScientist
对于通用的复制,您可以编写一个方法(例如 old_obj.replicate()),该方法返回 copy.copy(self),但是使用这种方法的理由很少:您可以直接调用 copy.copy(old_obj)。我认为这是制作副本的标准方式,因为它甚至可以与更多对象特定的方法(例如复制某些属性并引用其他属性)一起使用,这些方法会覆盖对象的 __copy__ 方法。 - Vincenzooo

0
from copy import copy                                                           

class Organism(object):                                                         

    def __init__(self,name):                                                    
        self.name=name                                                          

    def setName(self,name):                                                     
        self.name=name                                                          

    def reproduce(self,childname):     
        #use deepcopy if necessary                                         
        cp = copy(self)                                                         
        cp.setName("descendant from " + self.name + " " + childname)            
        return cp                                                               

    def __str__(self):                                                          
        return self.name                                                        

first = Organism("first")                                                       
second = first.reproduce("second")                                              

print first                                                                     
print second 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接