假设我们有一个类 Foo
:
class Foo(object):
def __init__(self, x, y=0):
self.x = x
self.y = y
如果你实例化它(创建这个类的一个实例),底层到底发生了什么?
f = Foo(1, y=2)
当我们调用 Foo
的时候,到底什么函数或方法被调用了呢?大多数初学者,甚至许多有经验的Python 程序员会立刻回答 __init__
方法被调用了。但是,如果你停下来细细想一想,这远不是一个正确的答案。
__init__
方法本身不返回对象,但是调用 Foo(1, y=2)
却返回了对象。另外,__init__
是期望有一个 self
参数的,但在调用 Foo(1, y=2)
的时候却不存在这样的参数。这里的工作有一点点复杂,因此在这篇文章中,我们将一起探究一下当你在 Python 中实例一个类时会发生什么。
创建顺序
实例化一个对象在 Python 中包括几个阶段,但 Python 美妙之处在于它们始终保持自身的 Pythonic - 理解这些步骤能够加深我们对 Python 的了解。Foo
是一个类,但在 Python 中,类也是对象!类,函数,方法和实例的这些都是对象,当你在他们的名字后面加上括号的时候,其实你是调用了它们的方法 __call__
。所以 Foo(1, y=2)
等价于 Foo.__call__(1, y=2)
,__call__
方法是类 Foo
定义的,那么类 Foo
又是属于谁的?
>>> Foo.__class__
<class 'type'>
通过上面这段示例,我们可以发现 Foo
是 类型 type
的对象,然后调用 __call__
方法会返回类 Foo
的对象。接下来,我们会看一下 type
中的方法 __call__
是怎样的,这个方法很复杂,但是我们会尽可能简化它,下面,我们会贴出 CPython 中的 C代码以及 Pypy 中的 Python 实现,我发现阅读 Python 的源代码非常有趣,当你阅读源代码的时候,不要怕,像我下面一样简化它:
Cpython:
static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *obj;
if (type->tp_new == NULL) {
PyErr_Format(PyExc_TypeError,
"cannot create '%.100s' instances",
type->tp_name);
return NULL;
}
obj = type->tp_new(type, args, kwds);
obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL);
if (obj == NULL)
return NULL;
/* Ugly exception: when the call was type(something),
don't call tp_init on the result. */
if (type == &PyType_Type &&
PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 &&
(kwds == NULL ||
(PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
/* If the returned object is not an instance of type,
it won't be initialized. */
if (!PyType_IsSubtype(Py_TYPE(obj), type))
return obj;
type = Py_TYPE(obj);
if (type->tp_init != NULL) {
int res = type->tp_init(obj, args, kwds);
if (res < 0) {
assert(PyErr_Occurred());
Py_DECREF(obj);
obj = NULL;
}
else {
assert(!PyErr_Occurred());
}
}
return obj;
}
Pypy
def descr_call(self, space, __args__):
promote(self)
# invoke the __new__ of the type
if not we_are_jitted():
# note that the annotator will figure out that self.w_new_function
# can only be None if the newshortcut config option is not set
w_newfunc = self.w_new_function
else:
# for the JIT it is better to take the slow path because normal lookup
# is nicely optimized, but the self.w_new_function attribute is not
# known to the JIT
w_newfunc = None
if w_newfunc is None:
w_newtype, w_newdescr = self.lookup_where('__new__')
if w_newdescr is None: # see test_crash_mro_without_object_1
raise oefmt(space.w_TypeError, "cannot create '%N' instances",
self)
w_newfunc = space.get(w_newdescr, self)
if (space.config.objspace.std.newshortcut and
not we_are_jitted() and
isinstance(w_newtype, W_TypeObject)):
self.w_new_function = w_newfunc
w_newobject = space.call_obj_args(w_newfunc, self, __args__)
call_init = space.isinstance_w(w_newobject, self)
# maybe invoke the __init__ of the type
if (call_init and not (space.is_w(self, space.w_type) and
not __args__.keywords and len(__args__.arguments_w) == 1)):
w_descr = space.lookup(w_newobject, '__init__')
if w_descr is not None: # see test_crash_mro_without_object_2
w_result = space.get_and_call_args(w_descr, w_newobject,
__args__)
if not space.is_w(w_result, space.w_None):
raise oefmt(space.w_TypeError,
"__init__() should return None")
return w_newobject
忽略掉错误检查,然后实例化一个类的代码等价于:
def __call__(obj_type, *args, **kwargs):
obj = obj_type.__new__(*args, **kwargs)
if obj is not None and issubclass(obj, obj_type):
obj.__init__(*args, **kwargs)
return obj
__new__
为对象分配内存,然后将对象构造为一个空对象,然后再调用 __init__
方法初始化它。
总结:
Foo(*args, **kwargs)
等价于Foo.__call__(*args, **kwargs)
- 因为
Foo
是type
的对象,所以Foo.__call__(*args, **kwargs)
调用的是type.__call__(Foo, *args, **kwargs)
type.__call__(Foo, *args, **kwargs)
调用的是type.__new__(Foo, *args, **kwargs)
,然后返回了 obj- obj 被
obj.__init__(*args, **kwargs)
初始化 - 最后 obj 就被返回了
自定义
现在,我们把注意力放到到 __new__
方法。事实上,它是实际创建对象的方法。因为 __new__
方法比较复杂,我们就不详细的阅读具体实现代码了,但是,我们知道一个事实即可,那就是:它为对象分配空间并返回它。有趣的是,但你知道它做了什么之后,你可以用它来定制一些有趣的方式来创建实例。值得注意的是,虽然 __new__
是一个静态方法,但是你不需要用 @staticmethod
来声明,因为它是 Python解释器 的特例。
一个利用 __new__
方法的很强大的示例就是实现 Singleton 类:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
然后尝试一下:
>>> s1 = Singleton()
... s2 = Singleton()
... s1 is s2
True
需要注意的是,在这个单例实现中,__init__
方法在每次调用 Singleton()
的时候都会被调用的。
另一个类似的例子就是实现 Borg design pattern:
class Borg(object):
_dict = None
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls, *args, **kwargs)
if cls._dict is None:
cls._dict = obj.__dict__
else:
obj.__dict__ = cls._dict
return obj
使用示例:
Then:
>>> b1 = Borg()
... b2 = Borg()
... b1 is b2
False
>>> b1.x = 8
... b2.x
8
一个非常重要的提示:这些例子只是说明一下 __new__
的强大威力,但是只是说我们可以用它,并不意味着我们应该用他:
__new__
is one of the most easily abused features in Python. It’s obscure, riddled with pitfalls, and almost every use case I’ve found for it has been better served by another of Python’s many tools. However, when you do need__new__
, it’s incredibly powerful and invaluable to understand.– Arion Sprague, Python’s Hidden New
在 Python 中基本上不会有说使用 __new__
是最合适的解决办法。我们最常见的误区就是,当你手上有把锤子的时候,你看什么都像钉子,但你知道 __new__
这个方法后,你可能会觉得很多问题都可以通过 __new__
来解决。这个时候,你需要谨记前辈们的经验:更好的设计胜过强大的新工具,__new__
方法并不永远是最好的。