为什么__init __()后,总是叫__new __()?

问题描述 投票:495回答:18

我只是想简化我的课之一,并推出在同一风格为flyweight design pattern一些功能。

不过,我有点困惑,为什么__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

为什么?

python design-patterns class-design
18个回答
538
投票

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

当你需要控制一个新的实例初始化使用__init__。

__new__是实例创建的第一个步骤。它的第一个调用,并负责退回类的新实例。

相比之下,__init__不返回任何东西;它只是为它的创建后,初始化实例负责。

在一般情况下,你不应该需要重写__new__除非你继承一个不变的类型像海峡,INT,Unicode或元组。

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

你应该考虑你正在做什么通常与Factory完成,这是做到这一点的最好办法。使用__new__是不是一个好干净的解决方案,所以请考虑工厂的使用。在这里,你有a good factory example


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()

我已经取得了进展,并与__init__方法更新的原代码,并改变了语法到Python 3符号(不带参数调用super和元类的类参数,而不是作为一个属性)。

无论哪种方式,这里最重要的一点是,如果关键是找到你的类初始化(__call__法)将不会执行任何__new____init__。这比使用__new__,如果你想跳过默认__init__步骤,需要您标记对象更清洁。


4
投票

Referring to this doc

当继承不变的内置类型,如数字和字符串,偶尔在其他情况下,新的静态方法就派上用场了。新的是例如建筑的第一步,之前的init调用。

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

与此相比,INIT:init的调用一个实例作为第一个参数,它不返回任何东西;其职责是初始化实例。

有没有在那里(当实例从泡菜装为例)调用init程序创建新实例的情况。有没有办法来创建无需调用新的(尽管在某些情况下,你可以逃脱调用基类的新的)一个新的实例。

至于你想达到什么样的,也有在大约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:
...

4
投票

挖得更深一些成!

该类型CPython的泛型类是type其基类是Object(除非你明确地定义另一个基类像元类)。水平低调用序列可以发现here。所谓的第一种方法是type_call然后调用tp_new然后tp_init

这里最有意思的是,tp_new将调用其分配内存的对象:)的Object的(基类)的新方法object_new它做了tp_allocPyType_GenericAlloc

在该点的对象在内存中创建,然后将__init__方法被调用。如果__init__未在类中实现,则object_init被调用,它什么也不做:)

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


4
投票

每个人都应该看__init__在传统的面向对象语言的一个简单的构造。例如,如果你熟悉Java或C ++中,构造函数传递指针,以自己的实例含蓄。在Java的情况下,它是this变量。如果有人来检查生成的Java字节码,一会发现两个电话。所述第一呼叫是一个“新”方法,然后下一个呼叫是init方法(这是实际呼叫到用户定义的构造)。这两步过程使实际实例的创建调用这是该实例的另一种方法在类的构造方法之前。

现在,在Python的情况下,__new__是一个补充设施,为用户访问。 Java不提供的灵活性,由于其类型特性。如果一种语言提供的设施,然后__new__的实现者可以返回实例,包括在某些情况下,创造一个不相关的对象的一个​​全新的实例之前,做在方法很多东西。而且,这种方法也适用进行的顺利尤其是恒定类型在Python的情况。


3
投票

不过,我有点困惑,为什么__init____new__后,总是叫。

我认为C ++的类比是有用的位置:

  1. __new__简单地为对象分配内存。一个对象需要存储的实例变量来保存它,这就是步骤__new__会做。
  2. __init__初始化对象到特定值的内部变量(可以是默认值)。

2
投票

__init____new__后调用,这样当你在子类中重写它,让您的代码仍然可以被调用。

如果您正试图子类化已经有一个__new__一类,有人不知道这可能通过调整__init__和转移呼叫到子类__init__开始。后__init__调用__new__本公约有助于这项工作预期。

__init__仍然需要允许超__new__所需的任何参数,但不这样做通常会建立一个清晰的运行时错误。而__new__也许应该明确允许*args和“**千瓦”,使之清楚,扩展确定。

这是一般不好的形式,在继承同级别同时拥有__new____init__在同一个班,因为行为的楼主描述。


1
投票

不过,我有点困惑,为什么__init____new__后,总是叫。

没有太多的比它只是这样做的方式以外的原因。 __new__没有初始化类,其他的一些方法确实有责任(__call__,possibly--我不肯定知道)。

我没想到这一点。谁能告诉我为什么发生这种情况,我该如何以其它方式实现这个功能? (除了把实施成感觉相当哈克的__new__)。

你可以有__init__做什么,如果它已经被初始化,或者你可以写一个新的元类与新的__call__,只有在新的情况下调用__init__,否则直接返回__new__(...)


