我只是试图简化我的一个类,并引入了一些与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__可以初始化它。注意,__init__以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:
...
当你需要控制时使用__new__
创建一个新实例。
使用
__init__当你需要控制一个新实例的初始化时。
__new__是创建实例的第一步。它叫做first,并且是
负责退货
类的实例。
相比之下,
__init__不返回任何东西;它只负责初始化
实例。
一般来说,你不需要这样做
重写__new__,除非你
继承一个不可变类型的子类
Str, int, unicode或tuple。
从2008年4月的帖子:什么时候使用__new__ vs. __init__?在mail.python.org上。
您应该考虑到您正在尝试做的事情通常是通过Factory完成的,这是最好的方法。使用__new__不是一个好的干净的解决方案,所以请考虑使用工厂。下面是一个很好的例子:ActiveState FᴀᴄᴛᴏʀʏᴘᴀᴛᴛᴇʀɴRecipe。
现在我又遇到了同样的问题,出于某些原因,我决定避免使用装饰器、工厂和元类。我是这样做的:
主文件
def _alt(func):
import functools
@functools.wraps(func)
def init(self, *p, **k):
if hasattr(self, "parent_initialized"):
return
else:
self.parent_initialized = True
func(self, *p, **k)
return init
class Parent:
# Empty dictionary, shouldn't ever be filled with anything else
parent_cache = {}
def __new__(cls, n, *args, **kwargs):
# Checks if object with this ID (n) has been created
if n in cls.parent_cache:
# It was, return it
return cls.parent_cache[n]
else:
# Check if it was modified by this function
if not hasattr(cls, "parent_modified"):
# Add the attribute
cls.parent_modified = True
cls.parent_cache = {}
# Apply it
cls.__init__ = _alt(cls.__init__)
# Get the instance
obj = super().__new__(cls)
# Push it to cache
cls.parent_cache[n] = obj
# Return it
return obj
示例类
class A(Parent):
def __init__(self, n):
print("A.__init__", n)
class B(Parent):
def __init__(self, n):
print("B.__init__", n)
在使用
>>> A(1)
A.__init__ 1 # First A(1) initialized
<__main__.A object at 0x000001A73A4A2E48>
>>> A(1) # Returned previous A(1)
<__main__.A object at 0x000001A73A4A2E48>
>>> A(2)
A.__init__ 2 # First A(2) initialized
<__main__.A object at 0x000001A7395D9C88>
>>> B(2)
B.__init__ 2 # B class doesn't collide with A, thanks to separate cache
<__main__.B object at 0x000001A73951B080>
警告:你不应该初始化父类,它会与其他类冲突——除非你在每个子类中定义了单独的缓存,这不是我们想要的。
警告:以Parent为祖父母的类似乎行为怪异。(未经证实的)
在网上试试!
One should look at __init__ as a simple constructor in traditional OO languages. For example, if you are familiar with Java or C++, the constructor is passed a pointer to its own instance implicitly. In the case of Java, it is the this variable. If one were to inspect the byte code generated for Java, one would notice two calls. The first call is to an "new" method, and then next call is to the init method (which is the actual call to the user defined constructor). This two step process enables creation of the actual instance before calling the constructor method of the class which is just another method of that instance.
现在,在Python中,__new__是一个用户可以访问的附加功能。由于Java的类型化特性,它没有提供这种灵活性。如果一种语言提供了这种功能,那么__new__的实现者可以在返回实例之前在该方法中做很多事情,包括在某些情况下为不相关的对象创建一个全新的实例。而且,这种方法也适用于Python中的不可变类型。