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

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

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

没有太多的原因,除了它就是这样做的。__new__没有初始化类的责任,其他一些方法有(__call__,可能——我不确定)。

我没想到会这样。有人能告诉我为什么会发生这种情况,以及我如何实现这个功能吗?(除了将实现放在__new__中,这感觉相当粗糙)。

如果__init__已经初始化,你可以让它什么都不做,或者你可以用一个新的__call__写一个新的元类,它只在新实例上调用__init__,否则只返回__new__(…)。

当你需要控制时使用__new__ 创建一个新实例。

使用 __init__当你需要控制一个新实例的初始化时。 __new__是创建实例的第一步。它叫做first,并且是 负责退货 类的实例。

相比之下, __init__不返回任何东西;它只负责初始化 实例。 一般来说,你不需要这样做 重写__new__,除非你 继承一个不可变类型的子类 Str, int, unicode或tuple。

从2008年4月的帖子:什么时候使用__new__ vs. __init__?在mail.python.org上。

您应该考虑到您正在尝试做的事情通常是通过Factory完成的,这是最好的方法。使用__new__不是一个好的干净的解决方案,所以请考虑使用工厂。下面是一个很好的例子:ActiveState FᴀᴄᴛᴏʀʏᴘᴀᴛᴛᴇʀɴRecipe。

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中作为a ._dict访问,所以注意不要顺便覆盖它。