有没有办法在不调用__init__的情况下实例化一个类?

时间:2022-03-30 18:21:56

Is there a way to circumvent the constructor __init__ of a class in python?

有没有办法绕过python中类的构造函数__init__?

Example:

例:

class A(object):    
    def __init__(self):
        print "FAILURE"

    def Print(self):
        print "YEHAA"

Now I would like to create an instance of A. It could look like this, however this syntax is not correct.

现在我想创建一个A的实例。它可能看起来像这样,但是这种语法不正确。

a = A
a.Print()

EDIT:

编辑:

An even more complex example:

一个更复杂的例子:

Suppose I have an object C, which purpose it is to store one single parameter and do some computations with it. The parameter, however, is not passed as such but it is embedded in a huge parameter file. It could look something like this:

假设我有一个对象C,其目的是存储一个参数并对其进行一些计算。但是,该参数不是这样传递的,而是嵌入在一个巨大的参数文件中。它可能看起来像这样:

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

Now I would like to dump and load an instance of that object C. However, when I load this object, I only have the single variable self._Parameter and I cannot call the constructor, because it is expecting the parameter file.

现在我想转储并加载该对象的实例C.但是,当我加载这个对象时,我只有单个变量self._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

In other words, it is not possible to create an instance without passing the parameter file. In my "real" case, however, it is not a parameter file but some huge junk of data I certainly not want to carry around in memory or even store it to disc.

换句话说,如果不传递参数文件,则无法创建实例。然而,在我的“真实”情况下,它不是一个参数文件,而是一些巨大的数据垃圾,我当然不想在内存中随身携带,甚至将它存储到光盘中。

And since I want to return an instance of C from the method Load I do somehow have to call the constructor.

因为我想从Load方法返回一个C实例,所以我必须以某种方式调用构造函数。

OLD EDIT:

旧编辑:

A more complex example, which explains why I am asking the question:

一个更复杂的例子,它解释了我在问这个问题的原因:

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

As you can see, since data is not stored in a class variable I cannot pass it to __init__. Of course I could simply store it, but what if the data is a huge object, which I do not want to carry around in memory all the time or even save it to disc?

如您所见,由于数据未存储在类变量中,因此无法将其传递给__init__。当然我可以简单地存储它,但如果数据是一个巨大的对象,我不想一直在内存中携带甚至将其保存到光盘呢?

6 个解决方案

#1


45  

You can circumvent __init__ by calling __new__ directly. Then you can create a object of the given type and call an alternative method for __init__. This is something that pickle would do.

您可以通过直接调用__new__来规避__init__。然后,您可以创建给定类型的对象,并为__init__调用替代方法。这是泡菜会做的事情。

However, first I'd like to stress very much that it is something that you shouldn't do and whatever you're trying to achieve, there are better ways to do it, some of which have been mentioned in the other answers. In particular, it's a bad idea to skip calling __init__.

然而,首先我要强调的是,这是你不应该做的事情,无论你想要做什么,有更好的方法去做,其中一些已在其他答案中提到过。特别是,跳过调用__init__是个坏主意。

When objects are created, more or less this happens:

创建对象时,或多或少会发生这种情况:

a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)

You could skip the second step.

你可以跳过第二步。

Here's why you shouldn't do this: The purpose of __init__ is to initialize the object, fill in all the fields and ensure that the __init__ methods of the parent classes are also called. With pickle it is an exception because it tries to store all the data associated with the object (including any fields/instance variables that are set for the object), and so anything that was set by __init__ the previous time would be restored by pickle, there's no need to call it again.

这就是你不应该这样做的原因:__ init__的目的是初始化对象,填写所有字段并确保也调用父类的__init__方法。使用pickle它是一个例外,因为它试图存储与该对象关联的所有数据(包括为该对象设置的任何字段/实例变量),因此上一次由__init__设置的任何内容都将由pickle恢复,没有必要再打电话。

