有没有一种方法可以在不调用 __init__ 的情况下实例化一个类?

发布于 2024-11-16 04:21:25 字数 1838 浏览 5 评论 0原文

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

示例:

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

    def Print(self):
        print "YEHAA"

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

a = A
a.Print()

编辑:

一个更复杂的示例:

假设我有一个对象 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

现在我想转储并加载该对象 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

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

由于我想从方法 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 未存储在类变量中,我无法将其传递给__init__。当然,我可以简单地存储它,但是如果数据是一个巨大的对象,我不想一直在内存中携带它,甚至不想将其保存到磁盘上怎么办?

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

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.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:

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.

    @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.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

初见你 2024-11-23 04:21:25

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

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

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

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

您可以跳过第二步。

以下是您不应该这样做的原因:__init__的目的是初始化对象,填写所有字段并确保<还会调用父类的 code>__init__ 方法。对于pickle来说这是一个例外,因为它尝试存储与对象关联的所有数据(包括为对象设置的任何字段/实例变量),等等上次由 __init__ 设置的内容会被 pickle 恢复,无需再次调用。

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

另外,通过这样做,您或多或少会覆盖 Python 的实例创建并创建您自己的实例。 Python 已经很好地为你做到了这一点,无需重新发明它,它会让使用你的代码的人感到困惑。

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

  1. 支持 __init__ 的不同签名,通过使用可选参数来处理您的情况。
  2. 创建几个用作替代构造函数的类方法。确保它们都以正常方式创建类的实例(即调用 __init__),如 Roman Bodnarchuk 所示< /a>,同时执行额外的工作或其他操作。最好将所有数据传递给类(并且 __init__ 处理它),但如果这是不可能或不方便的,您可以在创建实例和 __init__ 之后设置一些实例变量code> 已完成初始化。

如果 __init__ 有一个可选步骤(例如,像处理 data 参数一样,尽管您必须更具体),您可以将其设为可选参数或将其设为进行处理的正常方法...或两者兼而有之。

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.

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__.

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.

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.

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.

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:

  1. Support different signatures for __init__ that handle your cases by using optional arguments.
  2. 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.

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.

晚风撩人 2024-11-23 04:21:25

使用 classmethod 装饰器为您的 Load 方法:

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>

Use classmethod decorator for your Load method:

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__:

>>> 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>
墨洒年华 2024-11-23 04:21:25

从字面上理解你的问题,我会使用元类:

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

这对于复制构造函数而不污染参数列表很有用。
但要正确地做到这一点,比我提议的黑客攻击需要更多的工作和关怀。

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.

薄荷港 2024-11-23 04:21:25

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

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

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

您还可以将其子类化以创建自己的类,该类执行所有相同的操作,除了重写 __init__ 方法来执行您想要的操作(可能什么也不做)。

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

在您的代码中,您放置了 @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__ ,我实际上没有意识到(尽管这是完全有道理的)。谢谢罗什!

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:

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.

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

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.

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:

@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!

何处潇湘 2024-11-23 04:21:25

我正在阅读 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'
>>> 

但是我认为这只适用于 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__'
>>> 

I was reading the Python cookbook and there's a section talking about this: the example is given using __new__ to bypass __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

>>> 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__'
>>> 
余生一个溪 2024-11-23 04:21:25

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

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

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文