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


当前回答

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访问,所以注意不要顺便覆盖它。

其他回答

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

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

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

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

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

原因很简单,new用于创建实例,而init用于初始化实例。在初始化之前,应该先创建实例。这就是为什么new应该在init之前调用。

我知道这个问题很老了,但我也遇到过类似的问题。 以下是我想要的:

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

当__new__返回同一类的实例时,__init__随后在返回的对象上运行。也就是说,你不能使用__new__来阻止__init__被运行。即使你从__new__返回之前创建的对象,它也会被__init__一次又一次地初始化。

下面是单例模式的通用方法,它扩展了上面的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的任何真实实例。

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

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

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