python descript_2020-12-10 Python Descriptor & Bound method

Descriptor描述器

python中的descriptor表示一类对象,如果定义了 __getters__(), __setters__(), __delete__()的任意一个对象则可称之为descriptor, 基本上就是实现了一个对象的储存系统

三种实现descriptor protocal的方式

1. 自定义Descriptor class

类似django Model -> Field的写法,Model的每个字段为一个Descriptor的实例化,Descriptor则为Field

class Descriptor(object):

def __init__(self, value=""):

self.value = value

# 注意这里的value只是实现了一个储存器

# 在获取和修改时对self(当前对象)的属性进行操作

def __get__(self, obj, objtype):

"""在此例中

self为实例化描述符的本身即Descriptor()

obj为描述符所属的对象, MyClass()——实例化的MyClass对象

objtype则时MyClass——对象的class

"""

return "{}for{}".format(self.value, self.value)

def __set__(self, obj, value):

if isinstance(value, str):

self.value = value

else:

raise TypeError("Name should be string")

class MyClass(object):

name = Descriptor()

notes = Descriptor()

>>> obj = MyClass()

>>> print(obj.name)

for

>>> obj.notes = "test"

>>> print(obj.notes)

testfortest

2. 使用property函数

class MyClass(object):

def __init__(self, value):

self._value = value

def get_value(self):

return self._value

def set_value(self, value):

self._value = value

def del_value(self):

del self._value

value = property(get_value, set_value, del_value, "doc_string")

3. 使用@property装饰器

class MyClass(object):

def __init__(self, value):

self._value = value

@property

def value(self):

return self._value

@value.setter

def value(self, value):

self._value = value

@value.deleter

def value(self):

del self._value

function和bound method

python3.0以后没有unbound method这一说,仅有function和bound method, python2与之对应的为以下:

python3: &

vs python2: &

不同类别的函数的详细情况:

@classmethod 【bound method】

利用method-wrapper将类的本身cls作为函数第一个参数传入(method-wrapper在文章下一节会详细介绍)

instance method 【bound method】

利用method-wrapper将实例的本身self作为函数的第一个参数传入

@staticmethod 【function】

@property 【property —— 一种特殊的bound method?】

class MyClass:

@classmethod

def method1(cls):

return None

def method2(self):

return None

@staticmethod

def method3():

return None

@property

def p(self):

return 1

# 类方法

>>> MyClass.method1, MyClass().method1

(>,

>)

# 实例方法

>>> MyClass.method2, MyClass().method2

(,

at 0x00000289F45D74C0>>)

# 实例方法另外一种使用方法,使用descriptor __get__方法将实例对象传入

# 把function 转化为bound method

>>> MyClass.method2.__get__(MyClass())

at 0x00000289F459BF40>>

# 静态方法

>> MyClass.method3, MyClass().method3

(,

)

slot wrapper 和 method-wrapper

与function和bound method类似, slot wrapper和method-wrapper分别为CPython中对于function/bound method的实现,底层实现为C语言。

两者均为callable,其中slot wrapper有descriptor的实现(i.e.实现了__get__)。

python3官方包里的types.MethodType(func, obj)也实现了descriptor, 可以利用它来动态替换实例的方法(不建议这样使用):

"""

my_class_obj = MyClass() 时

types.MethodType(MyClass.method2, my_class_obj) 与

my_class_obj.method2 是等价的

"""

# instance-level method patch example

class MyClass:

def __init__(self, patch=False):

def method2_patch(self):

return 1

if patch:

self.method2 = types.MethodType(method2_patch, self)

def method2(self):

return None

Tips: 所有method-wrapper/bound method都有__self__属性,表示它被绑定的cls/self是什么,可以用于调试,在method-wrapper/bound method实际被调用时,会从__self__属性获取对象作为第一个需要传入的参数cls/self

可以使用object.__getattribute__获取一个slot wrapper,再使用slot wrapper的__get__方法将cls/self传入以获取cls/self的method-wrapper

General Example:

# 获取slot wrapper

sw = object.__getattribute__

# 然后可以使用descriptor protocal中的__get__(self, obj, objtype=None)

# 将类cls/实例self传入以获取method-wrapper bound method

sw.__get__(object())

sw.__get__(object)

Specific Example for MyClass:

# 一、实例函数由slot wrapper -> method-wrappert -> descriptor protocal

# 直到获取到bound method(实例函数)的方式:

>>> sw = object.__getattribute__

>>> sw

>>> sw.__get__(MyClass)

# MyClass默认由type创建

# 所以是type object, 实际上是一个type object,同时应该也是 class MyClass

# 只是这里__repr__展示的是创建它的objtype的object

# 注: objtype为__get__的最后一个参数

>>> sw.__get__(MyClass)("method2")

# 这里获取到的是MyClass的实例函数(没有被bound的, 属于function),与以下等价

>>> MyClass.method2

# 接着对funtion进行bound处理获取到bound method

>>> my_class_obj = MyClass()

>>> sw.__get__(MyClass)("method2").__get__(my_class_obj)

at 0x00000180E56D3BE0>>

>>> MyClass.method2.__get__(my_class_obj)

at 0x00000180E56D3BE0>>

# 二、类函数:

>>> object.__getattribute__.__get__(MyClass)("method1").__get__(MyClass())

>

>>> MyClass.method1

>

小结

以下的调用方式中 同一个行里面的本质是差不多一样的

# 1. 类方法

MyClass.method1, MyClass().method1

# 2. 实例方法

MyClass().method2, MyClass.method2.__get__(MyClass()), types.MethodType(MyClass.method2, MyClass())

MyClass.method2, object.__getattribute__.__get__(MyClass)("method2")

# 3. 静态方法

MyClass.method3, MyClass().method3

MyClass.__dict__["method3"], object.__getattribute__.__get__(MyClass)("method3")

Keywords

python, descriptor, function, bound method, slot wrapper, method-wrapper, staticmethod, classmethod, property, dynamic method patching, types.MethodType

参考

TODO: Descriptor How To Guide

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值