描述器(Descriptor)

描述器(又称描述符)(Descriptor)

描述器:如果一个类中实现了__get__, __set__, __delete__三个方法中的任何一个,那么这样的类的实例就称为描述器。 当某一个类的类属性是一个描述器的时候,通过这个类或者类的实例来访问、修改或删除这个类属性时,就会分别触发描述器的__get__, __set__和__delete__方法。

class Descriptor():
    def __get__(self, instance, owner):
        return {'instance': instance, 'owner': owner, 'value': 12345}

glb_descriptor = Descriptor()  # 在模块全局声明一个描述器

class A:
    prop = glb_descriptor

class B:
    def __init__(self):
        self.prop = glb_descriptor  # 由于描述器不是B类的类属性,所以不会起作用。

class C:
    cls_descriptor = Descriptor()  # 在类定义中声明一个描述器
    def __init__(self):
        # 如下三种方式都能将描述器的__get__返回内容添加到实例的__dict__属性中,但内容略有不同,主要区别在于instance不同。
        self.prop = self.cls_descriptor  # c.prop: {'instance': <__main__.C object at 0x00000235799B6210>, 'owner': <class '__main__.C'>, 'value': 12345}
        # self.prop = C.cls_descriptor   # c.prop: {'instance': None, 'owner': <class '__main__.C'>, 'value': 12345}
        # self.prop = type(self).cls_descriptor  # c.prop: {'instance': None, 'owner': <class '__main__.C'>, 'value': 12345}

a = A()
b = B()
c = C()
print('a.prop:', a.prop)
print('b.prop:', b.prop)
print('c.prop:', c.prop)

print('-' * 25, '分隔线', '-' * 25)

print(A.prop)
print(C.cls_descriptor)

print('-' * 25, '分隔线', '-' * 25)

print(a.__dict__)  # a实例的__dict__没有内容
print(c.__dict__)  # c实例的__dict__中存储了描述器__get__方法的返回值。因为在c的实例化过程中,`__init__`方法里就已经通过"实例.属性"(self.cls_descriptor)的方式触发了描述器的`__get__`方法。

输出结果:

a.prop: {'instance': <__main__.A object at 0x0000017589326190>, 'owner': <class '__main__.A'>, 'value': 12345}
b.prop: <__main__.Descriptor object at 0x00000175893256D0>
c.prop: {'instance': <__main__.C object at 0x0000017589326210>, 'owner': <class '__main__.C'>, 'value': 12345}
------------------------- 分隔线 -------------------------
{'instance': None, 'owner': <class '__main__.A'>, 'value': 12345}
{'instance': None, 'owner': <class '__main__.C'>, 'value': 12345}
------------------------- 分隔线 -------------------------
{}
{'prop': {'instance': <__main__.C object at 0x0000017589326210>, 'owner': <class '__main__.C'>, 'value': 12345}}

非数据描述器: 只实现了__get__方法,未实现__set__和__delete__方法中的任何一个。这种情况下,实例优先访问自身的属性,如果不存在,再找描述器。

class Descriptor():
    def __get__(self, instance, owner) :
        return {'instance': instance, 'owner': owner, 'value': 12345}

class C:
    cls_descriptor = Descriptor()  # 在类定义中声明一个描述器
    def __init__(self, prop):
        self.cls_descriptor = prop  # cls_descriptor是非数据描述器,prop值会保存在实例自己的同名属性中。


c = C('88888')

print('c.cls_descriptor:', c.cls_descriptor)

print('-' * 25, '分隔线', '-' * 25)


print(C.cls_descriptor)

print('-' * 25, '分隔线', '-' * 25)
print(c.cls_descriptor)
print(c.__dict__)  # 由于是非数据描述器,给实例中与类属性描述器同名的属性赋值不会触发描述器,而是直接保存在实例属性中。

输出结果:

c.cls_descriptor: 88888
------------------------- 分隔线 -------------------------
{'instance': None, 'owner': <class '__main__.C'>, 'value': 12345}
------------------------- 分隔线 -------------------------
88888
{'cls_descriptor': '88888'}

数据描述器: 描述器实现了__set__或__delete__方法,此时通过实例访问属性时,优先触发描述器,而不是优先访问实例属性。

