Python实例化

假设您有一个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; /* 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; } 

y


链接到源


 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__对其进行初始化。


总结一下:


  1. Foo(* args,** kwargs)等效于Foo .__ call __(* args,** kwargs)
  2. 由于Foo对象是类型类的实例,因此调用Foo .__ call __(* args,** kwargs)等同于类型.__ call __(Foo,* args,** kwargs)
  3. 类型.__调用__(Foo,* args,** kwargs) 类型将调用类型.__ new __(Foo,* args,** kwargs)方法,该方法返回obj
  4. 通过调用obj .__ init __(* args,** kwargs) 初始化obj
  5. 整个过程的结果是一个初始化的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__ 。 但是,当您拿着锤子时,每个问题都开始看起来像钉子,因此始终喜欢使用功能最强大的工具来使用最合适的工具

Source: https://habr.com/ru/post/zh-CN480022/


All Articles