为什么一个类中同时定义'__new__'和'__init__
我觉得你可以在一个类里定义'__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 个回答
我猜在这种情况下,这个类的作者希望在调用 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__
实际上可以返回一个包装器的实例,或者一个与你认为要实例化的类完全不同的类。这用于提供类似元类的特性,而实际上并不需要元类。
__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 风格建议,除非必要,否则不要使用它(总之,更好的类设计应该是你首先考虑的工具)。
你可以定义 __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__
中,这个想法就值得记住了。