我只是试图简化我的一个类,并引入了一些与flyweight设计模式相同风格的功能。
然而,我有点困惑,为什么__init__总是在__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
Why?
当__new__返回同一类的实例时,__init__随后在返回的对象上运行。也就是说,你不能使用__new__来阻止__init__被运行。即使你从__new__返回之前创建的对象,它也会被__init__一次又一次地初始化。
下面是单例模式的通用方法,它扩展了上面的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的任何真实实例。
当__new__返回同一类的实例时,__init__随后在返回的对象上运行。也就是说,你不能使用__new__来阻止__init__被运行。即使你从__new__返回之前创建的对象,它也会被__init__一次又一次地初始化。
下面是单例模式的通用方法,它扩展了上面的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的任何真实实例。
@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和元类,而不是作为属性)。
无论哪种方式,这里的重点是,如果找到键,您的类初始化器(__call__方法)将不会执行__new__或__init__。这比使用__new__干净得多,如果你想跳过默认的__init__步骤,则需要标记对象。
当你需要控制时使用__new__
创建一个新实例。
使用
__init__当你需要控制一个新实例的初始化时。
__new__是创建实例的第一步。它叫做first,并且是
负责退货
类的实例。
相比之下,
__init__不返回任何东西;它只负责初始化
实例。
一般来说,你不需要这样做
重写__new__,除非你
继承一个不可变类型的子类
Str, int, unicode或tuple。
从2008年4月的帖子:什么时候使用__new__ vs. __init__?在mail.python.org上。
您应该考虑到您正在尝试做的事情通常是通过Factory完成的,这是最好的方法。使用__new__不是一个好的干净的解决方案,所以请考虑使用工厂。下面是一个很好的例子:ActiveState FᴀᴄᴛᴏʀʏᴘᴀᴛᴛᴇʀɴRecipe。