If you skip __init__ and use an alternative initializer, you'd have a sort of a code duplication - there would be two places where the instance variables are filled in, and it's easy to miss one of them in one of the initializers or accidentally make the two fill the fields act differently. This gives the possibility of subtle bugs that aren't that trivial to trace (you'd have to know which initializer was called), and the code will be more difficult to maintain. Not to mention that you'd be in an even bigger mess if you're using inheritance - the problems will go up the inheritance chain, because you'd have to use this alternative initializer everywhere up the chain.

如果你跳过__init__并使用另一个初始化程序,你会有一种代码重复 - 有两个地方填充实例变量,并且很容易在其中一个初始化程序中错过其中一个或意外制作两个填充字段的行为不同。这给出了微小的错误的可能性,这些错误不是那么简单(你必须知道调用了哪个初始化程序),并且代码将更难维护。更不用说如果你使用继承,你将陷入更大的混乱 - 问题会在继承链上出现,因为你必须在链的各个地方使用这个替代的初始化器。

Also by doing so you'd be more or less overriding Python's instance creation and making your own. Python already does that for you pretty well, no need to go reinventing it and it will confuse people using your code.

通过这样做,你或多或少会覆盖Python的实例创建并创建自己的实例。 Python已经很好地为您完成了这项工作,无需重新设计它,它会让使用您代码的人感到困惑。

Here's what to best do instead: Use a single __init__ method that is to be called for all possible instantiations of the class that initializes all instance variables properly. For different modes of initialization use either of the two approaches:

以下是最好的方法:使用单个__init__方法,该方法可以为类的所有可能实例化调用,以正确初始化所有实例变量。对于不同的初始化模式,请使用以下两种方法之一:

  1. Support different signatures for __init__ that handle your cases by using optional arguments.
  2. 支持__init__的不同签名,通过使用可选参数来处理您的案例。
  3. Create several class methods that serve as alternative constructors. Make sure they all create instances of the class in the normal way (i.e. calling __init__), as shown by Roman Bodnarchuk, while performing additional work or whatever. It's best if they pass all the data to the class (and __init__ handles it), but if that's impossible or inconvenient, you can set some instance variables after the instance was created and __init__ is done initializing.
  4. 创建几个用作替代构造函数的类方法。确保他们以正常方式创建类的实例(即调用__init__),如Roman Bodnarchuk所示,同时执行其他工作或其他任何工作。它最好是将所有数据传递给类(并且__init__处理它),但如果这是不可能或不方便的,您可以在创建实例并且__init__完成初始化后设置一些实例变量。

If __init__ has an optional step (e.g. like processing that data argument, although you'd have to be more specific), you can either make it an optional argument or make a normal method that does the processing... or both.

如果__init__有一个可选步骤(例如处理数据参数,尽管你必须更具体),你可以使它成为一个可选参数,或者做一个正常的方法来处理......或两者兼而有之。

#2


15  

Use classmethod decorator for your Load method:

为您的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)

So you can do:

所以你可以这样做:

loaded_obj = B.Load('filename.txt', 'foo')

Edit:

编辑:

Anyway, if you still want to omit __init__ method, try __new__:

无论如何,如果您仍想省略__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>

#3


9  

Taking your question literally I would use meta classes :

从字面上看你的问题我会使用元类:

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()

This can be useful e.g. for copying constructors without polluting the parameter list. But to do this properly would be more work and care than my proposed hack.

这可能是有用的,例如用于复制构造函数而不会污染参数列表。但要正确地做到这一点将比我提出的黑客更多的工作和关心。

#4


8  

Not really. The purpose of __init__ is to instantiate an object, and by default it really doesn't do anything. If the __init__ method is not doing what you want, and it's not your own code to change, you can choose to switch it out though. For example, taking your class A, we could do the following to avoid calling that __init__ method:

