我只是试图简化我的一个类,并引入了一些与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__
派上了用场。__new__是实例构造的第一步,被调用
__init__之前。
__new__方法是用类来调用的
第一个参数;它的职责是返回该函数的一个新实例
类。
将其与__init__进行比较:__init__是通过实例调用的
作为它的第一个参数,它不返回任何东西;它的
职责是初始化实例。
有一些情况
在没有调用__init__(例如
当实例从pickle加载时)。没有办法去创造
一个不调用__new__的新实例(尽管在某些情况下可以
不需要调用基类的__new__)。
关于你想要达到的目标,还有关于单例模式的相同文档信息
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:
...
现在我又遇到了同样的问题,出于某些原因,我决定避免使用装饰器、工厂和元类。我是这样做的:
主文件
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为祖父母的类似乎行为怪异。(未经证实的)
在网上试试!
当你需要控制时使用__new__
创建一个新实例。
使用
__init__当你需要控制一个新实例的初始化时。
__new__是创建实例的第一步。它叫做first,并且是
负责退货
类的实例。
相比之下,
__init__不返回任何东西;它只负责初始化
实例。
一般来说,你不需要这样做
重写__new__,除非你
继承一个不可变类型的子类
Str, int, unicode或tuple。
从2008年4月的帖子:什么时候使用__new__ vs. __init__?在mail.python.org上。
您应该考虑到您正在尝试做的事情通常是通过Factory完成的,这是最好的方法。使用__new__不是一个好的干净的解决方案,所以请考虑使用工厂。下面是一个很好的例子:ActiveState FᴀᴄᴛᴏʀʏᴘᴀᴛᴛᴇʀɴRecipe。