鱼C论坛

 找回密码
 立即注册
查看: 1145|回复: 0

[学习笔记] 类和对象-静态方法

[复制链接]
发表于 2023-4-2 19:00:33 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
>>> # 函数和方法如出一辙,实现对象绑定的函数叫方法,没有实现绑定的是普通的函数。
        本节基于对官方文档的解读,探究了函数、方法、静态方法、类方法的底层实现原理,“知其所以然”。
        Python底层通过描述符来辨别函数和方法,函数是一个非数据描述符,利用__get__()方法进行鉴别,instance参数为None的是函数,不为None的是方法。
        静态方法是StaticMethod类的对象,作为函数调用时直接调用传递进来的函数自身,故无需绑定类和对象;
        类方法是ClassMethod类的对象,将参数klass即owner调整为新函数的第一个参数,以此来绑定类。
        Python3.9还引入了串联装饰器的功能,仅通过访问属性便可得到与访问方法相同的结果,由新增的hasattr()条件分支来实现。

>>> # 函数和方法
>>> class C:
...         def func(self, x):
...                 return x
...
>>> c = C()
>>> print(c.func)
<bound method C.func of <__main__.C object at 0x01B6E040>>
>>> print(C.func)
<function C.func at 0x01B6A340>

>>> # 函数和方法本质上是一个东西,python在底层分辨函数和方法的原理 -> 描述符
>>> # 函数和方法本质上是一个东西,python在底层分辨函数和方法的原理 -> 描述符

# 官方文档:
# class Function:
#     ...
#     def __get__(self, obj, objtype=None):
#         '''Simulate func_descr_get() in Objects/funcobject.c'''
#         if obj is None:
#             return self
#         return MethodType(self, obj)
# 当定义函数时,其实是实例化了一个叫做Function的类(底层为C语言形式实现),包含一个__get__()方法。
# 当数定义一个类,再在类里边定义一个函数的时候,就会用到__get__()方法(obj对应instance,objtype对应owner)
# 先判断第二个参数obj是否为None,是则返回自己,否则返回MethodType
# 官方代码相当于:
>>> class D:
...         def __get__(self, instance, owner):
...                 if instance is None:
...                         print('函数')
...                 else:
...                         print('方法')
...
>>> class C:
...         x = D()
...
>>> c = C()
>>> c.x
方法
>>> c.x
方法
>>> C.x
函数

>>> # MethodType其实也是一个类,其官方文档:
# class MethodType:
#     '''Emulate PyMethod_Type in Objects/classobject.c'''
#     def __init__(self, func, obj):
#         self.__func__ = func
#         self.__self__ = obj
#     def __call__(self, *args, **kwargs):
#         func = self.__func__
#         obj = self.__self__
#         return func(obj, *args, **kwargs)
# 当它的实例化对象被当做函数调用的时候,__call__()魔法方法就会被执行,将传递进去的func和obj两个参数进行整合并且返回,即将传递进来的第一个实参func作为函数名,第二个实参作obj为该函数的第一个参数,整合之后返回
# 那么上边return MethodType其实就相当于return self(obj)。

# 静态方法,官方文档:
# class StaticMethod:
#     '''Emulate PyStaticMethod_Type() in Objects/funcobject.c'''
#     def __init__(self, f):
#         self.f = f
#     def __get__(self, obj, objtype=None):
#         return self.f
#     def __call__(self, *args, **kwargs):
#         return self.f(*args, **kwargs)
# 只要被StaticMethod装饰器碰过的对象就会变成StaticMethod类的对象,当这个对象被作为函数调用的时候,会直接调用传递进来的函数自身,省去类和对象的中间环节

# 类方法,官方文档:
# class ClassMethod:
#     '''Emulate PyClassMethod_Type() in Objects/funcobject.c'''
#     def __init__(self, f):
#         self.f = f
#     def __get__(self, obj, cls=None):
#         if cls is None:
#             cls = type(obj)
#         if hasattr(type(self.f), '__get__'):
#             return self.f.__get__(cls, cls)
#         return MethodType(self.f, cls)
# type()如果传入一个对象,那么他会返回该对象所属的类
>>> class C:
...         pass
...
>>> c = C()
>>> print(type(c))
<class '__main__.C'>
>>> print(type(c) is C)
True

>>> # 代码hasattr(type(self.f), '__get__')为的是让类方法能够和其他装饰器串联起来使用,如;
>>> class C:
...         @classmethod
...         def __doc__(cls):
...                 return f'i love you -- from class {cls.__name__}'
...
>>> c = C()
>>> print(c.__doc__())
i love you -- from class C
>>> print(C.__doc__())
i love you -- from class C
>>> # 调用方法的形式
>>> class G:
...         @classmethod
...         @property
...         def __doc__(cls):
...                 return f'A doc for {cls.__name__!r}'
...
>>> g = G()
>>> print(g.__doc__)
A doc for 'G'
>>> print(G.__doc__)
A doc for 'G'
>>> # 属性访问的形式
>>> #将官方文档复制下来,并添加一些print语句
class MethodType:
    def __init__(self, func, obj):
        self.__func__ = func
        self.__self__ = obj
    def __call__(self, *args, **kwargs):
        func = self.__func__
        obj = self.__self__
        print('小白')
        return func(obj, *args, **kwargs)

class ClassMethod:
    def __init__(self, f):
        self.f = f
    def __get__(self, obj, cls=None):
        if cls is None:
            print('旺财')
            cls = type(obj)
        if hasattr(type(self.f), '__get__'):
            print(f'来福,type(self.f) -> {type(self.f)}')
            return self.f.__get__(cls, cls)
        return MethodType(self.f, cls)

class D:
    @ClassMethod# 注意不是classmethod
    @property
    def __doc__(cls):
        return  f'i love you. -- from class {cls.__name__}'
>>> class D:
...         @ClassMethod
...         @property
...         def __doc__(cls):
...                 return f'i love you. -- from class {cls.__name__}'
...
>>> d = D()
>>> d.__doc__
来福,type(self.f) -> <class 'property'>
'i love you. -- from class D'

>>> # 如果多个装饰器串联,那么传入这个参数的类type(self.f)就是property,相当于
        # __doc__是property的实例对象__doc__ = property(__doc__),然后到@ClassMethod,运行到hasattr分支,type(self.f)运行的结果是property类,包含'__get__'方法,然后if里对__get__进行改造

# self.f.__get__(cls, cls)调用的是propert类里的__get__方法,并将instance对象参数的位置替换成了cls类参数
>>> class D:
...         @ClassMethod
...         def __doc__(cls):
...                 return f' i love you. -- from class {cls.__name__}'
...
>>> d = D()
>>> d.__doc__
来福,type(self.f) -> <class 'function'>
<bound method D.__doc__ of <class '__main__.D'>>
>>> # python万物皆对象,函数也是对象,这里type(self.f)指的是<class 'function'>类
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-24 02:14

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表