有没有一种方法可以在不调用 __init__ 的情况下实例化一个类?
有没有办法绕过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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您可以通过直接调用
__new__
来规避__init__
。然后,您可以创建给定类型的对象并调用 __init__ 的替代方法。这是pickle
会做的事情。然而,首先我想强调的是,这是你不应该做的事情,无论你想实现什么目标,都有更好的方法可以做到,其中一些在其他答案中已经提到过。特别是,跳过调用 __init__ 是一个坏主意。
创建对象时,或多或少会发生这种情况:
您可以跳过第二步。
以下是您不应该这样做的原因:
__init__
的目的是初始化对象,填写所有字段并确保<还会调用父类的 code>__init__ 方法。对于pickle来说这是一个例外,因为它尝试存储与对象关联的所有数据(包括为对象设置的任何字段/实例变量),等等上次由__init__
设置的内容会被 pickle 恢复,无需再次调用。如果您跳过 __init__ 并使用替代的初始化程序,则会出现某种代码重复 - 将会有两个地方填充实例变量,并且很容易错过其中一个初始化程序之一或意外地使两个填充字段的行为不同。这可能会出现一些微妙的错误,这些错误的追踪并不是那么简单(您必须知道调用了哪个初始化程序),并且代码将更难以维护。更不用说,如果您使用继承,您会陷入更大的混乱 - 问题将沿着继承链上升,因为您必须在链上的任何地方使用这个替代初始化程序。
另外,通过这样做,您或多或少会覆盖 Python 的实例创建并创建您自己的实例。 Python 已经很好地为你做到了这一点,无需重新发明它,它会让使用你的代码的人感到困惑。
以下是最好的做法: 使用单个
__init__
方法,该方法将被调用以正确初始化所有实例变量的类的所有可能实例化。对于不同的初始化模式,请使用以下两种方法之一:__init__
的不同签名,通过使用可选参数来处理您的情况。__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 thatpickle
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:
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. Withpickle
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:__init__
that handle your cases by using optional arguments.__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 thatdata
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.使用
classmethod
装饰器为您的Load
方法:所以您可以这样做:
编辑:
无论如何,如果您仍然想省略
__init__
方法,请尝试__new__
:Use
classmethod
decorator for yourLoad
method:So you can do:
Edit:
Anyway, if you still want to omit
__init__
method, try__new__
:从字面上理解你的问题,我会使用元类:
这对于复制构造函数而不污染参数列表很有用。
但要正确地做到这一点,比我提议的黑客攻击需要更多的工作和关怀。
Taking your question literally I would use meta classes :
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.
并不真地。
__init__
的目的是实例化一个对象,默认情况下它实际上不执行任何操作。如果__init__
方法没有执行您想要的操作,并且不需要更改您自己的代码,那么您可以选择将其切换出来。例如,以类 A 为例,我们可以执行以下操作以避免调用__init__
方法:这将从类中动态切换出哪个
__init__
方法,并将其替换为空调用。请注意,这可能不是一件好事,因为它不会调用超类的__init__
方法。您还可以将其子类化以创建自己的类,该类执行所有相同的操作,除了重写 __init__ 方法来执行您想要的操作(可能什么也不做)。
然而,也许您只是希望从类中调用方法而不实例化对象。如果是这种情况,您应该查看 @classmethod 和 @staticmethod 装饰器。他们只允许这种行为。
在您的代码中,您放置了 @staticmethod 装饰器,它不接受
self
参数。也许对于这个目的来说,@classmethod可能更好,它可能看起来更像这样:更新: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: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: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!我正在阅读 Python 食谱,其中有一节讨论了这一点:该示例是使用 __new__ 绕过 __init__()
但是我认为这只适用于 Python3。下面是2.7下运行的
I was reading the Python cookbook and there's a section talking about this: the example is given using
__new__
to bypass__init__()
However I think this only works in Python3. Below is running under 2.7
正如我在评论中所说,您可以更改
__init__
方法,以便它允许创建而无需为其参数提供任何值:将变为:
或:
As I said in my comment you could change your
__init__
method so that it allows creation without giving any values to its parameters:would become:
or: