为什么一个类中同时定义'__new__'和'__init__

11 投票
4 回答
2542 浏览
提问于 2025-04-15 17:43

我觉得你可以在一个类里定义'__init__'或者'__new__',但是为什么这些都在django.utils.datastructures.py这个文件里定义呢?

我的代码:

class a(object):
    def __init__(self):
        print  'aaa'
    def __new__(self):
        print 'sss'

a()#print 'sss'

class b:
    def __init__(self):
        print  'aaa'
    def __new__(self):
        print 'sss'
b()#print 'aaa'

datastructures.py文件:

class SortedDict(dict):
    """
    A dictionary that keeps its keys in the order in which they're inserted.
    """
    def __new__(cls, *args, **kwargs):
        instance = super(SortedDict, cls).__new__(cls, *args, **kwargs)
        instance.keyOrder = []
        return instance

    def __init__(self, data=None):
        if data is None:
            data = {}
        super(SortedDict, self).__init__(data)
        if isinstance(data, dict):
            self.keyOrder = data.keys()
        else:
            self.keyOrder = []
            for key, value in data:
                if key not in self.keyOrder:
                    self.keyOrder.append(key)

还有在什么情况下会调用到SortedDict.__init__这个方法呢?

谢谢!

4 个回答

2

我猜在这种情况下,这个类的作者希望在调用 SortedDict.__init__ 之前,keyOrder 列表就已经存在于这个类中了。

需要注意的是,SortedDict 在它的 __init__ 方法中调用了 super(),这通常会去调用 dict.__init__,而这个方法可能会调用 __setitem__ 等方法来开始添加项目。SortedDict.__setitem__ 期望 .keyOrder 属性已经存在,这就是问题所在(因为 .keyOrder 通常是在调用 super() 之后才创建的)。这可能只是一个关于如何继承 dict 的问题,因为我通常的直觉是应该在调用 super() 之前就初始化 .keyOrder

__new__ 中的代码也可能是为了让 SortedDict 能够在一个菱形继承结构中被继承,在这种情况下,可能在第一次调用 __setitem__ 等方法之前并没有调用 SortedDict.__init__。Django 需要处理支持从 2.3 版本开始的各种 Python 版本的问题;在某些版本中,这段代码可能完全不必要,而在其他版本中则是需要的。


定义 __new____init__ 的一个常见用途是:访问类属性,而不必使用 type(self)self.__class__(在存在元类的情况下,这可能甚至不是正确的做法)。

举个例子:

class MyClass(object):
    creation_counter = 0

    def __new__(cls, *args, **kwargs):
        cls.creation_counter += 1
        return super(MyClass, cls).__new__(cls)

    def __init__(self):
         print "I am the %dth myclass to be created!" % self.creation_counter

最后,__new__ 实际上可以返回一个包装器的实例,或者一个与你认为要实例化的类完全不同的类。这用于提供类似元类的特性,而实际上并不需要元类。

9

__new____init__ 的作用完全不同。__init__ 是用来初始化一个类的新实例的,也就是我们常说的构造函数。而 __new__ 则是一个更复杂的东西——它可以改变传入的参数,甚至可以改变创建出来的对象的类型。举个例子,下面的代码:

class Meters(object):
    def __new__(cls, value):
        return int(value / 3.28083)

如果你调用 Meters(6),你实际上并不是在创建一个 Meters 的实例,而是在创建一个 int 的实例。你可能会想,这有什么用呢?其实这对于元类来说是非常重要的,虽然这个概念比较晦涩,但它非常强大。

你会注意到,在 Python 2.x 中,只有继承自 object 的类才能使用 __new__,正如你上面的代码所示。

你在 Django 中展示的 __new__ 的用法似乎是为了保持 SortedDict 对象的方法解析顺序是合理的。不过,我得承认,有时候很难理解为什么需要使用 __new__。标准的 Python 风格建议,除非必要,否则不要使用它(总之,更好的类设计应该是你首先考虑的工具)。

22

你可以定义 __new____init__,或者只定义其中一个。

__new__ 这个方法必须返回一个对象——这个对象可以是一个新的(通常这个工作是交给 type.__new__ 来完成),也可以是一个已经存在的对象(比如为了实现单例模式,或者从对象池中“回收”实例等等),甚至可以是一个不是这个类的实例的对象。如果 __new__ 返回的是这个类的实例(无论是新的还是已有的),那么 __init__ 就会被调用;如果 __new__ 返回的是一个不是这个类的实例的对象,那么 __init__不会被调用。

__init__ 方法的第一个参数是类的实例(状态和 __new__ 返回时一样,通常是“空的”),它必须根据需要对这个实例进行修改,以便让它可以使用(通常是通过添加属性来实现)。

一般来说,最好把所有可以做的事情都放在 __init__ 里——而 __new__ 则用来处理那些 __init__ 不能做的“额外事情”。

所以如果你在 __init__ 里有一些有用的操作,但又不是所有你想在类实例化时发生的事情,你通常会同时定义这两个方法。

举个例子,假设有一个类继承自 int,但它还有一个 foo 的属性——你希望在实例化时同时为 int.foo 提供初始化值。由于 int 是不可变的,这部分必须在 __new__ 中处理,所以严格来说可以这样写:

>>> class x(int):
...   def __new__(cls, i, foo):
...     self = int.__new__(cls, i)
...     return self
...   def __init__(self, i, foo):
...     self.foo = foo
...   __slots__ = 'foo',
... 
>>> a = x(23, 'bah')
>>> print a
23
>>> print a.foo
bah
>>> 

实际上,对于这么简单的情况,如果你省略 __init__,直接把 self.foo = foo 移到 __new__ 中,大家也不会介意。但如果初始化的内容复杂到最好放在 __init__ 中,这个想法就值得记住了。

撰写回答