我只是想简化我的一个类,并引入了一些与flyweight设计模式相同的功能.
但是,我有点困惑,为什么__init__
总是被称为__new__
.我没想到这个.任何人都可以告诉我为什么会这样,以及我如何能够实现这个功能呢?(除了将实施放入__new__
感觉相当hacky之外.)
这是一个例子:
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
为什么?
需要控制新实例的创建时,请使用__new__.需要控制新实例的初始化时,请使用 __init__.
__new__是实例创建的第一步.它首先被调用,并负责返回您的类的新实例.相比之下, __ init__不会返回任何内容; 它只负责在创建实例后初始化它.
通常,除非您继承了str,int,unicode或tuple之类的不可变类型,否则不应该覆盖__new__.
来自:http://mail.python.org/pipermail/tutor/2008-April/061426.html
您应该考虑到您尝试做的事情通常是通过工厂完成的,这是最好的方法.使用__new__不是一个很好的清洁解决方案,所以请考虑工厂的使用.在这里你有一个很好的工厂例子.
__new__
是静态类方法,而__init__
实例方法.
__new__
必须先创建实例,__init__
才能初始化它.请注意,__init__
需要self
作为参数.在你创建实例之前没有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)
将分配一个新实例,初始化实例的属性,然后返回它.
在大多数众所周知的OO语言中,可以通过定义构造函数为每个类定制"初始化实例的属性"部分,构造函数基本上只是在新实例上运行的代码块(使用提供给构造函数表达式的参数) )设置所需的任何初始条件.在Python中,这对应于类的__init__
方法.
Python __new__
不仅仅是"分配新实例"部分的类似定制.这当然允许您执行不寻常的事情,例如返回现有实例而不是分配新实例.所以在Python中,我们不应该认为这部分必然涉及分配; 我们需要的就是__new__
从某个地方提供合适的实例.
但它仍然只是工作的一半,并且Python系统无法知道有时候你想要运行另一半的工作(__init__
),有时你不会.如果您想要这种行为,您必须明确说出来.
通常,您可以重构,因此您只需要__new__
,或者您不需要__new__
,或者__init__
对已经初始化的对象执行不同的操作.但是,如果你真的想,Python不竟让你重新定义"工作",所以SomeClass(arg1, arg2)
不一定需要__new__
后面__init__
.为此,您需要创建元类,并定义其__call__
方法.
元类只是类的类.并且类' __call__
方法控制调用类的实例时发生的事情.因此,元类 ' __call__
方法控制调用类时会发生什么; 即它允许您从头到尾重新定义实例创建机制.这是您可以最优雅地实现完全非标准实例创建过程(如单例模式)的级别.事实上,用了不到10行代码就可以实现一个Singleton
元类是那么甚至不要求你到futz __new__
可言,并且可以将任何通过简单地增加,否则正常的,定义为单__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 __()方法然后根据需要修改新创建的实例来创建类的新实例在回来之前.
...
如果__new __()没有返回cls的实例,则不会调用新实例的__init __()方法.
__new __()主要用于允许不可变类型的子类(如int,str或tuple)自定义实例创建.
我意识到这个问题已经很老了,但我遇到了类似的问题.以下做了我想要的:
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__
返回的值与类的类型相同,则__init__
函数执行,否则不会.在这种情况下,您的代码将返回A._dict('key')
与其相同的类cls
,因此__init__
将执行.
当__new__
返回同一类的实例时,__init__
在返回的对象之后运行.即你不能__new__
用来防止__init__
被运行.即使你从之前返回创建的对象__new__
,它也会被__init__
一次又一次地初始化为double(triple等).
以下是Singleton模式的通用方法,它扩展了上面的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__
是使用classmethods:
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__函数中向该对象添加一个属性,以指示它已被初始化。首先检查该属性是否存在,如果已存在,请不要继续进行。
对@AntonyHatchkins答案的更新,您可能想要为元类型的每个类提供单独的实例字典,这意味着您应__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表示法(super
类参数中的no-arg调用和metaclass而不是作为属性)。
无论哪种方式,最重要的一点是,你的类初始化函数(__call__
方法)将不会执行任何__new__
或者__init__
如果键被找到。这比使用干净得多__new__
,如果要跳过默认__init__
步骤,使用标记您需要标记该对象。