class Descriptor():
    def __get__(self, instance, owner) :
        return {'instance': instance, 'owner': owner, 'value': 12345}

    def __set__(self, instance, value):
        instance.papapa = value

class C:
    cls_descriptor = Descriptor()  # 在类定义中声明一个描述器
    def __init__(self, prop):
        self.cls_descriptor = prop  # 类属性cls_descriptor是描述器,通过实例访问这个属性会触发描述器的__set__方法,而不会在实例中存储一个同名属性。


c = C('88888')

print('c.cls_descriptor:', c.cls_descriptor)

print('-' * 25, '分隔线', '-' * 25)


print(C.cls_descriptor)

print('-' * 25, '分隔线', '-' * 25)
print(c.cls_descriptor)
print(c.__dict__)  # 由于cls_descriptor是数据描述器,所以实例在初始化时`self.cls_descriptor = prop`实际上会触发描述器的`__set__`方法,这样,实例自身的`__dict__`中并没有成功填加与描述器同名的属性。。

输出结果:

c.cls_descriptor: {'instance': <__main__.C object at 0x000001BBE5075B90>, 'owner': <class '__main__.C'>, 'value': 12345}
------------------------- 分隔线 -------------------------
{'instance': None, 'owner': <class '__main__.C'>, 'value': 12345}
------------------------- 分隔线 -------------------------
{'instance': <__main__.C object at 0x000001BBE5075B90>, 'owner': <class '__main__.C'>, 'value': 12345}
{'papapa': '88888'}

下面为了说明属性查找的优先级,我直接通过实例的__dict__字典来添加实例属性,但在通过实例.属性的方式来查找属性时,仍然会显示调用描述器的结果。
属性查找顺序: 数据描述器(通过mro查找,即包含父类) > 实例属性 > 非数据描述器(通过mro查找,即包含父类) > 普通类属性(通过mro查找,即包含父类)

class Descriptor():
    def __get__(self, instance, owner) :
        return {'instance': instance, 'owner': owner, 'value': 12345}

    def __set__(self, instance, value):
        instance.papapa = value

class C:
    cls_descriptor = Descriptor()  # 在类定义中声明一个描述器
    def __init__(self, prop):
        self.cls_descriptor = prop  # 类属性cls_descriptor是描述器,通过实例访问这个属性会触发描述器的__set__方法,而不会在实例中存储一个同名属性。


c = C('88888')
c.__dict__['cls_descriptor'] = '这是实例自己的属性' # 为了不触发描述器的__set__方法,直接通过实例的__dict__字典为其添加实例属性。
print('c.cls_descriptor:', c.cls_descriptor) # 这里仍然触发了描述器方法,而不是显示实例自身的属性的值
print('-' * 25, '分隔线', '-' * 25)
print(C.cls_descriptor)
print('-' * 25, '分隔线', '-' * 25)
print(c.cls_descriptor)
print(c.__dict__)  # 这里保存了实例初始化时触发描述器时生成的属性,以及直接通过__dict__字典添加的实例属性

输出结果:

c.cls_descriptor: {'instance': <__main__.C object at 0x000002189035BAD0>, 'owner': <class '__main__.C'>, 'value': 12345}
------------------------- 分隔线 -------------------------
{'instance': None, 'owner': <class '__main__.C'>, 'value': 12345}
------------------------- 分隔线 -------------------------
{'instance': <__main__.C object at 0x000002189035BAD0>, 'owner': <class '__main__.C'>, 'value': 12345}
{'papapa': '88888', 'cls_descriptor': '这是实例自己的属性'}

__getattribute__魔术方法的等价实现:

__getattribute__方法是用c实现的,如非必需要,尽量不要覆写此方法。此方法的内部实现也体现了python内部属性访问机制的复杂性。 但其内部本身不包含对 __getattr__ 方法的调用。即,只有当__getattribute__返回AttributeError时, __getattr__ 方法才会被触发。

如下为官方文档中使用python写的__getattribute__的内部机制的模拟实现。注意,这里只针对通过实例对象访问属性的情况,通过类对象及super()来访问的情况不在此代码中实现。

def find_name_in_mro(cls, name, default):
    "Emulate _PyType_Lookup() in Objects/typeobject.c"
    for base in cls.__mro__:
        if name in vars(base):
            return vars(base)[name]
    return default

