有没有办法在不调用 __init__ 的情况下实例化一个类? [英] Is there a way to instantiate a class without calling __init__?

查看:26
本文介绍了有没有办法在不调用 __init__ 的情况下实例化一个类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

示例:

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

    def Print(self):
        print "YEHAA"

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

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

一个更复杂的例子:

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

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

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

而且由于我想从方法 Load 返回一个 C 的实例,所以我必须以某种方式调用构造函数.

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

一个更复杂的例子,它解释了为什么我问这个问题:

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

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

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?

推荐答案

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

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.

但是,首先我要非常强调,这是您不应该做的事情,无论您想实现什么,都有更好的方法,其中一些已在其他答案中提到.特别是,跳过调用 __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__.

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

When objects are created, more or less this happens:

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

您可以跳过第二步.

这就是为什么你不应该这样做: __init__ 的目的是初始化对象,填写所有字段并确保父类的 __init__ 方法也被调用.使用 pickle 这是一个例外,因为它试图存储与对象关联的所有数据(包括为对象设置的任何字段/实例变量),等等__init__ 上次设置的会被pickle恢复,不需要再次调用.

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__ 并使用替代初始化程序,您将有一种代码重复 - 将有两个地方填充实例变量,很容易错过一个它们在其中一个初始化程序中或不小心使两者填充字段的行为不同.这可能会出现一些不太容易追踪的细微错误(您必须知道调用了哪个初始化程序),并且代码将更难以维护.更不用说如果您使用继承,您会陷入更大的混乱 - 问题会在继承链上上升,因为您必须在链上的任何地方使用这个替代初始化器.

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.

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

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.

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

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

如果 __init__ 有一个可选步骤(例如处理那个 data 参数,但你必须更具体),你可以将它设为可选参数或者制作一个正常的方法来进行处理......或两者兼而有之.

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__ 的情况下实例化一个类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