从Foo
这个类开始说起:
class Foo(object):
def __init__(self, x, y=0):
self.x = x
self.y = y
当实例化这个类时候发生了什么?
f = Foo(1, y=2)
实例化的时候Foo
被调用了,在这里面到底有什么方法被依次调用了呢?__init__
肯定是被调用了。但是__init__
并不会返回一个对象,而且__init__
需要一个self
参数。调用Foo(1, y=2)
的时候也并没有这样给一个参数。所以另有玄机藏于内。
调用的序列
在Python中实例化一个对象要经过几个步骤。Foo
是一个类,但是类在Python中也是一个对象,类/方法/实例在Python中都是对象。不管任何时候你将参数通过括号的形式包括起来放在对象后面,都调用了一个__call__
方法。所以Foo(1, y=2)
相当于Foo.__call__(1, y=2)
。 这个__call__
是由Foo
的class定义出来。那什么是Foo
的类呢?
>>> Foo.__class__
<class 'type'>
所以说,Foo
是类type
的对象 调用__call__
返回一个Foo类的实例。有点绕口( :。接下来让我们看看type
的__call__
方法到底长什么样子?这个方法有点复杂,我们试着简化一些。 下面我们从CPython和PyPy的实现来解读一下。
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__
函数,从而对对象的创建过程做一些有意思的控制。比较具有代表性的是通过修改__new__
函数实现单例类:
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
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
然后
>>> 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