为什么__init __()总是在__new __()之后调用?
我只是想简化我的一个类,并已经引入了与flyweightdevise模式相同风格的一些function。
不过,我对__init__
之后总是调用__init__
有点困惑。 我并不期待这一点。 任何人都可以告诉我为什么发生这种情况,如何才能实现这个function呢? (除了把这个实现放到__new__
里面,觉得挺__new__
)。
这是一个例子:
class A(object): _dict = dict() def __new__(cls): if 'key' in A._dict: print "EXISTS" return A._dict['key'] else: print "NEW" return super(A, cls).__new__(cls) def __init__(self): print "INIT" A._dict['key'] = self print "" a1 = A() a2 = A() a3 = A()
输出:
NEW INIT EXISTS INIT EXISTS INIT
为什么?
当你需要控制新实例的创build时使用__new__ 。 当需要控制新实例的初始化时,使用__init__ 。
__new__是实例创build的第一步。 它被称为第一个,并负责返回您的类的新实例。 相反, __init__不返回任何东西; 它只是在创build实例后才负责初始化。
一般来说,除非你inheritance了像str,int,unicode或tuple这样的不可变types,否则你不需要重载__new__ 。
来自: http : //mail.python.org/pipermail/tutor/2008-April/061426.html
你应该考虑你所要做的通常是用一个工厂来完成,这是最好的方法。 使用__new__不是一个很好的清洁解决scheme,所以请考虑工厂的使用情况。 在这里你有一个很好的工厂的例子 。
__new__
是静态类方法,而__init__
是实例方法。 __new__
必须首先创build实例,所以__init__
可以初始化它。 请注意, __init__
将self
作为参数。 直到你创build实例,没有self
。
现在,我收集,你正在尝试在Python中实现单例模式 。 有几种方法可以做到这一点。
另外,从Python 2.6开始,您可以使用类装饰器 。
def singleton(cls): instances = {} def getinstance(): if cls not in instances: instances[cls] = cls() return instances[cls] return getinstance @singleton class MyClass: ...
在大多数着名的OO语言中,像SomeClass(arg1, arg2)
这样的expression式将分配一个新的实例,初始化实例的属性,然后返回它。
在大多数着名的OO语言中,可以通过定义一个构造函数为每个类定制“初始化实例的属性”部分,这个构造函数基本上只是一个在新实例上运行的代码块(使用提供给构造函数expression式的参数)设置任何最初的条件是需要的。 在Python中,这对应于类__init__
方法。
Python的__new__
是“分配新实例”部分的类定制而已。 这当然可以让你做不寻常的事情,例如返回一个现有的实例,而不是分配一个新的实例。 所以在Python中,我们不应该认为这部分是必然涉及分配的; 所有我们需要的是__new__
从某处出现一个合适的实例。
但是它仍然只有一半的工作,Python系统无法知道有时候你想要运行另一半的工作( __init__
),有时候你不需要。 如果你想要这样的行为,你必须明确地说出来。
通常情况下,你可以重构,所以你只需要__new__
,所以你不需要__new__
,或者__init__
在已经初始化的对象上performance不同。 但是,如果你真的想,Python实际上允许你重新定义“工作”,所以SomeClass(arg1, arg2)
不一定会调用__new__
后面跟__init__
。 为此,您需要创build一个元类,并定义它的__call__
方法。
元类只是一个类的类。 而一个类的__call__
方法控制了当你调用类的实例时会发生什么。 所以一个元类的__call__
方法控制了当你调用一个类时会发生什么。 即它允许您从头到尾重新定义实例创build机制 。 这是您可以最优雅地实现完全非标准的实例创build过程(如单例模式)的级别。 事实上,只需less于10行的代码,你就可以实现一个Singleton
元类,它甚至不需要你用__new__
来完成任何 __metaclass__ = Singleton
,只需要添加__metaclass__ = Singleton
,就可以把任何其他正常的类变成单__metaclass__ = Singleton
。
class Singleton(type): def __init__(self, *args, **kwargs): super(Singleton, self).__init__(*args, **kwargs) self.__instance = None def __call__(self, *args, **kwargs): if self.__instance is None: self.__instance = super(Singleton, self).__call__(*args, **kwargs) return self.__instance
然而,这可能是更深的魔力比真正保证这种情况!
引用文档 :
典型实现通过使用具有适当参数的“super(currentclass,cls).__new__(cls [,…])”调用超类的__new__()方法,然后根据需要修改新创build的实例来创build类的新实例在返回之前。
…
如果__new __()没有返回一个cls实例,那么新实例的__init __()方法将不会被调用。
__new __()主要用于允许不可变types的子类(如int,str或tuple)自定义实例创build。
我意识到这个问题是相当古老的,但我也有类似的问题。 以下做了我想要的:
class Agent(object): _agents = dict() def __new__(cls, *p): number = p[0] if not number in cls._agents: cls._agents[number] = object.__new__(cls) return cls._agents[number] def __init__(self, number): self.number = number def __eq__(self, rhs): return self.number == rhs.number Agent("a") is Agent("a") == True
我用这个页面作为资源http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html
我认为这个问题的简单答案是,如果__new__
返回一个与类相同types的值, __init__
函数将会执行,否则不会。 在这种情况下,您的代码会返回与cls
相同的类A._dict('key')
,所以__init__
将被执行。
当__new__
返回同一个类的实例时, __init__
会在返回的对象上运行。 即你不能使用__new__
来防止__init__
被运行。 即使你从__new__
返回以前创build的对象,它将会是__init__
被一次又一次初始化的double(triple等)。
这里是对单例模式的通用方法,它扩展了上面的vartec答案并修复了它:
def SingletonClass(cls): class Single(cls): __doc__ = cls.__doc__ _initialized = False _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Single, cls).__new__(cls, *args, **kwargs) return cls._instance def __init__(self, *args, **kwargs): if self._initialized: return super(Single, self).__init__(*args, **kwargs) self.__class__._initialized = True # Its crucial to set this variable on the class! return Single
完整的故事在这里 。
另一种实际上涉及__new__
是使用类方法:
class Singleton(object): __initialized = False def __new__(cls, *args, **kwargs): if not cls.__initialized: cls.__init__(*args, **kwargs) cls.__initialized = True return cls class MyClass(Singleton): @classmethod def __init__(cls, x, y): print "init is here" @classmethod def do(cls): print "doing stuff"
请注意,使用这种方法,您需要使用@classmethod
修饰所有方法,因为您永远不会使用任何MyClass
实例。
class M(type): _dict = {} def __call__(cls, key): if key in cls._dict: print 'EXISTS' return cls._dict[key] else: print 'NEW' instance = super(M, cls).__call__(key) cls._dict[key] = instance return instance class A(object): __metaclass__ = M def __init__(self, key): print 'INIT' self.key = key print a1 = A('aaa') a2 = A('bbb') a3 = A('aaa')
输出:
NEW INIT NEW INIT EXISTS
NB作为一个副作用, M._dict
属性可以自动地从A
作为A._dict
访问,所以注意不要随意覆盖它。
__new__应该返回一个新的空白的类实例。 然后调用__init__来初始化那个实例。 在__new__的“NEW”情况下,你不会调用__init__,所以它正在被调用。 调用__new__
的代码不会跟踪__init__是否在特定的实例上被调用,也不应该这样做,因为您在这里做的事情非常不寻常。
你可以在__init__函数中添加一个属性来指示它已经被初始化了。 检查__init__中是否存在该属性,如果已经存在,则不要继续。
深入挖掘!
CPython中generics类的types是type
,它的基类是Object
(除非你显式定义了另一个基类,如元类)。 低级调用的顺序可以在这里find。 调用的第一个方法是type_call
,然后调用tp_new
然后调用tp_init
。
这里有趣的部分是, tp_new
将调用Object
的(基类)新方法object_new
,它执行一个tp_alloc
( PyType_GenericAlloc
)为对象分配内存:)
此时,对象在内存中创build,然后调用__init__
方法。 如果__init__
没有在你的类中实现,那么object_init
被调用,它什么都不做:)
然后type_call
只是返回绑定到你的variables的对象。
参考这个文档 :
在inheritance不可变的内置types(如数字和string)时,偶尔会出现其他情况,静态方法new派上用场。 new是实例构造的第一步,在init之前调用。
新的方法是以类作为第一个参数来调用的; 其职责是返回该类的新实例。
将它与init进行比较:使用实例作为第一个参数调用init ,并且不返回任何内容; 它的职责是初始化实例。
有些情况下,不用调用init就可以创build一个新的实例(例如,当实例从一个pickle中加载时)。 如果不调用new,就没有办法创build一个新的实例(尽pipe在某些情况下,你可以通过调用一个新的基类来实现)。
关于你想达到什么目的,还有关于Singleton模式的相同的文档信息
class Singleton(object): def __new__(cls, *args, **kwds): it = cls.__dict__.get("__it__") if it is not None: return it cls.__it__ = it = object.__new__(cls) it.init(*args, **kwds) return it def init(self, *args, **kwds): pass
您也可以使用装饰器从PEP 318使用此实现
def singleton(cls): instances = {} def getinstance(): if cls not in instances: instances[cls] = cls() return instances[cls] return getinstance @singleton class MyClass: ...
我们应该将init看作传统OO语言中的简单构造函数。 例如,如果您熟悉Java或C ++,则构造函数将隐式地传递一个指向其自己的实例的指针。 在Java的情况下,它是“这个”variables。 如果要检查为Java生成的字节代码,则会注意到两个调用。 第一个调用是“新”方法,然后下一个调用是init方法(这是对用户定义的构造函数的实际调用)。 这两个步骤过程可以在调用该类的构造方法之前创build实际的实例,这只是该实例的另一种方法。
现在,在Python的情况下, 新增function是用户可以访问的。 由于Java的types性质,Java不提供这种灵活性。 如果一个语言提供了这个工具,那么new的实现者可以在返回实例之前用这个方法做很多事情,包括在某些情况下创build一个不相关对象的全新实例。 而且,对于Python中的不可变types,这种方法也适用。
__init__
在__new__
之后被调用,所以当你在一个子类中覆盖它时,你添加的代码仍然会被调用。
如果你试图给一个已经拥有__new__
的类子类, __new__
不知道这个的类可能会先调用__init__
并将调用转发给子类__init__
。 这个在__new__
之后调用__init__
__new__
有助于按预期工作。
__init__
仍然需要允许超类__new__
所需的任何参数,但是如果不这样做,通常会产生一个明确的运行时错误。 而__new__
应该明确地允许*args
和'** kw',以表明扩展是可以的。
由于原始海报描述的行为,在相同级别的inheritance中同时拥有__new__
和__init__
通常是不好的forms。
对@AntonyHatchkins的更新回答,你可能需要为每个元types的一个单独的实例字典,这意味着你应该在元类中有一个__init__
方法用这个字典初始化你的类对象,而不是在所有的类中使它成为全局的。
class MetaQuasiSingleton(type): def __init__(cls, name, bases, attibutes): cls._dict = {} def __call__(cls, key): if key in cls._dict: print('EXISTS') instance = cls._dict[key] else: print('NEW') instance = super().__call__(key) cls._dict[key] = instance return instance class A(metaclass=MetaQuasiSingleton): def __init__(self, key): print 'INIT' self.key = key print()
我已经提前使用__init__
方法更新了原始代码,并将语法更改为Python 3符号(no-arg在类参数中调用super
和metaclass,而不是作为属性)。
无论哪种方式,这里的重要一点是,如果find了密钥,则类初始化程序( __call__
方法)将不会执行__new__
或__init__
。 这比使用__new__
要干净得多,如果你想跳过缺省的__init__
步骤,需要标记对象。
不过,我对
__init__
之后总是调用__init__
有点困惑。
除了这样做以外,没有什么其他原因。 __new__
没有初始化类的责任,还有一些方法( __call__
,可能 – 我不知道)。
我并不期待这一点。 任何人都可以告诉我为什么发生这种情况,如何实现这个function呢? (除了把这个实现放到
__new__
里面感觉很__new__
)。
如果__init__
已经被初始化了,你可以不做任何事情,或者你可以用一个新的__call__
编写一个新的元类,它只在新实例上调用__init__
,否则返回__new__(...)
。