1
投票

原因很简单,新的用于创建一个实例,同时初始化用于初始化实例。在初始化之前,实例应该先创建。这就是为什么新的应初始化之前被调用。


1
投票

现在,我已经得到了同样的问题,因为某些原因,我决定,以避免装饰,工厂和元类。我这样做是这样的:

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)

正在使用

>>> 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>
  • 警告:你不应该初始化父,它将与其他类碰撞 - 除非你在每一个孩子的规定单独的缓存,这不是我们想要的。
  • 警告:这似乎与父类的祖父母行为怪异。 [未验证]

Try it online!


163
投票

__new__是静态类方法,而__init__是实例方法。 __new__必须先创建实例,所以__init__可以初始化它。需要注意的是__init__需要self作为参数。直到你创建实例没有self

现在,我猜想,那你想在Python中实现singleton pattern。有几个方法可以做到这一点。

此外,由于Python 2.6中,你可以使用类decorators

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

@singleton
class MyClass:
  ...

136
投票

在最知名的面向对象语言,如SomeClass(arg1, arg2)表达式将分配一个新的实例,初始化实例的属性,然后返回。

在大多数公知的面向对象的语言中,“初始化实例的属性”部分可以通过定义一个构造函数,它基本上是一个块中的代码,关于新的实例操作(使用提供给构造表达式的参数来定制用于每个类别)来设置任何期望的初始条件。在Python,这对应于类的__init__方法。

Python的__new__是仅此而已低于同类每个类定制“分配一个新的实例”的一部分。当然,这可以让你做不寻常的事情,如返回现有的实例,而不是分配一个新的。因此,在Python中,我们不应该真的认为这部分是不一定涉及分配;我们要求所有的是,__new__从什么地方合适的情况下出现。

但它仍然只是工作的一半,而且也没有办法让Python的系统知道,有时候你想要运行的作业(__init__)的另一半之后,有时你不知道。如果你想要的行为,你必须明确这样说。

通常情况下,你可以重构,所以你只需要__new__,或者你不需要__new__,或使__init__行为不同的已初始化的对象。但是,如果你真的想,Python不竟让你重新定义“工作”,让SomeClass(arg1, arg2)不一定需要__new__其次__init__。要做到这一点,你需要创建一个元类,并定义其__call__方法。

一个元只是类的类。和A类__call__方法控制,当你调用类的实例会发生什么。因此,元类__call__方法控制,当你调用一个类发生了什么;即它可以让你的实例创建机制重新从开始到结束。这是您可以在哪些最优雅的实现完全非标实例的创建过程中,如Singleton模式的水平。事实上,用了不到10行代码就可以实现一个Singleton元类是那么甚至不要求你与__new__在所有futz,并且可以通过简单地增加__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

然而,这可能是更深层次的魔法不是真的必要针对这种情况!


22
投票

引述documentation

典型的实现方式通过使用调用超类的__new __()方法“超级(currentclass,CLS).__新__(CLS [,...])”与适当的参数,然后修改为所需要的新创建的实例来创建类的新实例之前它返回。

...

如果__new __()不返回CLS的实例,那么新实例的__init __()方法将不会被调用。

__new __()的目的主要是,以允许稳定的类型的子类(如int,STR,或元组)来定制实例创建。


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

我用这个网页作为一种资源http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html


9
投票

我想简单回答这个问题是,如果__new__返回的值是相同类型的课堂上,__init__函数执行,否则它不会。在这种情况下,你的代码返回A._dict('key')这是同一类cls,所以__init__将被执行。


9
投票

__new__返回相同的类的实例,__init__被返回的对象之后运行。即你不能用__new__防止__init__从正在运行。即使你从__new__返回先前创建的对象,这将是双(三人间,等...)通过__init__连连初始化。

下面是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

全文是here

另一种方法,这实际上涉及__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"

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


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')

输出:

NEW
INIT

NEW
INIT

EXISTS

NB作为一个副作用M._dict财产A所以要小心不要顺便改写,它会自动变为从A._dict访问。


5
投票

__new__应该返回一个类的新的空实例。然后__init__被调用来初始化该实例。你不是在__new__的“NEW”的情况下调用__init__,所以它被称为你。正在调用__new__不守是否__init__被称为在特定情况下轨道或不是也不应该,因为你在这里做的事情非常不寻常的代码。

你可以在__init__功能的属性添加到该对象,以表明它已经初始化。检查该属性为__init__的第一件事情的存在,并没有进一步进行任何若已。

© www.soinside.com 2019 - 2024. All rights reserved.