社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 中 staticmethod 和 classmethod 原理探究

Python中文社区 • 3 年前 • 385 次点击  


起步

文章Python 中 property 的实现原理及实现中探究了 property 的实现原理。如果能理解那边描述符的使用方式,那也能很快理解本篇中的 staticmethodclassmethod

函数与方法

对于类中定义的方法来说,通过类来调用与实例调用是不一样的:

class C:
    def f(self): pass

print(C.f)    
print(C().f)  

一个返回的是 function 类型,一个返回的是 method 类型。他们的主要区别在于,函数的 传参都是显式传递的 而方法则方法中 传参往往都会有隐式传递的,具体根据于调用方。例如示例中的 C().f 通过实例调用的方式会隐式传递 self数据。

staticmethod 的实现

staticmethod 的效果是让 C.fc.f 都返回函数,等价于 object.__getattribute__(c, "f")object.__getattribute__(C, "f"),运行代码如下:

class C:
    @staticmethod
    def sf(): pass

c = C()
print(C.sf)         
print(c.sf)         
print(C.sf is c.sf) # True

要实现这样的方式也可以依托于描述符的机制,在 __get__ 中返回原始的函数,因此它的 Python 实现版本异常的简单:

class staticmethod(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f

这么简单的代码也已经是 C 实现版本对应的Python完整代码了。

classmethod 的实现

classmethod 则是要让 C.fc.f 都返回方法,并且传递隐式参数 cls , 运行代码如下:

class C:
    @classmethod
    def cf(cls): pass

c = C()
print(C.cf)         
print(c.cf)         
print(C.cf is c.cf) # False

classmethod 不仅要隐式传递参数,还需要每次创建新的 对象。因此它的实现上需要用闭包,将闭包函数作为返回值以便得到新的对象:

class classmethod(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, klass=None):
        if klass is None:
            klass = type(obj)
        def newfunc(*args):
            return self.f(klass, *args)
        return newfunc

这里的技巧就在于闭包将隐式的 cls 通过闭包空间进行绑定。这个纯python实现版本在功能上没什么问题,仅有个小缺陷:

c = C()
print(C.cf)         .newfunc at 0x000001EDF2527EE0>
print(c.cf)         .newfunc at 0x000001EDF2527EE0>
print(C.cf is c.cf) # False

尽管我们用闭包绑定了个隐式参数,但通过 c.cf获取的依然是 function 对象。在Python代码中创建 实例的方式:

import types
class classmethod(object):
    def __init__(self, f):
        self.f = f
    def __get__(self, obj, klass=None):
        if klass is None:
            klass = type(obj)
        return types.MethodType(self.f, klass)

总结

staticmethod classmethod 都运用了描述符的机制,学习描述符不仅能提供接触到更多工具集的方法,还能更深地理解 Python 工作的原理并更加体会到其设计的优雅性。

作者:weapon,闲来笑浮生悬笔一卷入毫端,朱绂临身可与言者不过二三。

Blog:zhihu.com/people/hong-wei-peng


推荐阅读:
一文读懂高并发情况下的常见缓存问题
用 Django 开发基于以太坊智能合约的 DApp
一文读懂 Python 分布式任务队列 celery
5 分钟解读 Python 中的链式调用
用 Python 创建一个比特币价格预警应用


 点击阅读原文,即享阿里云产品 0.9折优惠起

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/70374
 
385 次点击