def object_getattribute(obj, name):
    "Emulate PyObject_GenericGetAttr() in Objects/object.c"
    null = object()
    objtype = type(obj)
    cls_var = find_name_in_mro(objtype, name, null)
    descr_get = getattr(type(cls_var), '__get__', null)
    if descr_get is not null:
        if (hasattr(type(cls_var), '__set__')
            or hasattr(type(cls_var), '__delete__')):
            return descr_get(cls_var, obj, objtype)     # data descriptor
    if hasattr(obj, '__dict__') and name in vars(obj):
        return vars(obj)[name]                          # instance variable
    if descr_get is not null:
        return descr_get(cls_var, obj, objtype)         # non-data descriptor
    if cls_var is not null:
        return cls_var                                  # class variable
    raise AttributeError(name)

下面是我写的一个类似的等价实现,当然没有官方写的好。

def find_in_mro(cls_obj, name):
    for cls in cls_obj.mro():
        if name in vars(cls):
            return cls, vars(cls)[name]
    else:
        return None, None


def object_getattribute(obj, name):
    cls_obj = type(obj)
    cls_prop, prop_obj = find_in_mro(cls_obj, name) # 查找类属性

    if prop_obj:  # 存在同名类属性
        _, descriptior_get = find_in_mro(type(prop_obj), '__get__')
        _, descriptior_set = find_in_mro(type(prop_obj), '__set__')
        _, descriptior_del = find_in_mro(type(prop_obj), '__delete__')
        if descriptior_get: # 类属性是描述器
            if descriptior_set or descriptior_del: # 类属性是数据描述器
                return descriptior_get(prop_obj, obj, cls_obj)
            else: # 类属性是 非数据描述器
                if name in vars(obj):  # 存在同名实例属性
                    return vars(obj)[name]
                else: # 不存在同名实例属性
                    return descriptior_get(prop_obj, obj, cls_obj)
        else:
            if name in vars(obj):  # 存在同名实例属性
                return vars(obj)[name]
            else:
                return prop_obj
    else: # 不存在同名类属性
        if name in vars(obj):  # 存在同名实例属性
            return vars(obj)[name]
        else: # 类和实例中都未找到该属性
            raise AttributeError(name)

下面是对自己写的方法的一个测试:

class Descriptor:
    def __get__(self, instance, owner):
        print('描述器 __get__ 被调用')
        return '描述器get返回值'

    def __set__(self, instance, value):
        print('描述器 __set__ 被调用')
        pass  # 让其成为 数据描述器

class A:
    cls_prop = Descriptor()
    pass

class B(A):
    b_cls_prop = 'value of b_cls_prop'
    pass

输出结果: 如果注释掉描述器的__set__方法,结果将会不同。

描述器 __get__ 被调用
描述器get返回值
{}
------------------------- 分隔线 -------------------------
描述器 __set__ 被调用
描述器 __get__ 被调用
描述器get返回值
{}
------------------------- 分隔线 -------------------------
描述器 __get__ 被调用
描述器get返回值
value of b_cls_prop

描述器实现ORM

我们常用的ORM模块,比如sqlalchemy,也使用了描述器来实现。 下面是我写的一个模拟实现代码:

import sqlite3

con = sqlite3.connect('example.db')
cur = con.cursor()

# Create table
# cur.execute('''CREATE TABLE scores
#                (study_no number, name text, english number, math number)''')
#
# cur.execute("INSERT INTO scores VALUES ('002','Harry',90,95)")
# cur.execute("INSERT INTO scores VALUES ('002','Harry',85,95)")
# cur.execute("INSERT INTO scores VALUES ('003','Cong',95,85)")
# con.commit()

# for row in cur.execute('SELECT * FROM scores ORDER BY study_no'):
#     print(row)

# 定义描述器,意义为表的字段
class Field():
    def __set_name__(self, owner, name):
        self.col_name = name
        self.table = owner.table
    def __get__(self, instance, owner):
        query_statement = f'''select {self.col_name} from {self.table} where {owner.key} = \'{instance.key}\''''
        con.execute(query_statement)
        return cur.fetchone()[0]
    def __set__(self, instance, value):
        update_statement = f'''update {self.table} set {self.col_name} = ? where {type(instance).key} = ?'''
        con.execute(update_statement, [value, instance.key])
        con.commit()

