641

我只是想简化我的一个类,并引入了一些与享元设计模式相同风格的功能。

但是,我有点困惑为什么__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()

输出:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

为什么?

4

18 回答 18

672

__new__当您需要控制新实例的创建时使用。

__init__当您需要控制新实例的初始化时使用。

__new__是创建实例的第一步。它首先被调用,并负责返回你的类的一个新实例。

相反, __init__不返回任何东西;它只负责在创建实例后对其进行初始化。

__new__通常,除非您将不可变类型(如 str、int、unicode 或 tuple)子类化,否则您不需要重写。

来自 2008 年 4 月的帖子:何时使用__new__vs. __init__在 mail.python.org 上。

您应该考虑到您尝试做的事情通常是通过工厂完成的,这是最好的方法。使用__new__不是一个好的清洁解决方案,因此请考虑使用工厂。这是一个很好的例子:ActiveState Fᴀᴄᴛᴏʀʏ ᴘᴀᴛᴛᴇʀɴ 食谱

于 2009-03-23T17:23:04.923 回答
190

__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:
  ...
于 2009-03-23T17:20:19.157 回答
169

在大多数著名的 OO 语言中,表达式 likeSomeClass(arg1, arg2)将分配一个新实例,初始化该实例的属性,然后返回它。

在大多数著名的 OO 语言中,可以通过定义构造函数为每个类自定义“初始化实例的属性”部分,该构造函数基本上只是对新实例进行操作的代码块(使用提供给构造函数表达式的参数) 来设置所需的任何初始条件。在 Python 中,这对应于类的__init__方法。

Python__new__无非就是“分配一个新实例”部分的类似的每类定制。这当然允许您执行不寻常的事情,例如返回现有实例而不是分配新实例。所以在 Python 中,我们不应该真的认为这部分必然涉及分配;我们所需要的只是__new__从某个地方提出一个合适的实例。

但这仍然只是工作的一半,Python 系统无法知道有时你想在__init__之后运行另一半工作( ),而有时你不想。如果你想要这种行为,你必须明确地说出来。

通常,您可以重构以便只需要__new__,或者不需要__new__,或者__init__在已经初始化的对象上表现不同。但如果你真的想这样做,Python 实际上确实允许你重新定义“工作”,所以SomeClass(arg1, arg2)不一定 call__new__后跟__init__. 为此,您需要创建一个元类,并定义它的__call__方法。

元类只是一个类的类。类的__call__方法控制调用类实例时发生的情况。所以一个metaclass__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

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

于 2011-12-29T07:39:47.963 回答
29

引用文档

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

...

如果 __new__() 没有返回 cls 的实例,则不会调用新实例的 __init__() 方法。

__new__() 主要用于允许不可变类型(如 int、str 或 tuple)的子类自定义实例创建。

于 2009-03-23T17:29:23.317 回答
13

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

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

于 2013-05-13T14:12:10.363 回答
11

__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.

于 2015-01-26T21:25:19.400 回答
10

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

于 2013-07-07T14:45:17.557 回答
7
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请注意不要意外覆盖它。

于 2015-07-30T12:04:51.010 回答
6

参考此文档

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

以类作为第一个参数调用新方法;它的职责是返回该类的一个新实例。

将此与init进行比较:init以实例作为其第一个参数调用,它不返回任何内容;它的职责是初始化实例。

在某些情况下,在不调用init的情况下创建了新实例(例如,从 pickle 加载实例时)。如果不调用new就无法创建新实例(尽管在某些情况下,您可以通过调用基类的new来逃避)。

关于您希望实现的目标,还有关于 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:
...
于 2013-09-30T06:13:28.573 回答
6

@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__如果找到密钥。这比 using 干净得多__new__,如果要跳过默认__init__步骤,则需要标记对象。

于 2017-07-18T19:39:06.347 回答
5

__new__ 应该返回一个新的、空白的类实例。然后调用 __init__ 来初始化该实例。你没有在 __new__ 的“新”案例中调用 __init__,所以它是为你调用的。正在调用的代码__new__不会跟踪 __init__ 是否已在特定实例上被调用,也不应该跟踪,因为您在这里做了一些非常不寻常的事情。

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

于 2009-03-23T17:23:42.907 回答
5

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

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

于 2014-04-24T01:30:44.927 回答
5

再深入一点!

CPython 中泛型类的类型是type,它的基类是Object(除非您明确定义另一个基类,如元类)。可以在此处找到低级别调用的顺序。调用的第一个方法是type_callthen 调用tp_newthen tp_init

这里有趣的部分是tp_new它将调用Object的(基类)新方法object_new执行一个tp_allocPyType_GenericAlloc)为对象分配内存:)

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

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

于 2016-11-15T20:47:55.403 回答
5

但是,我有点困惑为什么__init__总是在__new__.

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

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

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

于 2019-02-11T22:38:05.490 回答
2

__init__之后调用,因此__new__当您在子类中覆盖它时,您添加的代码仍将被调用。

如果您尝试对已经具有 的类进行子类化,那么__new__不知道这一点的人可能会首先调整__init__并将调用转发到子类__init__。这种调用__init__后的约定__new__有助于按预期工作。

__init__仍然需要允许超类所需的任何参数,__new__但不这样做通常会产生明显的运行时错误。并且__new__应该明确允许*args和'**kw',以明确扩展是可以的。

__new__由于原始海报描述的行为,__init__在同一类中具有相同的继承级别通常是不好的形式。

于 2012-06-18T16:30:45.803 回答
2

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

主文件

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,它与其他类发生冲突 - 除非您在每个孩子中定义单独的缓存,否则这不是我们想要的。
  • 警告:似乎以父母为祖父母的班级表现得很奇怪。[未验证]

在线尝试!

于 2018-07-25T19:21:33.323 回答
1

但是,我有点困惑为什么__init__总是在__new__.

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

我没想到会这样。谁能告诉我为什么会发生这种情况以及我如何实现此功能?(除了将实现放入__new__感觉很hacky的地方)。

如果它已经被初始化,你可以什么都不做,或者你可以用一个只调用新实例__init__的 new 编写一个新的元类,否则只返回.__call____init____new__(...)

于 2009-03-23T17:23:37.900 回答
1

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

于 2018-03-23T13:48:43.893 回答