为什么总是在__new __()之后调用__init __()?

问题:为什么总是在__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属性会自动变成可触及AA._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_allocPyType_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 ++类比在这里会很有用:

  1. __new__只需为对象分配内存。一个对象的实例变量需要内存来保存它,这就是该步骤__new__要做的。

  2. __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:

  1. __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.

  2. __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!