我只是想简化我的一个类,并以与flyweight设计模式相同的样式介绍了一些功能。
但是,对于为什么总是在__new__
之后调用__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
为什么?
#1楼
在__new__
之后__new__
__init__
,以便在子类中重写它时,仍将调用您添加的代码。
如果您试图对已经具有__new__
的类进行子类化,则可能对此__new__
人可能会先改编__init__
并将调用转发给子类__init__
。 在__new__
之后调用__init__
__new__
有助于按预期工作。
__init__
仍需要允许超类__new__
所需的任何参数,但是如果不这样做,通常会产生明显的运行时错误。 __new__
应该可能明确允许*args
和'** kw',以明确表示扩展名是可以的。
由于原始发布者描述的行为,将__new__
和__init__
放在同一个类中且具有相同的继承级别通常是不好的形式。
#2楼
我意识到这个问题已经很久了,但是我也遇到了类似的问题。 以下是我想要的:
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
#3楼
我认为此问题的简单答案是,如果__new__
返回的值与该类的类型相同,则__init__
函数将执行,否则不会执行。 在这种情况下,您的代码返回与cls
相同的类A._dict('key')
,因此将执行__init__
。
#4楼
参考此文档 :
当子类化不可变的内置类型(例如数字和字符串)时,有时在其他情况下,使用new静态方法非常方便。 new是实例构造的第一步,在init之前调用。
以类为第一个参数调用新方法。 它的责任是返回该类的新实例。
将此与init进行比较: init是使用实例作为其第一个参数调用的,它不返回任何内容; 它的责任是初始化实例。
在某些情况下,无需调用init即可创建新实例(例如,从泡菜中加载实例时)。 如果不调用new,就无法创建新实例(尽管在某些情况下,您可以通过调用基类的new来摆脱困境)。
关于您希望实现的目标,在有关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:
...
#5楼
人们应该将__init__
视为传统OO语言中的一种简单构造函数。 例如,如果您熟悉Java或C ++,则向构造函数隐式传递一个指向其自身实例的指针。 对于Java,它是this
变量。 如果要检查为Java生成的字节码,则将注意到两个调用。 第一个调用是对“ new”方法的调用,然后下一个调用是对init方法的调用(这是对用户定义的构造函数的实际调用)。 通过两步过程,可以在调用类的构造方法(该实例的另一个方法)之前创建实际实例。
现在,对于Python, __new__
是用户可以访问的附加功能。 Java由于其类型性质而没有提供这种灵活性。 如果一种语言提供了该功能,则__new__
的实现者可以在返回实例之前使用该方法做很多事情,包括在某些情况下创建不相关对象的全新实例。 而且,对于Python而言,这种方法也非常适用于不可变类型。
#6楼
当__new__
返回相同类的实例时, __init__
随后在返回的对象上运行。 也就是说,您不能使用__new__
来阻止__init__
运行。 即使您从__new__
返回先前创建的对象,它也会被__init__
一次又一次地初始化为double(三倍,等等)。
这是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__
另一种方法是使用类方法:
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
任何实际实例。
#7楼
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
注意:作为副作用, M._dict
属性会自动以A._dict
从A
访问,因此请注意不要意外覆盖它。
#8楼
深入了解这一点!
CPython中泛型类的类型为type
,其基类为Object
(除非您明确定义了另一个基类(如元类))。 低级呼叫的顺序可以在这里找到。 调用的第一个方法是type_call
,然后调用tp_new
,然后调用tp_init
。
这里有趣的部分是tp_new
将调用Object
的(基类)新方法object_new
,该方法执行tp_alloc
( PyType_GenericAlloc
),该方法为对象分配内存:)
那时,将在内存中创建对象,然后调用__init__
方法。 如果在您的类中未实现__init__
则调用object_init
,并且它什么也不做:)
然后, type_call
仅返回绑定到您的变量的对象。
#9楼
对@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
和metaclass的no-arg调用)。
无论哪种方式,这里的重点是,如果找到了键,您的类初始化器( __call__
方法)将不会执行__new__
或__init__
。 这比使用__new__
干净得多,后者需要跳过默认的__init__
步骤来标记对象。
#10楼
简单的原因是, 新的用于创建实例,而init用于初始化实例。 在初始化之前,应先创建实例。 这就是为什么应该在init之前调用new的原因。
#11楼
现在,我遇到了同样的问题,由于某些原因,我决定避免使用装饰器,工厂和元类。 我这样做是这样的:
主文件
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,它会与其他类冲突-除非您在每个子代中都定义了单独的缓存,否则我们就不需要这样做。
- 警告:祖父母的祖父母类似乎表现怪异。 [未验证]
#12楼
但是,对于为什么总是在
__new__
之后调用__init__
感到有些困惑。
我认为C ++类比在这里会很有用:
__new__
只是为对象分配内存。 一个对象的实例变量需要内存来保存它,这就是__new__
步骤所要做的。__init__
将对象的内部变量初始化为特定值(可以是默认值)。
#13楼
__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:
...
#14楼
当您需要控制新实例的创建时,请使用
__new__
。当您需要控制新实例的初始化时,请使用
__init__
。
__new__
是实例创建的第一步。 首先调用它,它负责返回类的新实例。相反,
__init__
不返回任何内容。 创建实例后,它仅负责初始化实例。通常,除非您要继承诸如str,int,unicode或tuple之类的不可变类型,否则无需覆盖
__new
__` 。
从2008年4月开始: 何时使用__new__
和__init__
? 在mail.python.org上。
您应该考虑要尝试做的事通常是通过Factory完成的,这是最好的方法。 使用__new__
并不是一个好的解决方案,因此请考虑使用工厂。 在这里,您有一个很好的工厂示例 。
#15楼
但是,对于为什么总是在
__new__
之后调用__init__
感到有些困惑。
除了这样做是没有其他原因的。 __new__
不负责初始化类,而其他方法则负责( __call__
,可能-我不确定)。
我没想到这一点。 谁能告诉我为什么会这样,否则我如何实现此功能? (除了将实现放入
__new__
,这感觉很__new__
)。
如果__init__
已经被初始化,则可以不执行任何操作,也可以使用新的__call__
编写新的元类,该新的__call__
仅在新实例上调用__init__
,否则仅返回__new__(...)
。
#16楼
__new__应该返回一个类的新的空白实例。 然后调用__init__初始化该实例。 您不是在__new__的“ NEW”情况下调用__init__,因此正在为您调用它。 调用__new__
的代码无法跟踪是否已在特定实例上调用__init__,因为您在这里所做的事情很不寻常。
您可以在__init__函数中向该对象添加一个属性,以指示它已被初始化。 首先检查该属性是否存在,如果已存在,请不要继续进行。
#17楼
引用文档 :
典型的实现通过使用带有适当参数的“ super(currentclass,cls).__ new __(cls [,...])”调用超类的__new __()方法,然后根据需要修改新创建的实例来创建该类的新实例。在返回之前。
...
如果__new __()不返回cls的实例,则将不会调用新实例的__init __()方法。
__new __()主要用于允许不可变类型的子类(例如int,str或tuple)自定义实例创建。
#18楼
在大多数众所周知的OO语言中,诸如SomeClass(arg1, arg2)
类的表达式将分配一个新实例,初始化该实例的属性,然后返回它。
在大多数著名的OO语言中,可以通过定义构造函数为每个类自定义“初始化实例的属性”部分,该构造函数基本上只是在新实例上运行的代码块(使用提供给构造函数表达式的参数) )来设置所需的任何初始条件。 在Python中,这对应于类的__init__
方法。
Python的__new__
就是“分配新实例”部分的类似的按类自定义。 当然,这可以让您做一些不寻常的事情,例如返回一个现有实例而不是分配一个新实例。 因此,在Python中,我们不应该真的认为这部分必然涉及分配。 我们所需要的就是__new__
从某个地方想出一个合适的实例。
但这仍然只是工作的一半,Python系统无法知道有时您希望稍后再运行另一部分工作( __init__
),而有时又不想。 如果您想要这种行为,则必须明确地说出。
通常,您可以重构,因此只需要__new__
,或者就不需要__new__
,或者使__init__
在已经初始化的对象上的行为有所不同。 但是,如果您确实愿意,Python确实允许您重新定义“作业”,因此SomeClass(arg1, arg2)
不一定__new__
调用__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
但是,这可能比这种情况下真正应具有的魔力还要深!