python类的理解_Python类实例理解

本文详细介绍了Python中类实例化的过程,特别是__new__和__init__方法的调用顺序和作用。__new__方法负责分配内存并构建对象,而__init__用于初始化对象的属性。文章还提到了如何通过修改__new__实现单例模式和Borg模式,并强调了正确理解并谨慎使用__new__的重要性。
摘要由CSDN通过智能技术生成

从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__

所以说,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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值