不是真的。 __init__的目的是实例化一个对象,默认情况下它实际上没有做任何事情。如果__init__方法没有按照您的要求进行操作,并且您不需要更改自己的代码,则可以选择将其切换出来。例如,考虑到A类,我们可以执行以下操作以避免调用__init__方法:

def emptyinit(self):
    pass
A.__init__ = emptyinit
a = A()
a.Print()

This will dynamically switch out which __init__ method from the class, replacing it with an empty call. Note that this is probably NOT a good thing to do, as it does not call the super class's __init__ method.

这将动态地从类中切换出__init__方法,将其替换为空调用。请注意,这可能不是一件好事,因为它不会调用超类的__init__方法。

You could also subclass it to create your own class that does everything the same, except overriding the __init__ method to do what you want it to (perhaps nothing).

您也可以创建自己的类来创建自己的类,除了重写__init__方法以执行您想要的操作(可能没有)。

Perhaps, however, you simply wish to call the method from the class without instantiating an object. If that is the case, you should look into the @classmethod and @staticmethod decorators. They allow for just that type of behavior.

但是,您可能希望在不实例化对象的情况下从类中调用该方法。如果是这种情况,您应该查看@classmethod和@staticmethod装饰器。他们只允许这种行为。

In your code you have put the @staticmethod decorator, which does not take a self argument. Perhaps what may be better for the purpose would a @classmethod, which might look more like this:

在您的代码中,您放置了@staticmethod装饰器,它不带自参数。对于这个目的而言可能更好的是@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)

UPDATE: Rosh's Excellent answer pointed out that you CAN avoid calling __init__ by implementing __new__, which I was actually unaware of (although it makes perfect sense). Thanks Rosh!

更新:Rosh的优秀答案指出你可以通过实现__new__来避免调用__in__,我实际上并不知道(尽管它很有意义)。谢谢Rosh!

#5


5  

I was reading the Python cookbook and there's a section talking about this: the example is given using __new__ to bypass __init__()

我正在阅读Python食谱,并且有一节讨论这个:使用__new__来绕过__init __()给出了一个例子

>>> class A:
    def __init__(self,a):
        self.a = a


>>> test = A('a')
>>> test.a
'a'
>>> test_noinit = A.__new__(A)
>>> test_noinit.a
Traceback (most recent call last):
  File "", line 1, in 
    test_noinit.a
AttributeError: 'A' object has no attribute 'a'
>>> 

However I think this only works in Python3. Below is running under 2.7

但是我认为这只适用于Python3。以下是在2.7下运行

>>> class A:
    def __init__(self,a):
        self.a = a


>>> test = A.__new__(A)

Traceback (most recent call last):
  File "", line 1, in 
    test = A.__new__(A)
AttributeError: class A has no attribute '__new__'
>>> 

#6


2  

As I said in my comment you could change your __init__ method so that it allows creation without giving any values to its parameters:

正如我在评论中所说,您可以更改__init__方法,以便允许创建而不为其参数赋予任何值:

def __init__(self, p0, p1, p2):
   # some logic

would become:

会成为:

def __init__(self, p0=None, p1=None, p2=None):
   if p0 and p1 and p2:
       # some logic

or:

要么:

def __init__(self, p0=None, p1=None, p2=None, init=True):
    if init:
        # some logic

#1


45  

You can circumvent __init__ by calling __new__ directly. Then you can create a object of the given type and call an alternative method for __init__. This is something that pickle would do.

您可以通过直接调用__new__来规避__init__。然后,您可以创建给定类型的对象,并为__init__调用替代方法。这是泡菜会做的事情。

However, first I'd like to stress very much that it is something that you shouldn't do and whatever you're trying to achieve, there are better ways to do it, some of which have been mentioned in the other answers. In particular, it's a bad idea to skip calling __init__.

然而,首先我要强调的是,这是你不应该做的事情,无论你想要做什么,有更好的方法去做,其中一些已在其他答案中提到过。特别是,跳过调用__init__是个坏主意。

When objects are created, more or less this happens:

创建对象时,或多或少会发生这种情况:

