假设您有一个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实例化包含几个阶段。 理解每个步骤使我们更加了解整个语言。 Foo是一个类,但是在Python中,类也是对象! 类,函数,方法和实例都是对象,只要在其名称后加上方括号,就调用它们的__call__方法。 因此Foo(1,y = 2)等于Foo .__调用__(1,y = 2) 。 此外, __call__方法是在Foo对象的类中声明的。 Foo对象的类是什么?
>>> Foo.__class__ <class 'type'>
因此, Foo类是类型类的实例,并且调用最后一个的__call__方法将返回Foo类。 现在让我们看看类型类的__call__方法是什么。 以下是其在CPython和PyPy中的C实现。 如果您厌倦了观看它们,请进一步滚动以找到简化版本:
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; if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 && (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0))) return obj; 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; }
y
链接到源 。
def descr_call(self, space, __args__): promote(self)
如果您忘记了所有类型的错误检查,则上面的代码大约与此等效:
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对象是类型类的实例,因此调用Foo .__ call __(* args,** kwargs)等同于类型.__ call __(Foo,* args,** kwargs) 。
- 类型.__调用__(Foo,* args,** kwargs) 类型将调用类型.__ new __(Foo,* args,** kwargs)方法,该方法返回obj 。
- 通过调用obj .__ init __(* args,** kwargs) 来初始化obj 。
- 整个过程的结果是一个初始化的obj 。
客制化
现在,我们将注意力转向__new__ 。 此方法为对象分配内存并返回它。 您可以通过许多不同方式自由定制此过程。 应该注意的是,尽管__new__是静态方法,但是您无需使用@staticmethod声明它:解释器将__new__视为特例。
__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
请注意,每次调用Singleton()时都会调用__init__ ,因此应格外小心。
__new__覆盖的另一个示例是Borg模式(“ Borg”)的实现 :
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__是最常遭受虐待的受害者之一。 通常,通过其他方法可以更好地实现通过重写此方法可以完成的操作。 但是,在确实需要时, __ new__是一个非常有用且功能强大的工具。
-Arion Sprag, Python中被遗忘的古老
在Python中很少有人遇到问题,最好的解决方案是使用__new__ 。 但是,当您拿着锤子时,每个问题都开始看起来像钉子,因此始终喜欢使用功能最强大的工具来使用最合适的工具 。