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

Wol*_*tan 57 python constructor class instantiation

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

例:

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

    def Print(self):
        print "YEHAA"
Run Code Online (Sandbox Code Playgroud)

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

a = A
a.Print()
Run Code Online (Sandbox Code Playgroud)

编辑:

一个更复杂的例子:

假设我有一个对象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
Run Code Online (Sandbox Code Playgroud)

现在我想转储并加载该对象的实例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
Run Code Online (Sandbox Code Playgroud)

换句话说,如果不传递参数文件,则无法创建实例.在我的"真实"的情况,但是,它不是一个参数文件,但一些大数据的垃圾我当然不希望在内存中随身携带,甚至将其存储到光盘上.

因为我想C从方法返回一个实例,Load我不得不调用构造函数.

旧编辑:

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

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
Run Code Online (Sandbox Code Playgroud)

如您所见,由于data未存储在类变量中,因此无法将其传递给__init__.当然,我可以简单地存储它,但如果该数据是一个巨大的物体,这是我不希望所有的时间进行在内存中,甚至将它保存到光盘?

Ros*_*ron 55

可以直接__init__打电话规避__new__.然后,您可以创建给定类型的对象并为其调用替代方法__init__.这是pickle可以做到的.

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

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

a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

你可以跳过第二步.

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

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

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

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

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

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

  • 这样做的有效用途是,如果您正在构建工厂模式并希望将构造委托给另一个类,但是在您不为该类使用工厂的情况下也希望具有默认构造.`class ThreadPoolFactory:def PersistentThreadPool():ThreadPool .__ new__ .... build and log it` (5认同)
  • 如果你不想要覆盖`A .__ new__`,你甚至可以使用`a = object .__ new __(A)`. (2认同)

Rom*_*huk 20

使用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)
Run Code Online (Sandbox Code Playgroud)

所以你可以这样做:

loaded_obj = B.Load('filename.txt', 'foo')
Run Code Online (Sandbox Code Playgroud)

编辑:

无论如何,如果您仍想省略__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>
Run Code Online (Sandbox Code Playgroud)

  • `return cls(newName, s)` 正在调用 `__init__()`。 (3认同)

小智 9

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

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()
Run Code Online (Sandbox Code Playgroud)

这对于复制构造函数而不污染参数列表非常有用.但要正确地做到这一点将比我提议的黑客更多的工作和关心.


Rya*_*ert 8

并不是的.目的__init__是实例化一个对象,默认情况下它实际上没有做任何事情.如果__init__方法没有按照你想要的方式进行,而且你自己的代码不是要改变的,你可以选择将其切换出去.例如,拿A类,我们可以执行以下操作以避免调用该__init__方法:

def emptyinit(self):
    pass
A.__init__ = emptyinit
a = A()
a.Print()
Run Code Online (Sandbox Code Playgroud)

这将动态地__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)
Run Code Online (Sandbox Code Playgroud)

更新:Rosh的优秀答案指出,你可以__init__通过实施避免调用__new__,我实际上并不知道(尽管它很有道理).谢谢Rosh!


xbb*_*xbb 6

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

  • 使用2.7,您需要使用"新样式类" - 即子类对象:`class A(object)`. (2认同)