a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)

You could skip the second step.

你可以跳过第二步。

Here's why you shouldn't do this: The purpose of __init__ is to initialize the object, fill in all the fields and ensure that the __init__ methods of the parent classes are also called. With pickle it is an exception because it tries to store all the data associated with the object (including any fields/instance variables that are set for the object), and so anything that was set by __init__ the previous time would be restored by pickle, there's no need to call it again.

这就是你不应该这样做的原因:__ init__的目的是初始化对象,填写所有字段并确保也调用父类的__init__方法。使用pickle它是一个例外,因为它试图存储与该对象关联的所有数据(包括为该对象设置的任何字段/实例变量),因此上一次由__init__设置的任何内容都将由pickle恢复,没有必要再打电话。

If you skip __init__ and use an alternative initializer, you'd have a sort of a code duplication - there would be two places where the instance variables are filled in, and it's easy to miss one of them in one of the initializers or accidentally make the two fill the fields act differently. This gives the possibility of subtle bugs that aren't that trivial to trace (you'd have to know which initializer was called), and the code will be more difficult to maintain. Not to mention that you'd be in an even bigger mess if you're using inheritance - the problems will go up the inheritance chain, because you'd have to use this alternative initializer everywhere up the chain.

如果你跳过__init__并使用另一个初始化程序,你会有一种代码重复 - 有两个地方填充实例变量,并且很容易在其中一个初始化程序中错过其中一个或意外制作两个填充字段的行为不同。这给出了微小的错误的可能性,这些错误不是那么简单(你必须知道调用了哪个初始化程序),并且代码将更难维护。更不用说如果你使用继承,你将陷入更大的混乱 - 问题会在继承链上出现,因为你必须在链的各个地方使用这个替代的初始化器。

Also by doing so you'd be more or less overriding Python's instance creation and making your own. Python already does that for you pretty well, no need to go reinventing it and it will confuse people using your code.

通过这样做,你或多或少会覆盖Python的实例创建并创建自己的实例。 Python已经很好地为您完成了这项工作,无需重新设计它,它会让使用您代码的人感到困惑。

Here's what to best do instead: Use a single __init__ method that is to be called for all possible instantiations of the class that initializes all instance variables properly. For different modes of initialization use either of the two approaches:

以下是最好的方法:使用单个__init__方法,该方法可以为类的所有可能实例化调用,以正确初始化所有实例变量。对于不同的初始化模式,请使用以下两种方法之一:

  1. Support different signatures for __init__ that handle your cases by using optional arguments.
  2. 支持__init__的不同签名,通过使用可选参数来处理您的案例。
  3. Create several class methods that serve as alternative constructors. Make sure they all create instances of the class in the normal way (i.e. calling __init__), as shown by Roman Bodnarchuk, while performing additional work or whatever. It's best if they pass all the data to the class (and __init__ handles it), but if that's impossible or inconvenient, you can set some instance variables after the instance was created and __init__ is done initializing.
  4. 创建几个用作替代构造函数的类方法。确保他们以正常方式创建类的实例(即调用__init__),如Roman Bodnarchuk所示,同时执行其他工作或其他任何工作。它最好是将所有数据传递给类(并且__init__处理它),但如果这是不可能或不方便的,您可以在创建实例并且__init__完成初始化后设置一些实例变量。

If __init__ has an optional step (e.g. like processing that data argument, although you'd have to be more specific), you can either make it an optional argument or make a normal method that does the processing... or both.

如果__init__有一个可选步骤(例如处理数据参数,尽管你必须更具体),你可以使它成为一个可选参数,或者做一个正常的方法来处理......或两者兼而有之。

#2


15  

Use classmethod decorator for your Load method:

为您的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)

So you can do:

所以你可以这样做:

loaded_obj = B.Load('filename.txt', 'foo')

Edit:

编辑:

Anyway, if you still want to omit __init__ method, try __new__:

无论如何,如果您仍想省略__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>

