【Python笔记】装饰器语法糖(@staticmethod/@classmethod/@property)原理剖析及使用场景说明

本文详细解析Python中@staticmethod、@classmethod和@property三个装饰器的原理和使用场景。@staticmethod用于创建不需要类实例的类方法,@classmethod常用于类的工厂方法和预处理逻辑。@property则用于创建只读属性,控制对象属性的读取、设置和删除。文章结合官方文档和实例进行了深入探讨。
摘要由CSDN通过智能技术生成

在阅读一些开源Python库的源码时,经常会看到在某个类的成员函数前,有类似于@staticmethod或@classmethod或@property的语法糖。本质上,它们都是函数装饰器,只不过通常被用来修饰类成员函数而已。

本笔记旨在说明这些语法糖的用途,关于普通函数装饰器语法的解释,可以参考这篇笔记

在解释这些装饰器函数前,先来分析下普通成员函数。

1. 类的普通成员函数
对于Python的类,其普通类成员函数的第一个参数默认为当前的类实例,通常的定义形式示例:

class C:
   def __init__(self):  ## NOTICE: 不传入参数时创建实例会报错;"self"只是约定俗成的参数名而已,非语法的硬性规定
       pass
我们可以通过print C.__init__查看函数__init__():
>>> class C():
...     def __init__(self):
...         pass
... 
>>> print C.__init__
<unbound method C.__init__>  ## __init__()是类C的unbound method,即它此时未bound到任何类实例上
>>> c = C()
>>> print c.__init__
<bound method C.__init__ of <__main__.C instance at 0x7fb4346d38c0>>  ## 当类实例创建后,__init__()变成实例的bound method
>>> print C.__init__.__get__ 
<method-wrapper '__get__' of instancemethod object at 0x7fb434772c80> ## 类成员函数默认实现了__get__()方法
总之,对于类的普通成员函数来说,在创建类的具体实例前,它们是unbound method,通过类名调用时(如 C.fn()),会报错;类实例创建 后,它们都是实例的bound method,只能通过实例来调用(inst = C(); inst.fn())。
关于unbound method函数调用报错的原因,与Python底层实现时对函数名的查找规则有关。从上面示例代码可看到,__init__()函数默 认实现了__get__()方法,而根据Python底层规则,当某个对象(Python中万物皆对象)实现了_get__()/__set__()/__delete__()这3个 method中任何一个时,它就成了一个支持descriptor protocal的descriptor。当调用c.__init__时(当然,这个是Python解释器帮我们调 用的,但这并不改变__init__实际上是个普通类函数的事实),根据descriptor invoking规则,其将被转化为type(c).__dict__ ['__init__'].__get__(c, type(c))的形式,可见,实际上发生的调用类似于C.__init__(c),即类C的实例c会被当作第1个参数传给普通类 成员函数。所以,在定义类普通成员函数时,至少需要有self参数,且类的普通成员函数必须通过类实例来调用,而不能通过类名直接调用。
关于上面提到的Descriptor Protocol及其对obj attribute查找规则的影响,强烈建议读懂这篇文档Descriptor HowTo Guide 。不夸张的说,理解Descriptor对我们理解Python代码的底层行为有巨大帮助。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值