# 数据模型 data model
class Score:
    table = 'scores'
    key = 'name'
    english = Field()
    math = Field()
    def __init__(self, key):
        self.key = key

print(Score('Roland').math)
Score('Roland').math = 100
print(Score('Roland').math)

输出结果:

95
100

当然,还是下面官方的例子更好:
定义描述器

class Field:

    def __set_name__(self, owner, name):
        self.fetch = f'SELECT {name} FROM {owner.table} WHERE {owner.key}=?;'
        self.store = f'UPDATE {owner.table} SET {name}=? WHERE {owner.key}=?;'

    def __get__(self, obj, objtype=None):
        return conn.execute(self.fetch, [obj.key]).fetchone()[0]

    def __set__(self, obj, value):
        conn.execute(self.store, [value, obj.key])
        conn.commit()

定义数据库表模型

class Movie:
    table = 'Movies'                    # Table name
    key = 'title'                       # Primary key
    director = Field()
    year = Field()

    def __init__(self, key):
        self.key = key

class Song:
    table = 'Music'
    key = 'title'
    artist = Field()
    year = Field()
    genre = Field()

    def __init__(self, key):
        self.key = key

使用这个模拟ORM

import sqlite3
conn = sqlite3.connect('entertainment.db')

>>> Movie('Star Wars').director
'George Lucas'
>>> jaws = Movie('Jaws')
>>> f'Released in {jaws.year} by {jaws.director}'
'Released in 1975 by Steven Spielberg'

>>> Song('Country Roads').artist
'John Denver'

>>> Movie('Star Wars').director = 'J.J. Abrams'
>>> Movie('Star Wars').director
'J.J. Abrams'

描述器实现property类装饰器

property是python提供的内置类(C语言实现的),可以用于装饰器,将类中的方法变成属性。 有点类似于C Sharp语言中,类的属性都默认有get和set方法,用于实现复杂的计算逻辑,而不是简单的修改和返回一个固定值。
如下是我对property类的模拟实现过程:

定义Property类做为描述器的类。

class Property:
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        self.doc = doc

    def __get__(self, instance, owner):
        return self.fget(instance)

    def __set__(self, instance, value):
        if self.fset:
            self.fset(instance, value)

    def __delete__(self, instance):
        if self.fdel:
            self.fdel(instance)

    def setter(self, fset):
        self.fset = fset

自定义一个类,类中包含要对属性进行get和set的方法。
将prop类属性设置为描述器,并将对属性的get和set方法的引用传递给描述器。

class Foo:
    def __init__(self, name):
        self.__name = name

    def get_prop(self):
        print(f'{self.__name} get_prop 被执行')
        return self.__prop * 2

    def set_prop(self, value):
        print(f'{self.__name} set_prop 被执行')
        self.__prop = value

    prop = Property(get_prop, set_prop)  

测试对属性的值的修改和读取

foo = Foo('Roland')
foo.prop = 10
print(foo.prop)

输出结果:

Roland set_prop 被执行
Roland get_prop 被执行
20

使用装饰器语法实现:
@Property相当于调用Property类的实例化方法,并把get_prop方法做为函数引用传递给__init_方法,生成一个描述器,变量get_prop将指向这个描述器。
@get_prop.setter,相当于调用了前一步创建的描述器的setter方法,而这个方法只是指这个set_prop函数引用保存到描述器对象中。

class Bar:
    def __init__(self, name):
        self.__name = name

    @Property
    def get_prop(self):
        print(f'{self.__name} get_prop 被执行')
        return self.__prop * 2

    @get_prop.setter
    def set_prop(self, value):
        print(f'{self.__name} set_prop 被执行')
        self.__prop = value

    print(locals())

测试使用:

bar = Bar('bar la')
bar.get_prop = 11
print(bar.get_prop)

输出结果:

{'__module__': '__main__', '__qualname__': 'Bar', '__init__': <function Bar.__init__ at 0x0000020F45AF3B00>, 'get_prop': <__main__.Property object at 0x0000020F45AF4C90>, 'set_prop': None}
bar la set_prop 被执行
bar la get_prop 被执行
22

