0

我正在编写一个具有多个构造函数的类,使用@classmethod. 现在我希望__init__构造函数和类方法构造函数都调用类的一些例程来设置初始值,然后再执行其他操作。

__init__这通常是用 self 完成的:

def __init__(self, name="", revision=None):
    self._init_attributes()

def _init_attributes(self):
    self.test = "hello"

从类方法构造函数中,我会改为调用另一个类方法,因为在self我将类方法留给return cls(...). 现在,我可以将我的_init_attributes()方法称为

@classmethod
def from_file(cls, filename=None)
    cls._init_attributes()
    # do other stuff like reading from file
    return cls()

这实际上有效(从某种意义上说,我没有收到错误,并且在执行后我实际上可以看到 test 属性c = Class.from_file()。但是,如果我理解正确,那么这将在类级别上设置属性,而不是在实例上因此,如果我用一个可变对象(例如一个列表)初始化一个属性,那么这个类的所有实例都将使用同一个列表,而不是它们自己的实例列表。这是正确的吗?如果是这样,有没有办法在类方法中初始化“实例”属性,还是我必须以所有属性初始化都在init中完成的方式编写代码?

嗯。实际上,在写这篇文章时:我什至可能遇到比我想象的更大的麻烦,因为从类方法返回时会调用init ,不是吗?那么处理这种情况的正确方法是什么?

注意:文章 [ 1 ] 讨论了一个类似的问题。

4

1 回答 1

1

是的,您理解正确:cls._init_attributes()将设置类属性,而不是实例属性。

同时,由您的替代构造函数来构造和返回一个实例。在构造它和返回它之间,你可以调用_init_attributes(). 换句话说:

@classmethod
def from_file(cls, filename=None)
    obj = cls()
    obj._init_attributes()
    # do other stuff like reading from file
    return obj

但是,您是对的,构造和返回实例的唯一明显方法就是调用cls(),这将调用__init__.


但这很容易解决:只需让备用构造函数传递一些额外的参数来__init__表示“跳过通常的初始化,我稍后再做”。例如:

def __init__(self, name="", revision=None, _skip_default_init=False):
    # blah blah

@classmethod
def from_file(cls, filename=""):
    # blah blah setup
    obj = cls(_skip_default_init=True)
    # extra initialization work
    return obj

如果你想让它不那么明显,你可以随时**kwargs在方法体中检查它……但请记住,这是 Python;你不能阻止人们做愚蠢的事情,你所能做的就是让人们明白他们是愚蠢的。_skip_default_init应该足以应付这个问题。


如果你真的想要,你也可以覆盖__new____init__除非__new__返回其实例cls或其某个子类,否则构造对象不会调用。所以,你可以给__new__一个标志,告诉它__init__通过 munging跳过obj.__class__,然后恢复__class__你自己。这真的很hacky,但可以想象是有用的。


一个更简洁的解决方案——但由于某种原因在 Python 中更不常见——是从 Smalltalk/ObjC 中借用“类集群”的想法:创建一个私有子类,它有一个不同__init__super(或故意跳过它的直接基础和supers 从那里开始),然后让基类中的备用构造函数返回该子类的实例。


或者,如果你不想打电话的唯一原因__init__是你可以做同样的事情__init__……为什么?DRY 代表“不要重复自己”,而不是“向后弯腰想办法强迫自己重复自己”,对吧?

于 2013-10-10T00:53:26.017 回答