我只是试图简化我的一个类,并引入了一些与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?


当前回答

然而,我有点困惑,为什么__init__总是在__new__之后被调用。

我认为c++的类比在这里是有用的:

__new__只是为对象分配内存。对象的实例变量需要内存来保存它,这就是步骤__new__要做的。 __init__将对象的内部变量初始化为特定的值(可以是默认值)。

其他回答

当实例化一个类时,首先调用__new__()来创建类的实例,然后调用__init__()来初始化实例。

__new__ ():

调用它来创建类cls. ...的新实例 如果在对象构造期间调用__new__(),它返回一个 实例,则新实例的__init__()方法将为 像__init__(self[,…])一样调用,…

__init__ ():

在实例创建后调用(通过__new__()),… 因为__new__()和__init__()在构造对象时一起工作 (__new__()来创建它,__init__()来定制它),…

例如,在实例化Teacher类时,首先调用__new__()来创建Teacher类的实例,然后调用__init__()来初始化实例,如下所示:

class Teacher:
    def __init__(self, name):
        self.name = name
        
class Student:
    def __init__(self, name):
        self.name = name

obj = Teacher("John") # Instantiation

print(obj.name)

输出如下:

<class '__main__.Teacher'>
John

并且,使用Teacher类实例的__new__(),我们可以创建Student类的实例,如下所示:

# ...

obj = Teacher("John")
print(type(obj))
print(obj.name)

obj = obj.__new__(Student) # Creates the instance of "Student" class
print(type(obj))

现在,创建了Student类的实例,如下所示:

<class '__main__.Teacher'>
<__main__.Teacher object at 0x7f4e3950bf10>
<class '__main__.Student'> # Here

接下来,如果我们尝试从Student类的**实例中获取name变量的值,如下所示:

obj = Teacher("John")
print(type(obj))
print(obj.name)

obj = obj.__new__(Student)
print(type(obj))
print(obj.name) # Tries to get the value of "name" variable

发生以下错误是因为Student类的实例还没有被__init__()初始化:

AttributeError: 'Student'对象没有属性'name'

因此,我们初始化Student类的实例,如下所示:

obj = Teacher("John") 
print(type(obj))
print(obj.name)

obj = obj.__new__(Student)
print(type(obj))
obj.__init__("Tom") # Initializes the instance of "Student" class
print(obj.name)

然后,我们可以从Student类的实例中获取name变量的值,如下所示:

<class '__main__.Teacher'>
John
<class '__main__.Student'>
Tom # Here

__init__被调用在__new__之后,所以当你在子类中重写它时,你添加的代码仍然会被调用。

如果你试图子类化一个已经有__new__的类,不知道这一点的人可能会通过调整__init__开始,并将调用转发到子类__init__。这种在__new__之后调用__init__的约定有助于按预期工作。

__init__仍然需要允许超类__new__所需的任何参数,但如果不这样做,通常会产生一个明确的运行时错误。__new__可能应该显式地允许使用*args和'**kw',以清楚地表明扩展是OK的。

在同一个类中同时拥有__new__和__init__在同一继承级别通常是不好的形式,因为原来的海报所描述的行为。

现在我又遇到了同样的问题,出于某些原因,我决定避免使用装饰器、工厂和元类。我是这样做的:

主文件

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__的“NEW”情况下调用__init__,所以它是为你调用的。调用__new__的代码不会跟踪__init__是否在特定实例上被调用,也不应该跟踪,因为你在这里做了一些非常不寻常的事情。

你可以在__init__函数中为对象添加一个属性,以表明它已被初始化。检查该属性是否作为__init__中的第一个属性存在,如果已经存在,则不要继续进行任何操作。

参考这个文档:

在继承不可变内置类型(如数字和字符串)时, 偶尔在其他情况下,会出现静态方法__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:
...