如下为官方的property类的模拟实现:
定义一个Property类,用于模拟property类的实现机制。 注意,这个类的getter和setter方法会返回不同的装饰器实例。

class Property:

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc
        self._name = ''

    def __set_name__(self, owner, name):
        self._name = name

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError(f"property '{self._name}' has no getter")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError(f"property '{self._name}' has no setter")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError(f"property '{self._name}' has no deleter")
        self.fdel(obj)

    def getter(self, fget):
        prop = type(self)(fget, self.fset, self.fdel, self.__doc__)
        prop._name = self._name
        return prop

    def setter(self, fset):
        prop = type(self)(self.fget, fset, self.fdel, self.__doc__)
        prop._name = self._name
        return prop

    def deleter(self, fdel):
        prop = type(self)(self.fget, self.fset, fdel, self.__doc__)
        prop._name = self._name
        return prop

自定义一个类,使用装饰器来对get和set方法进行装饰,注意,官方这个模拟实现要求get与set方法必须使用相同的方法名。
@Property相当于使用实例化一个Property描述器,并将get_prop方法保存在描述器中。此时,变量名get_prop也就指向了这个描述器。
@get_prop.setter相当于调用了描述器的setter方法,而这个方法将返回一个新的描述器实例,而这个新的描述器实例同时保存了原来的get_prop方法以及新的set_prop方法。同时变量名get_prop转而指向了这个新的描述器。

class Bar:
    def __init__(self, name):
        self.__name = name

    @Property
    def get_prop(self):
        print(f'{self.__name} get_prop 被执行')
        return self.__prop * 2

    @get_prop.setter
    def get_prop(self, value):
        print(f'{self.__name} set_prop 被执行')
        self.__prop = value

    print(locals())

测试使用效果:

bar = Bar('bar la')
bar.get_prop = 11
print(bar.get_prop)

输出结果:CSS

{'__module__': '__main__', '__qualname__': 'Bar', '__init__': <function Bar.__init__ at 0x0000026FA8413CE0>, 'get_prop': <__main__.Property object at 0x0000026FA8415010>}
bar la set_prop 被执行
bar la get_prop 被执行
22

描述器实现将类中定义的函数绑定为实例的方法

为什么类中声明的函数,在被类实例调用的时候,就自动将实例本身做为第一个参数传递给了函数的第一个参数(self)? 这背后的机制也是由描述器实现的。
最简单的示例

class Foo:

    def bar(self, x):
        print(f'self is {self}, x is {x}')
        return x


print(Foo.bar('roland', 'abc'))

print('-' * 25, '分隔线', '-' * 25)

foo = Foo()
print(foo.bar('abc'))

输出结果:

self is roland, x is abc
abc
------------------------- 分隔线 -------------------------
self is <__main__.Foo object at 0x0000020D860752D0>, x is abc
abc

将函数声明在类的外部,然后用类属性指向这个函数
不难发现,其实将函数定义在类外部,完全可以达到相同的效果。

def fun(self, x):
    print(f'self is {self}, x is {x}')
    return x

class Foo:
    bar = fun

print(Foo.bar('roland', 'abc'))

print('-' * 25, '分隔线', '-' * 25)

foo = Foo()
print(foo.bar('abc'))

输出结果:

self is roland, x is abc
abc
------------------------- 分隔线 -------------------------
self is <__main__.Foo object at 0x000001F5047E51D0>, x is abc
abc

模拟实现实例方法绑定机制
python内部已经给我们做好了这种将类函数绑定成实例方法的机制。如下我们来自己模拟一下这个机制,以深入了解其内部实现过程。

# 在类外部定义一个独立函数
def fun(self, x):
    print(f'self is {self}, x is {x}')
    return x

# 定义一个类,实现call方法,用于替换对函数的直接调用
class MethodType:
    def __init__(self, func, instance):
        self.func = func
        self.instance = instance

    def __call__(self, *args, **kwargs):  # call方法调用时,直接调用原函数,并将函数所属类实例做为第一个参数传递给函数,其他参数也通过万能形参传进去。
        return self.func(self.instance, *args, **kwargs)

