有没有办法实例化一个类而不调用__init__?
有没有办法绕过python类的构造函数__init__
?
例:
class A(object): def __init__(self): print "FAILURE" def Print(self): print "YEHAA"
现在我想创build一个A
的实例。 它可能看起来像这样,但是这个语法是不正确的。
a = A a.Print()
编辑:
一个更复杂的例子:
假设我有一个对象C
,目的是存储一个单一的参数,并用它做一些计算。 然而,这个参数并没有被传递,而是被embedded到一个巨大的参数文件中。 它可能看起来像这样:
class C(object): def __init__(self, ParameterFile): self._Parameter = self._ExtractParamterFile(ParameterFile) def _ExtractParamterFile(self, ParameterFile): #does some complex magic to extract the right parameter return the_extracted_parameter
现在我想转储并加载该对象C
一个实例。 然而,当我加载这个对象时,我只有一个variablesself._Parameter
,我不能调用构造函数,因为它期望参数文件。
@staticmethod def Load(file): f = open(file, "rb") oldObject = pickle.load(f) f.close() #somehow create newObject without calling __init__ newObject._Parameter = oldObject._Parameter return newObject
换句话说,不通过参数文件就无法创build实例。 然而,在我的“真实”情况下,它不是一个参数文件,而是一些我不想在内存中携带的大量数据,甚至是存储到磁盘上。
而且,因为我想从Load
方法返回一个C
的实例,所以不得不调用构造函数。
老编辑:
一个更复杂的例子,这解释了为什么我问这个问题:
class B(object): def __init__(self, name, data): self._Name = name #do something with data, but do NOT save data in a variable @staticmethod def Load(self, file, newName): f = open(file, "rb") s = pickle.load(f) f.close() newS = B(???) newS._Name = newName return newS
正如你所看到的,由于data
没有存储在类variables中,所以我不能将它传递给__init__
。 当然,我可以简单地存储它,但如果数据是一个巨大的对象,我不想随身携带,甚至把它保存到光盘上呢?
你可以通过直接调用__new__
来绕过__init__
。 然后你可以创build一个给定types的对象并为__init__
调用另一种方法。 这是pickle
会做的。
但是,首先我要强调的是,这是你不应该做的事情,无论你想达到什么目的,都有更好的方法去做,其中一些已经在其他答案中提到了。 特别是,跳过调用__init__
是一个坏主意。
当创build对象时,或多或less发生这种情况:
a = A.__new__(A, *args, **kwargs) a.__init__(*args, **kwargs)
你可以跳过第二步。
这是为什么你不应该这样做: __init__
的目的是初始化对象,填写所有的字段,并确保父类的__init__
方法也被调用。 pickle
是一个例外,因为它试图存储与对象相关的所有数据(包括为对象设置的任何字段/实例variables),所以上一次由__init__
设置的任何东西都会被pickle恢复,不需要再次调用它。
如果你跳过__init__
并使用另一个初始值设定项,那么你将会有一种代码重复项 – 将会有两个地方填充实例variables,并且很容易在其中一个初始值设定项中错过其中的一个或意外地两个填充字段的行为不同。 这就提供了一些微不足道的细微错误(你必须知道调用了哪个初始化方法),代码会更难维护。 更何况,如果你使用inheritance,你将会陷入更大的混乱之中 – 问题会延续到inheritance链上,因为你不得不在这个链的任何地方使用这个替代的初始化器。
同样这样做,你会或多或less地重写Python的实例创build并创build自己的实例。 Python已经为你做了很好的工作,不需要重新创build它,它会混淆使用你的代码的人。
下面是最好的做法:使用一个单一的__init__
方法来调用所有可能的实例化类,以正确地初始化所有的实例variables。 对于不同的初始化模式,可以使用以下两种方法之一:
- 支持
__init__
不同签名,通过使用可选参数来处理您的案例。 - 创build几个用作替代构造函数的类方法。 确保他们都以正常的方式创build类的实例(即调用
__init__
),如Roman Bodnarchuk所示,同时执行额外的工作或任何其他。 最好是将所有数据传递给类(而__init__
处理它),但是如果不可能或不方便,可以在创build实例并__init__
完成初始化之后设置一些实例variables。
如果__init__
有一个可选的步骤(例如像处理data
参数,尽pipe你必须更具体),你可以使它成为一个可选的参数或做一个正常的方法来处理…或两者。
为你的Load
方法使用classmethod
装饰器:
class B(object): def __init__(self, name, data): self._Name = name #store data @classmethod def Load(cls, file, newName): f = open(file, "rb") s = pickle.load(f) f.close() return cls(newName, s)
所以你可以这样做:
loaded_obj = B.Load('filename.txt', 'foo')
编辑:
无论如何,如果您仍然想要省略__init__
方法,请尝试__new__
:
>>> class A(object): ... def __init__(self): ... print '__init__' ... >>> A() __init__ <__main__.A object at 0x800f1f710> >>> a = A.__new__(A) >>> a <__main__.A object at 0x800f1fd50>
从字面上考虑你的问题,我会使用元类:
class MetaSkipInit(type): def __call__(cls): return cls.__new__(cls) class B(object): __metaclass__ = MetaSkipInit def __init__(self): print "FAILURE" def Print(self): print "YEHAA" b = B() b.Print()
这可以用于例如复制构造函数而不污染参数列表。 但是要正确地做到这一点,将比我提出的黑客更多的工作和照顾。
不是真的。 __init__
的目的是实例化一个对象,默认情况下它不会做任何事情。 如果__init__
方法没有做你想做的,而不是你自己的代码来改变,你可以select切换它。 例如,把你的类A,我们可以做以下,以避免调用__init__
方法:
def emptyinit(self): pass A.__init__ = emptyinit a = A() a.Print()
这将dynamic地从类中将__init__
方法切换出来,并用空的调用replace它。 请注意,这可能不是一件好事,因为它不调用超类的__init__
方法。
你也可以创build自己的类来创build你自己的类,除了重写__init__
方法来做你想做的事(也许没有什么)。
但是,也许只是希望从类中调用方法而不实例化一个对象。 如果是这样的话,你应该看看@classmethod和@staticmethod装饰器。 他们只允许这种types的行为。
在你的代码中,你已经把@staticmethod装饰器,它不采取self
参数。 也许更好的目的可能是一个@classmethod,可能看起来更像这样:
@classmethod def Load(cls, file, newName): # Get the data data = getdata() # Create an instance of B with the data return cls.B(newName, data)
更新:Rosh的优秀答案指出,你可以通过实现__new__
避免调用__init__
,我实际上并没有意识到(虽然它是非常有意义的)。 感谢Rosh!
我正在阅读Python的食谱,有一节讨论这个问题:这个例子是用__new__
来绕过__init__()
>>> A类: def __init __(self,a): self.a = a >>> test = A('a') >>> test.a '一个' >>> test_noinit = A .__ new __(A) >>> test_noinit.a 回溯(最近一次通话最后): 文件“”,第1行, test_noinit.a AttributeError:'A'对象没有属性'a' >>>
不过我认为这只适用于Python3。 下面是在2.7下运行
>>> A类: def __init __(self,a): self.a = a >>> test = A .__ new __(A) 回溯(最近一次通话最后): 文件“”,第1行, test = A .__ new __(A) AttributeError:类A没有属性'__new__' >>>
正如我在我的评论中所说的,你可以改变你的__init__
方法,这样它就可以在不给参数赋予任何值的情况下创build:
def __init__(self, p0, p1, p2): # some logic
会成为:
def __init__(self, p0=None, p1=None, p2=None): if p0 and p1 and p2: # some logic
要么:
def __init__(self, p0=None, p1=None, p2=None, init=True): if init: # some logic