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

Dan*_*Dan 525 python design-patterns class-design

我只是想简化我的一个类,并引入了一些与flyweight设计模式相同的功能.

但是,我有点困惑,为什么__init__总是被称为__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()
Run Code Online (Sandbox Code Playgroud)

输出:

NEW
INIT

EXISTS
INIT

EXISTS
INIT
Run Code Online (Sandbox Code Playgroud)

为什么?

mpe*_*son 552

需要控制新实例的创建时,请使用__new__.需要控制新实例的初始化时,请使用 __init__.

__new__是实例创建的第一步.它首先被调用,并负责返回您的类的新实例.相比之下, __ init__不会返回任何内容; 它只负责在创建实例后初始化它.

通常,除非您继承了str,int,unicode或tuple之类的不可变类型,否则不应该覆盖__new__.

来自:http://mail.python.org/pipermail/tutor/2008-April/061426.html

您应该考虑到您尝试做的事情通常是通过工厂完成的,这是最好的方法.使用__new__不是一个很好的清洁解决方案,所以请考虑工厂的使用.在这里你有一个很好的工厂例子.

  • 它现在的方式是单身人士,而不是工厂. (18认同)
  • 对不起,我不同意`__new__`的使用应严格限于所述的情况.我发现它对于实现可扩展的泛型类工厂非常有用 - 请参阅[我的回答](http://stackoverflow.com/a/28076300/355230)问题_Improper使用`__new__`在Python中生成类? _这样做的一个例子. (10认同)
  • 我强烈不同意“__new__”是一个糟糕的解决方案。在限制构造函数充当初始值设定项(防止您返回现有对象)的语言中,工厂模式是必要的;与大多数设计模式一样(尤其是早期由于语言不灵活性而专门为 Java 发明的大量设计模式),它是一种以一致的方式解决语言限制的方法。Python 没有这个限制;在这种情况下,您可以使用“__new__”,并使用“@classmethod”替代构造函数来从变体参数进行构造。 (4认同)
  • 最终在 Factory 类中使​​用了 `__new__`,它变得非常干净,所以感谢您的输入。 (2认同)

var*_*tec 163

__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:
  ...
Run Code Online (Sandbox Code Playgroud)

  • 为什么字典?由于cls将始终相同,并且您为每个单例获得了一个新的字典,因此您创建的字典中只包含一个项目. (11认同)
  • @Tyler Long,我不太明白"@singleton"是如何工作的?因为装饰器返回一个函数,但MyClass是一个类. (8认同)
  • @Alcott:不需要意见 - [文档](http://docs.python.org/reference/datamodel.html?highlight=__new__#object.__new__)同意你的看法. (7认同)
  • @TylerLong Alcott有一个很好的观点.这导致名称`MyClass`绑定到返回原始类的实例的函数.但是现在根本没有办法引用原始类,并且`MyClass`是一个函数中断`isinstance` /`issubclass`检查,直接访问类属性/方法作为`MyClass.something`,在`中命名类super` calls等等.这是否是一个问题取决于你正在应用它的类(以及程序的其余部分). (4认同)
  • @Alcott.是的装饰者返回一个函数.但是类和函数都是可调用的.我认为instances = {}应该是一个全局变量. (2认同)
  • @TylerLong Winston Ewert 关于实例是局部变量的说法是正确的。在 getinstance() 顶部添加 `print len(instances)` ,即使其他类用 @singleton 包装,它也只是 0 或 1 (2认同)
  • MyClass是一个* class *,但是在由`singleton`装饰后,它却变成了* function *,这会引起问题吗? (2认同)

Ben*_*Ben 142

在大多数众所周知的OO语言中,表达式SomeClass(arg1, arg2)将分配一个新实例,初始化实例的属性,然后返回它.

在大多数众所周知的OO语言中,可以通过定义构造函数为每个类定制"初始化实例的属性"部分,构造函数基本上只是在新实例上运行的代码块(使用提供给构造函数表达式的参数) )设置所需的任何初始条件.在Python中,这对应于类的__init__方法.

Python __new__不仅仅是"分配新实例"部分的类似定制.这当然允许您执行不寻常的事情,例如返回现有实例而不是分配新实例.所以在Python中,我们不应该认为这部分必然涉及分配; 我们需要的就是__new__从某个地方提供合适的实例.

但它仍然只是工作的一半,并且Python系统无法知道有时候你想要运行另一半的工作(__init__),有时你不会.如果您想要这种行为,您必须明确说出来.

通常,您可以重构,因此您只需要__new__,或者您不需要__new__,或者__init__对已经初始化的对象执行不同的操作.但是,如果你真的想,Python不竟让你重新定义"工作",所以SomeClass(arg1, arg2)不一定需要__new__后面__init__.为此,您需要创建元类,并定义其__call__方法.

元类只是类的类.并且类' __call__方法控制调用类的实例时发生的事情.因此,元类 ' __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
Run Code Online (Sandbox Code Playgroud)

然而,这可能是比这种情况真正需要的更深层次的魔法!

  • 太棒了:简单,并且不会像装饰那样让课堂上的工作变得烦人。 (3认同)

tgr*_*ray 23

引用文档:

典型的实现通过使用"super(currentclass,cls).__ new __(cls [,...])"使用适当的参数调用超类的__new __()方法然后根据需要修改新创建的实例来创建类的新实例在回来之前.

...

如果__new __()没有返回cls的实例,则不会调用新实例的__init __()方法.

__new __()主要用于允许不可变类型的子类(如int,str或tuple)自定义实例创建.

  • `如果__new __()没有返回cls的实例,那么将不会调用新实例的__init __()方法.这是一个重点.如果返回不同的实例,则永远不会调用原始的`__init__`. (15认同)

小智 11

我意识到这个问题已经很老了,但我遇到了类似的问题.以下做了我想要的:

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
Run Code Online (Sandbox Code Playgroud)

我使用此页面作为资源http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html

  • 注意:\ _\__ new\_\_ _始终返回适当的对象,因此始终调用\ _\_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ (6认同)

PMN*_*PMN 10

我认为这个问题的简单答案是,如果__new__返回的值与类的类型相同,则__init__函数执行,否则不会.在这种情况下,您的代码将返回A._dict('key')与其相同的类cls,因此__init__将执行.


Zaa*_*Hai 9

__new__返回同一类的实例时,__init__在返回的对象之后运行.即你不能__new__用来防止__init__被运行.即使你从之前返回创建的对象__new__,它也会被__init__一次又一次地初始化为double(triple等).

以下是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
Run Code Online (Sandbox Code Playgroud)

全文在这里.

实际上涉及的另一种方法__new__是使用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"
Run Code Online (Sandbox Code Playgroud)

请注意,使用这种方法你需要装饰你的所有方法@classmethod,因为你永远不会使用任何真实的实例MyClass.


kir*_*off 6

参考此文档

当子类化不可变的内置类型(如数字和字符串)时,有时在其他情况下,静态方法new会派上用场。new是实例构建的第一步,在init之前调用。

方法被称为与类作为第一个参数; 它的职责是返回该类的一个新实例。

将此与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
Run Code Online (Sandbox Code Playgroud)

你也可以使用 PEP 318 中的这个实现,使用装饰器

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
...
Run Code Online (Sandbox Code Playgroud)

  • 从 `__new__` 调用一个混蛋形式的 `init` 听起来真的很黑客。这就是元类的用途。 (2认同)

Ant*_*ins 6

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')
Run Code Online (Sandbox Code Playgroud)

输出:

NEW
INIT

NEW
INIT

EXISTS
Run Code Online (Sandbox Code Playgroud)

NB作为一个副作用M._dict属性自动变为可访问从AA._dict所以要小心不要顺带覆盖它.


And*_*son 5

__new__应该返回一个类的新的空白实例。然后调用__init__初始化该实例。您不是在__new__的“ NEW”情况下调用__init__,因此正在为您调用它。所调用的代码__new__无法跟踪是否已在特定实例上调用__init__,因为您在这里所做的事情很不寻常。

您可以在__init__函数中向该对象添加一个属性,以指示它已被初始化。首先检查该属性是否存在,如果已存在,请不要继续进行。


grd*_*vnl 5

人们应该将其__init__视为传统 OO 语言中的简单构造函数。例如,如果您熟悉 Java 或 C++,则构造函数会隐式传递一个指向其自身实例的指针。在 Java 中,它是this变量。如果要检查为 Java 生成的字节码,会注意到两个调用。第一次调用是对“new”方法的调用,然后是对 init 方法的调用(这是对用户定义的构造函数的实际调用)。这两个步骤的过程允许在调用类的构造函数方法之前创建实际实例,该方法只是该实例的另一个方法。

现在,在 Python 的情况下,__new__是用户可以访问的附加工具。由于其类型化性质,Java 不提供这种灵活性。如果一种语言提供了这种便利,那么__new__在返回实例之前, 的实现者可以在该方法中做很多事情,包括在某些情况下创建一个不相关对象的全新实例。而且,这种方法对于 Python 的不可变类型也很有效。


小智 5

深入一点!

CPython 中泛型类的类型是type,其基类是Object(除非您明确定义另一个基类,如元类)。可以在此处找到低级别调用的序列。调用的第一个方法是type_callwhich then 调用tp_new然后tp_init

这里有趣的部分是tp_new将调用Object's(基类)新方法object_new,该方法执行tp_alloc( PyType_GenericAlloc) 为对象分配内存:)

此时对象在内存中创建,然后__init__方法被调用。如果__init__没有在您的类中实现,则object_init调用它并且它什么都不做:)

然后type_call只返回绑定到您的变量的对象。


Mad*_*ist 5

对@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()
Run Code Online (Sandbox Code Playgroud)

我继续使用一种__init__方法更新了原始代码,并将语法更改为Python 3表示法(super类参数中的no-arg调用和metaclass而不是作为属性)。

无论哪种方式,最重要的一点是,你的类初始化函数(__call__方法)将不会执行任何__new__或者__init__如果键被找到。这比使用干净得多__new__,如果要跳过默认__init__步骤,使用标记您需要标记该对象。


HAl*_*tos 5

但是,我对为什么__init__总是在 之后调用有点困惑__new__

我认为 C++ 的类比在这里很有用:

  1. __new__简单地为对象分配内存。对象的实例变量需要内存来保存它,这就是该步骤__new__要做的事情。

  2. __init__ 将对象的内部变量初始化为特定值(可能是默认值)。