# 定义一个描述器类
class Function:
    def __init__(self, func):  # 将真正的函数保存起来
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:  # 如果该函数是由类直接调用,则直接返回该函数
            return self.func
        else:                 # 若函数是由所属的类的实例调用,则将其包装到一个MethodType类实例中,并转而使用该实例的call方法来代替直接调用函数
            return MethodType(self.func, instance)


class Foo:
    bar = Function(fun) # 实例化描述器,并把真正的函数保存在描述器中


print(Foo.bar('roland', 'abc'))  # 类调用属性时,描述器的__get__方法判断其为类调用后,直接返回原函数

print('-' * 25, '分隔线', '-' * 25)  # 类实例调用属性时,描述器的__get__方法判断将其先封装一个MethodType实例,然后调用其call方法来替代直接调用原函数。

foo = Foo()
print(foo.bar('abc'))

输出结果:

self is roland, x is abc
abc
------------------------- 分隔线 -------------------------
self is <__main__.Foo object at 0x00000210EA394A10>, x is abc
abc

改用装饰器语法也能达到同样效果

# 定义一个类,实现call方法,用于替换对函数的直接调用
class MethodType:
    def __init__(self, func, instance):
        self.func = func
        self.instance = instance

    def __call__(self, *args, **kwargs):  # call方法调用时,直接调用原函数,并将函数所属类实例做为第一个参数传递给函数,其他参数也通过万能形参传进去。
        return self.func(self.instance, *args, **kwargs)

# 定义一个描述器类
class Function:
    def __init__(self, func):  # 将真正的函数保存起来
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:  # 如果该函数是由类直接调用,则直接返回该函数
            return self.func
        else:                 # 若函数是由所属的类的实例调用,则将其包装到一个MethodType类实例中,并转而使用该实例的call方法来代替直接调用函数
            return MethodType(self.func, instance)


class Foo:
    @Function
    def bar(self, x):
        print(f'self is {self}, x is {x}')
        return x


print(Foo.bar('roland', 'abc'))  # 类调用属性时,描述器的__get__方法判断其为类调用后,直接返回原函数

print('-' * 25, '分隔线', '-' * 25)  # 类实例调用属性时,描述器的__get__方法判断将其先封装一个MethodType实例,然后调用其call方法来替代直接调用原函数。

foo = Foo()
print(foo.bar('abc'))

输出结果:

self is roland, x is abc
abc
------------------------- 分隔线 -------------------------
self is <__main__.Foo object at 0x000002189D9E4A10>, x is abc
abc

描述器实现内置classmethod装饰器

内置classmethod装饰器用法:

class Foo:
    @classmethod
    def test_class_method(cls, x):
        return cls.__name__, x

foo = Foo()
print(foo.test_class_method('abc'))  # 通过实例调用类方法
print(Foo.test_class_method('abc'))  # 通过类调用类方法

输出结果:

('Foo', 'abc')
('Foo', 'abc')

使用装饰器模拟实现classmethod内置装饰器

class MethodType:
    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)


class ClassMethod:
    def __init__(self, f):
        self.f = f

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

class Foo:
    @ClassMethod
    def test_class_method(cls, x):
        return cls.__name__, x

foo = Foo()
print(foo.test_class_method('abc'))  # 通过实例调用类方法
print(Foo.test_class_method('abc'))  # 通过类调用类方法

输出结果:

('Foo', 'abc')
('Foo', 'abc')

描述器实现内置staticmethod装饰器

内置staticmethod装饰器用法:

class Bar:
    @staticmethod
    def test_static_method(x):
        return x

bar = Bar()
print(bar.test_static_method('abc'))  # 通过对象调用静态方法
print(Bar.test_static_method('abc'))  # 通过类调用静态方法

输出结果:

abc
abc

使用装饰器模拟实现staticmethod内置装饰器

class StaticMethod:
    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        return self.f

    def __call__(self, *args, **kwargs):
        return self.f(*args, **kwargs)

class Bar:
    @StaticMethod
    def test_static_method(x):
        return x

bar = Bar()
print(bar.test_static_method('abc'))  # 通过对象调用静态方法
print(Bar.test_static_method('abc'))  # 通过类调用静态方法

输出结果:

abc
abc
posted @ 2024-12-05 15:51  RolandHe  阅读(23)  评论(0编辑  收藏  举报