#3


9  

Taking your question literally I would use meta classes :

从字面上看你的问题我会使用元类:

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()

This can be useful e.g. for copying constructors without polluting the parameter list. But to do this properly would be more work and care than my proposed hack.

这可能是有用的,例如用于复制构造函数而不会污染参数列表。但要正确地做到这一点将比我提出的黑客更多的工作和关心。

#4


8  

Not really. The purpose of __init__ is to instantiate an object, and by default it really doesn't do anything. If the __init__ method is not doing what you want, and it's not your own code to change, you can choose to switch it out though. For example, taking your class A, we could do the following to avoid calling that __init__ method:

不是真的。 __init__的目的是实例化一个对象,默认情况下它实际上没有做任何事情。如果__init__方法没有按照您的要求进行操作,并且您不需要更改自己的代码,则可以选择将其切换出来。例如,考虑到A类,我们可以执行以下操作以避免调用__init__方法:

def emptyinit(self):
    pass
A.__init__ = emptyinit
a = A()
a.Print()

This will dynamically switch out which __init__ method from the class, replacing it with an empty call. Note that this is probably NOT a good thing to do, as it does not call the super class's __init__ method.

这将动态地从类中切换出__init__方法,将其替换为空调用。请注意,这可能不是一件好事,因为它不会调用超类的__init__方法。

You could also subclass it to create your own class that does everything the same, except overriding the __init__ method to do what you want it to (perhaps nothing).

您也可以创建自己的类来创建自己的类,除了重写__init__方法以执行您想要的操作(可能没有)。

Perhaps, however, you simply wish to call the method from the class without instantiating an object. If that is the case, you should look into the @classmethod and @staticmethod decorators. They allow for just that type of behavior.

但是,您可能希望在不实例化对象的情况下从类中调用该方法。如果是这种情况,您应该查看@classmethod和@staticmethod装饰器。他们只允许这种行为。

In your code you have put the @staticmethod decorator, which does not take a self argument. Perhaps what may be better for the purpose would a @classmethod, which might look more like this:

在您的代码中,您放置了@staticmethod装饰器,它不带自参数。对于这个目的而言可能更好的是@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)

UPDATE: Rosh's Excellent answer pointed out that you CAN avoid calling __init__ by implementing __new__, which I was actually unaware of (although it makes perfect sense). Thanks Rosh!

更新:Rosh的优秀答案指出你可以通过实现__new__来避免调用__in__,我实际上并不知道(尽管它很有意义)。谢谢Rosh!

#5


5  

I was reading the Python cookbook and there's a section talking about this: the example is given using __new__ to bypass __init__()

我正在阅读Python食谱,并且有一节讨论这个:使用__new__来绕过__init __()给出了一个例子

>>> class A:
    def __init__(self,a):
        self.a = a


>>> test = A('a')
>>> test.a
'a'
>>> test_noinit = A.__new__(A)
>>> test_noinit.a
Traceback (most recent call last):
  File "", line 1, in 
    test_noinit.a
AttributeError: 'A' object has no attribute 'a'
>>> 

However I think this only works in Python3. Below is running under 2.7

但是我认为这只适用于Python3。以下是在2.7下运行

>>> class A:
    def __init__(self,a):
        self.a = a


>>> test = A.__new__(A)

Traceback (most recent call last):
  File "", line 1, in 
    test = A.__new__(A)
AttributeError: class A has no attribute '__new__'
>>> 

#6


2  

As I said in my comment you could change your __init__ method so that it allows creation without giving any values to its parameters:

正如我在评论中所说,您可以更改__init__方法,以便允许创建而不为其参数赋予任何值:

def __init__(self, p0, p1, p2):
   # some logic

would become:

会成为:

def __init__(self, p0=None, p1=None, p2=None):
   if p0 and p1 and p2:
       # some logic

or:

要么:

def __init__(self, p0=None, p1=None, p2=None, init=True):
    if init:
        # some logic