问题:为什么总是在__new __()之后调用__init __()?
我只是想简化我的一个类,并以与flyweight设计模式相同的样式介绍了一些功能。
但是,对于为什么__init__
总是被称为after ,我有点困惑__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
为什么?
I’m just trying to streamline one of my classes and have introduced some functionality in the same style as the flyweight design pattern.
However, I’m a bit confused as to why __init__
is always called after __new__
. I wasn’t expecting this. Can anyone tell me why this is happening and how I can implement this functionality otherwise? (Apart from putting the implementation into the __new__
which feels quite hacky.)
Here’s an example:
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()
Outputs:
NEW
INIT
EXISTS
INIT
EXISTS
INIT
Why?
回答 0
使用__new__
时,你需要控制一个新实例的创建。
使用
__init__
时,你需要一个新的实例的控件初始化。
__new__
是实例创建的第一步。它首先被调用,并负责返回您的类的新实例。
相反,
__init__
什么也不返回;创建实例后,它仅负责初始化实例。
通常,__new__
除非您要继承不可变类型(例如str,int,unicode或tuple),否则无需重写。
从2008年4月发布:何时使用__new__
vs __init__
?在mail.python.org上。
您应该考虑要尝试做的事通常是通过Factory完成的,这是最好的方法。使用__new__
不是一个好的清洁解决方案,因此请考虑使用工厂。在这里,您有一个很好的工厂示例。
Use __new__
when you need to control
the creation of a new instance.
Use
__init__
when you need to control initialization of a new instance.
__new__
is the first step of instance creation. It’s called first, and is
responsible for returning a new
instance of your class.
In contrast,
__init__
doesn’t return anything; it’s only responsible for initializing the
instance after it’s been created.
In general, you shouldn’t need to
override __new__
unless you’re
subclassing an immutable type like
str, int, unicode or tuple.
From April 2008 post: When to use __new__
vs. __init__
? on mail.python.org.
You should consider that what you are trying to do is usually done with a Factory and that’s the best way to do it. Using __new__
is not a good clean solution so please consider the usage of a factory. Here you have a good factory example.
回答 1
__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:
...
__new__
is static class method, while __init__
is instance method.
__new__
has to create the instance first, so __init__
can initialize it. Note that __init__
takes self
as parameter. Until you create instance there is no self
.
Now, I gather, that you’re trying to implement singleton pattern in Python. There are a few ways to do that.
Also, as of Python 2.6, you can use class decorators.
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
回答 2
在大多数众所周知的OO语言中,类似的表达式SomeClass(arg1, arg2)
将分配一个新实例,初始化该实例的属性,然后返回它。
在大多数著名的OO语言中,可以通过定义构造函数为每个类自定义“初始化实例的属性”部分,该构造函数基本上只是在新实例上运行的代码块(使用提供给构造函数表达式的参数) )来设置所需的任何初始条件。在Python中,这对应于class的__init__
方法。
Python的__new__
功能无非就是“分配新实例”部分的类似的按类自定义。当然,这允许您执行不同寻常的操作,例如返回现有实例而不是分配新实例。因此,在Python中,我们不应该真的认为这部分必然涉及分配。我们所需要的只是__new__
从某个地方提出一个合适的实例。
但这仍然只是工作的一半,Python系统无法知道有时您希望__init__
稍后再执行另一部分工作(),而有时又不想。如果您想要这种行为,则必须明确地说出。
通常,您可以重构,因此只需要__new__
,或者不需要__new__
,或者这样__init__
就可以在已初始化的对象上表现不同。但是,如果你真的想,Python不竟让你重新定义“工作”,所以SomeClass(arg1, arg2)
不一定需要__new__
后面__init__
。为此,您需要创建一个元类,并定义其__call__
方法。
元类只是类的类。而类的__call__
方法控制了当您调用类的实例时会发生什么。因此,metaclass的__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
但是,这可能比这种情况下真正应具有的魔力还要深!
In most well-known OO languages, an expression like SomeClass(arg1, arg2)
will allocate a new instance, initialise the instance’s attributes, and then return it.
In most well-known OO languages, the “initialise the instance’s attributes” part can be customised for each class by defining a constructor, which is basically just a block of code that operates on the new instance (using the arguments provided to the constructor expression) to set up whatever initial conditions are desired. In Python, this corresponds to the class’ __init__
method.
Python’s __new__
is nothing more and nothing less than similar per-class customisation of the “allocate a new instance” part. This of course allows you to do unusual things such as returning an existing instance rather than allocating a new one. So in Python, we shouldn’t really think of this part as necessarily involving allocation; all that we require is that __new__
comes up with a suitable instance from somewhere.
But it’s still only half of the job, and there’s no way for the Python system to know that sometimes you want to run the other half of the job (__init__
) afterwards and sometimes you don’t. If you want that behavior, you have to say so explicitly.
Often, you can refactor so you only need __new__
, or so you don’t need __new__
, or so that __init__
behaves differently on an already-initialised object. But if you really want to, Python does actually allow you to redefine “the job”, so that SomeClass(arg1, arg2)
doesn’t necessarily call __new__
followed by __init__
. To do this, you need to create a metaclass, and define its __call__
method.
A metaclass is just the class of a class. And a class’ __call__
method controls what happens when you call instances of the class. So a metaclass‘ __call__
method controls what happens when you call a class; i.e. it allows you to redefine the instance-creation mechanism from start to finish. This is the level at which you can most elegantly implement a completely non-standard instance creation process such as the singleton pattern. In fact, with less than 10 lines of code you can implement a Singleton
metaclass that then doesn’t even require you to futz with __new__
at all, and can turn any otherwise-normal class into a singleton by simply adding __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
However this is probably deeper magic than is really warranted for this situation!
回答 3
引用文档:
典型的实现通过使用带有适当参数的“ super(currentclass,cls).__ new __(cls [,…])”调用超类的__new __()方法,然后根据需要修改新创建的实例来创建该类的新实例。在返回之前。
…
如果__new __()不返回cls的实例,则将不会调用新实例的__init __()方法。
__new __()主要用于允许不可变类型的子类(例如int,str或tuple)自定义实例创建。
To quote the documentation:
Typical implementations create a new instance of the class by invoking
the superclass’s __new__() method using “super(currentclass,
cls).__new__(cls[, …])”with appropriate arguments and then
modifying the newly-created instance as necessary before returning it.
…
If __new__() does not return an instance of cls, then the new
instance’s __init__() method will not be invoked.
__new__() is intended mainly to allow subclasses of immutable
types (like int, str, or tuple) to customize instance creation.
回答 4
我意识到这个问题已经很久了,但是我也遇到了类似的问题。以下是我想要的:
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
I realize that this question is quite old but I had a similar issue.
The following did what I wanted:
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
I used this page as a resource http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html
回答 5
我认为这个问题的简单答案是,如果__new__
返回的值与类的类型相同,则__init__
函数将执行,否则将不会执行。在这种情况下,您的代码将返回A._dict('key')
与相同的类cls
,因此__init__
将被执行。
I think the simple answer to this question is that, if __new__
returns a value that is the same type as the class, the __init__
function executes, otherwise it won’t. In this case your code returns A._dict('key')
which is the same class as cls
, so __init__
will be executed.
回答 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
。
When __new__
returns instance of the same class, __init__
is run afterwards on returned object. I.e. you can NOT use __new__
to prevent __init__
from being run. Even if you return previously created object from __new__
, it will be double (triple, etc…) initialized by __init__
again and again.
Here is the generic approach to Singleton pattern which extends vartec answer above and fixes it:
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
Full story is here.
Another approach, which in fact involves __new__
is to use 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"
Please pay attention, that with this approach you need to decorate ALL of your methods with @classmethod
, because you’ll never use any real instance of MyClass
.
回答 7
参考此文档:
当对不可变的内置类型(例如数字和字符串)进行子类化时,有时在其他情况下,可以使用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:
...
Referring to this doc:
When subclassing immutable built-in types like numbers and strings,
and occasionally in other situations, the static method new comes
in handy. new is the first step in instance construction, invoked
before init.
The new method is called with the class as its
first argument; its responsibility is to return a new instance of that
class.
Compare this to init: init is called with an instance
as its first argument, and it doesn’t return anything; its
responsibility is to initialize the instance.
There are situations
where a new instance is created without calling init (for example
when the instance is loaded from a pickle). There is no way to create
a new instance without calling new (although in some cases you can
get away with calling a base class’s new).
Regarding what you wish to achieve, there also in same doc info about Singleton pattern
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
you may also use this implementation from PEP 318, using a decorator
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
回答 8
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
所以要小心不要顺带覆盖它。
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')
outputs:
NEW
INIT
NEW
INIT
EXISTS
NB As a side effect M._dict
property automatically becomes accessible from A
as A._dict
so take care not to overwrite it incidentally.
回答 9
__new__应该返回一个类的新的空白实例。然后调用__init__初始化该实例。您不是在__new__的“ NEW”情况下调用__init__,因此正在为您调用它。所调用的代码__new__
无法跟踪是否已在特定实例上调用__init__,也不会跟踪它,因为您在这里做的事情很不寻常。
您可以在__init__函数中向该对象添加一个属性,以指示它已被初始化。首先检查该属性是否存在,如果已存在,请不要继续进行。
__new__ should return a new, blank instance of a class. __init__ is then called to initialise that instance. You’re not calling __init__ in the “NEW” case of __new__, so it’s being called for you. The code that is calling __new__
doesn’t keep track of whether __init__ has been called on a particular instance or not nor should it, because you’re doing something very unusual here.
You could add an attribute to the object in the __init__ function to indicate that it’s been initialised. Check for the existence of that attribute as the first thing in __init__ and don’t proceed any further if it has been.
回答 10
对@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__
步骤,使用标记您需要标记该对象。
An update to @AntonyHatchkins answer, you probably want a separate dictionary of instances for each class of the metatype, meaning that you should have an __init__
method in the metaclass to initialize your class object with that dictionary instead of making it global across all the classes.
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()
I have gone ahead and updated the original code with an __init__
method and changed the syntax to Python 3 notation (no-arg call to super
and metaclass in the class arguments instead of as an attribute).
Either way, the important point here is that your class initializer (__call__
method) will not execute either __new__
or __init__
if the key is found. This is much cleaner than using __new__
, which requires you to mark the object if you want to skip the default __init__
step.
回答 11
深入了解这一点!
CPython中泛型类的类型为type
,其基类为Object
(除非您明确定义另一个基类,如元类)。低级呼叫的顺序可以在这里找到。所谓的第一种方法是type_call
,然后调用tp_new
,然后tp_init
。
这里有趣的部分是tp_new
将调用Object
的(基类)new方法object_new
,该方法执行tp_alloc
(PyType_GenericAlloc
)为对象分配内存的方法:)
那时在内存中创建对象,然后__init__
调用该方法。如果__init__
未在您的类中实现,则将object_init
调用gets并且不执行任何操作:)
然后type_call
只返回绑定到变量的对象。
Digging little deeper into that!
The type of a generic class in CPython is type
and its base class is Object
(Unless you explicitly define another base class like a metaclass). The sequence of low level calls can be found here. The first method called is the type_call
which then calls tp_new
and then tp_init
.
The interesting part here is that tp_new
will call the Object
‘s (base class) new method object_new
which does a tp_alloc
(PyType_GenericAlloc
) which allocates the memory for the object :)
At that point the object is created in memory and then the __init__
method gets called. If __init__
is not implemented in your class then the object_init
gets called and it does nothing :)
Then type_call
just returns the object which binds to your variable.
回答 12
应该将其__init__
视为传统OO语言中的一种简单构造函数。例如,如果您熟悉Java或C ++,则向构造函数隐式传递一个指向其自身实例的指针。对于Java,它是this
变量。如果要检查为Java生成的字节码,则有人会注意到有两个调用。第一个调用是对“ new”方法的调用,然后下一个调用是对init方法的调用(这是对用户定义的构造函数的实际调用)。通过两步过程,可以在调用类的构造方法(该实例的另一个方法)之前创建实际实例。
现在,对于Python,__new__
是用户可以访问的附加功能。Java由于其类型性质而没有提供这种灵活性。如果一种语言提供了该功能,那么的实现者__new__
可以在返回实例之前用该方法做很多事情,包括在某些情况下创建不相关对象的全新实例。而且,这种方法对于Python尤其适用于不可变类型也很有效。
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.
Now, in the case of Python, __new__
is a added facility that is accessible to the user. Java does not provide that flexibility, due to its typed nature. If a language provided that facility, then the implementor of __new__
could do many things in that method before returning the instance, including creating a totally new instance of a unrelated object in some cases. And, this approach also works out well for especially for immutable types in the case of Python.
回答 13
但是,对于为什么__init__
总是被称为after ,我有点困惑__new__
。
我认为C ++类比在这里会很有用:
__new__
只需为对象分配内存。一个对象的实例变量需要内存来保存它,这就是该步骤__new__
要做的。
__init__
将对象的内部变量初始化为特定值(可以是默认值)。
However, I’m a bit confused as to why __init__
is always called after __new__
.
I think the C++ analogy would be useful here:
__new__
simply allocates memory for the object. The instance variables of an object needs memory to hold it, and this is what the step __new__
would do.
__init__
initialize the internal variables of the object to specific values (could be default).
回答 14
在__init__
经过被称为__new__
所以,当你在子类中重写它,你添加的代码仍然会被调用。
如果您尝试对已经具有a的类进行子类化,则__new__
对此一无所知的人可能会先改编__init__
并将调用向下转发到子类__init__
。这种呼叫__init__
后的约定__new__
有助于按预期工作。
在__init__
仍然需要允许超任何参数__new__
需要的,但不这样做通常会建立一个清晰的运行时错误。并且__new__
可能应该明确允许*args
和’** kw’,以明确表示扩展名是可以的。
这是普遍不好的形式既有__new__
和__init__
在继承同级别在同一个Class,因为原来的海报中描述的行为。
The __init__
is called after __new__
so that when you override it in a subclass, your added code will still get called.
If you are trying to subclass a class that already has a __new__
, someone unaware of this might start by adapting the __init__
and forwarding the call down to the subclass __init__
. This convention of calling __init__
after __new__
helps that work as expected.
The __init__
still needs to allow for any parameters the superclass __new__
needed, but failing to do so will usually create a clear runtime error. And the __new__
should probably explicitly allow for *args
and ‘**kw’, to make it clear that extension is OK.
It is generally bad form to have both __new__
and __init__
in the same class at the same level of inheritance, because of the behavior the original poster described.
回答 15
但是,对于为什么__init__
总是被称为after ,我有点困惑__new__
。
除了这样做是没有其他原因的。__new__
没有初始化类的责任,其他方法有责任(__call__
,可能-我不确定)。
我没想到这一点。谁能告诉我为什么会这样,否则我如何实现此功能?(除了将实现放入__new__
hack之外)。
你可以有__init__
做什么,如果它已经被初始化,或者你可以写一个新的一个新的元类__call__
,只有调用__init__
新的实例,否则直接返回__new__(...)
。
However, I’m a bit confused as to why __init__
is always called after __new__
.
Not much of a reason other than that it just is done that way. __new__
doesn’t have the responsibility of initializing the class, some other method does (__call__
, possibly– I don’t know for sure).
I wasn’t expecting this. Can anyone tell me why this is happening and how I implement this functionality otherwise? (apart from putting the implementation into the __new__
which feels quite hacky).
You could have __init__
do nothing if it’s already been initialized, or you could write a new metaclass with a new __call__
that only calls __init__
on new instances, and otherwise just returns __new__(...)
.
回答 16
原因很简单,新函数用于创建实例,而init用于初始化实例。在初始化之前,应先创建实例。这就是为什么应该在init之前调用new的原因。
The simple reason is that the new is used for creating an instance, while init is used for initializing the instance. Before initializing, the instance should be created first. That’s why new should be called before init.
回答 17
现在我遇到了同样的问题,由于某些原因,我决定避免使用装饰器,工厂和元类。我这样做是这样的:
主文件
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,它会与其他类发生冲突-除非您在每个子代中都定义了单独的缓存,否则这不是我们想要的。
- 警告:父辈的祖父母类看起来很奇怪。[未验证]
在线尝试!
Now I’ve got the same problem, and for some reasons I decided to avoid decorators, factories and metaclasses. I did it like this:
Main file
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
Example classes
class A(Parent):
def __init__(self, n):
print("A.__init__", n)
class B(Parent):
def __init__(self, n):
print("B.__init__", n)
In use
>>> 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>
- Warning: You shouldn’t initialize Parent, it will collide with other classes – unless you defined separate cache in each of the children, that’s not what we want.
- Warning: It seems a class with Parent as grandparent behaves weird. [Unverified]
Try it online!