python-是否有一种无需调用__init__即可实例化类的方法?

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

例:

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

    def Print(self):
        print "YEHAA"

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

a = A
a.Print()

编辑:

一个更复杂的示例:

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

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

现在,我想转储并加载该对象的实例data。但是,当加载该对象时,我只有单个变量__init__,并且我无法调用构造函数,因为它需要参数文件。

    @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

换句话说,不传递参数文件就不可能创建实例。 但是,在我的“真实”情况下,它不是参数文件,而是一些巨大的数据垃圾,我当然不希望在内存中随身携带,甚至不希望将其存储到磁盘上。

并且由于我想从方法__init__返回data的实例,因此我必须以某种方式调用构造函数。

旧编辑:

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

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__。当然我可以简单地存储它,但是如果数据是一个巨大的对象,而又不想在内存中随身携带,那该怎么办? 时间,甚至将其保存到光盘上?

Woltan asked 2019-11-08T13:17:43Z
6个解决方案
55 votes

您可以通过直接致电__new__来规避__init__。 然后,您可以创建给定类型的对象,并为__init__调用替代方法。这就是pickle会执行的操作。

但是,首先,我非常想强调一点,这是您不应该做的事情,无论您要达到什么目的,都有更好的方法可以做到,其中一些已在其他答案中提到。 特别是,跳过调用__init__是一个坏主意。

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

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

您可以跳过第二步。

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

如果您跳过__init__并使用替代的初始化程序,则会有某种代码重复-实例变量将在两个位置填充,很容易在其中一个初始化程序中遗漏其中一个,或者不小心使 这两个填充字段的行为不同。 这样就可以发现难以跟踪的细微错误(您必须知道调用了哪个初始化程序),并且代码将更难以维护。 更不用说如果您使用继承会陷入更大的混乱-问题将在继承链中蔓延,因为您必须在链的各处使用此替代初始化器。

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

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

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

如果__init__具有可选步骤(例如,像处理data这样的参数,尽管您必须更具体一些),则可以将其设为可选参数,也可以将其设为执行该处理的常规方法...或同时执行两者。

Rosh Oxymoron answered 2019-11-08T13:19:19Z
20 votes

__new__装饰器用于__new__方法:

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>
Roman Bodnarchuk answered 2019-11-08T13:19:58Z
9 votes

从字面上考虑您的问题,我将使用meta类:

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

这可能是有用的,例如 用于复制构造函数而不会污染参数列表。但是,要正确地执行此操作,比我建议的修改要花更多的精力和精力。

user8335 answered 2019-11-08T13:20:37Z
8 votes

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

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

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

您也可以将其子类化以创建自己的类,该类执行所有相同的操作,除了覆盖__init__方法以执行所需的操作(也许什么也不做)。

但是,也许您只是希望从类中调用方法而无需实例化对象。 如果是这样,您应该查看@classmethod和@staticmethod装饰器。 它们只允许这种类型的行为。

在代码中,您放置了@staticmethod装饰器,该装饰器不使用__init__参数。 也许最好使用@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__来避免调用__new__,尽管我确实没有意识到(尽管这样做很合理)。 谢谢罗什!

Ryan Hiebert answered 2019-11-08T13:21:54Z
6 votes

我正在阅读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__'
>>> 
xbb answered 2019-11-08T13:22:38Z
2 votes

就像我在评论中说的,您可以更改__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
pajton answered 2019-11-08T13:23:18Z
translate from https://stackoverflow.com:/questions/6383914/is-there-a-way-to-instantiate-a-class-without-calling-init