标签:ring 不同 function one 影响 force shadow forward 其他
Python存取属性的方式特别不对等,通过实例读取属性时,通常返回的是实例中定义的属性,但如果实例未曾定义过该属性,就会获取类属性,而为实例的属性赋值时,通常会在实例中创建属性,而不会影响到类本身。这种不对等的方式对描述符类也有影响。
def cls_name(obj_or_cls): # 传入一个实例,返回类名
cls = type(obj_or_cls)
if cls is type:
cls = obj_or_cls
return cls.__name__.split(‘.‘)[-1]
def display(obj):
cls = type(obj)
if cls is type: # 如果obj是一个类,则进入该分支
return ‘<class {}>‘.format(obj.__name__)
elif cls in [type(None), int]: # 如果obj是None或者数值,进入该分支
return repr(obj)
else: # 如果obj是一个实例
return ‘<{} object>‘.format(cls_name(obj))
def print_args(name, *args):
pseudo_args = ‘, ‘.join(display(x) for x in args)
print(‘-> {}.__{}__({})‘.format(cls_name(args[0]), name, pseudo_args))
class Overriding: # <1>
"""a.k.a. data descriptor or enforced descriptor"""
def __get__(self, instance, owner):
print_args(‘get‘, self, instance, owner)
def __set__(self, instance, value):
print_args(‘set‘, self, instance, value)
class OverridingNoGet: # <2>
"""an overriding descriptor without ``__get__``"""
def __set__(self, instance, value):
print_args(‘set‘, self, instance, value)
class NonOverriding: # <3>
"""a.k.a. non-data or shadowable descriptor"""
def __get__(self, instance, owner):
print_args(‘get‘, self, instance, owner)
class Managed: # <4>
over = Overriding()
over_no_get = OverridingNoGet()
non_over = NonOverriding()
def spam(self): # <5>
print(‘-> Managed.spam({})‘.format(display(self)))
覆盖型描述符
实现__set__方法的描述符属于覆盖型描述符,虽然描述符是类属性,但是实现了__set__方法的话,会覆盖对实例属性的赋值操作。特性也是覆盖型描述符,见Python动态属性和特性(二),如果没有提供设值函数,property类中的fset方法就会抛出AttributeError异常,指明那个属性时只读的
下面我们来看下覆盖型描述符的行为:
>>> obj = Managed() # <1>
>>> obj.over # <2>
-> Overriding.__get__(<Overriding object>, <Managed object>, <class Managed>)
>>> Managed.over # <3>
-> Overriding.__get__(<Overriding object>, None, <class Managed>)
>>> obj.over = 8 # <4>
-> Overriding.__set__(<Overriding object>, <Managed object>, 8)
>>> obj.over # <5>
-> Overriding.__get__(<Overriding object>, <Managed object>, <class Managed>)
>>> obj.__dict__["over"] = 9 # <6>
>>> vars(obj) # <7>
{‘over‘: 9}
>>> obj.over # <8>
-> Overriding.__get__(<Overriding object>, <Managed object>, <class Managed>)
没有__get__方法的覆盖型描述符
>>> obj.over_no_get # <1> <descriptorkinds.OverridingNoGet object at 0x0000001742369780> >>> Managed.over_no_get # <2> <descriptorkinds.OverridingNoGet object at 0x0000001742369780> >>> obj.over_no_get = 8 # <3> -> OverridingNoGet.__set__(<OverridingNoGet object>, <Managed object>, 8) >>> obj.over_no_get # <4> <descriptorkinds.OverridingNoGet object at 0x0000001742369780> >>> obj.__dict__[‘over_no_get‘] = 6 # <5> >>> obj.over_no_get # <6> 6 >>> obj.over_no_get = 7 # <7> -> OverridingNoGet.__set__(<OverridingNoGet object>, <Managed object>, 7) >>> obj.over_no_get # <8> 6
非覆盖型描述符:没有实现__set__方法的描述符称为是非覆盖型描述符,如果设置了同名的实例属性,描述符会被覆盖,致使描述符无法处理那个实例的那个属性
>>> obj.non_over # <1> -> NonOverriding.__get__(<NonOverriding object>, <Managed object>, <class Managed>) >>> obj.non_over = 6 # <2> >>> obj.non_over # <3> 6 >>> Managed.non_over # <4> -> NonOverriding.__get__(<NonOverriding object>, None, <class Managed>) >>> del obj.non_over # <5> >>> obj.non_over # <6> -> NonOverriding.__get__(<NonOverriding object>, <Managed object>, <class Managed>)
覆盖类中的描述符:不管描述符是不是覆盖型的,为类属性赋值都能覆盖描述符
>>> obj = Managed() # <1> >>> Managed.over = 1 # <2> >>> Managed.over_no_get = 2 >>> Managed.non_over = 3 >>> obj.over, obj.over_no_get, obj.non_over # <3> (1, 2, 3)
方法是描述符:在类中定义的函数属于绑定方法,用户定义的函数都有__get__方法,所以依附到类上时,就相当于描述符,方法是非覆盖型描述符
>>> obj = Managed() >>> obj.spam # <1> <bound method Managed.spam of <descriptorkinds.Managed object at 0x00000017423284A8>> >>> Managed.spam # <2> <function Managed.spam at 0x0000001742322AE8> >>> obj.spam = 7 # <3> >>> obj.spam 7
函数没有__set__方法,因此是非覆盖型描述符,从上面的例子来看,obj.spam和Managed.spam获取的是不同对象。与描述符一样,通过托管类访问时,函数__get__方法会返回自身的引用,但是通过实例访问时,函数的__get__方法返回的是绑定方法对象:一种可调用的对象,里面包装着函数,并把托管实例(如obj)绑定给函数的第一个参数(即self),这与functools.partial函数的行为一致
为了了解这种机制,让我们看下面一个例子:
import collections
class Text(collections.UserString):
def __repr__(self):
return ‘Text({!r})‘.format(self.data)
def reverse(self):
return self[::-1]
测试Text类:
>>> word = Text("forward")
>>> word # <1>
Text(‘forward‘)
>>> word.reverse() # <2>
Text(‘drawrof‘)
>>> Text.reverse(Text(‘backward‘)) # <3>
Text(‘drawkcab‘)
>>> type(Text.reverse), type(word.reverse) # <4>
(<class ‘function‘>, <class ‘method‘>)
>>> list(map(Text.reverse, [‘repaid‘, (10, 20, 30), Text(‘stressed‘)])) # <5>
[‘diaper‘, (30, 20, 10), Text(‘desserts‘)]
>>> func = Text.reverse.__get__(word) # <6>
>>> func() # <7>
Text(‘drawrof‘)
>>> Text.reverse.__get__(None, Text) # <8>
<function Text.reverse at 0x000000266209E598>
>>> Text.reverse
<function Text.reverse at 0x000000266209E598>
>>> word.reverse # <9>
<bound method Text.reverse of Text(‘forward‘)>
>>> Text.reverse.__get__(word)
<bound method Text.reverse of Text(‘forward‘)>
>>> word.reverse.__self__ # <10>
Text(‘forward‘)
>>> word.reverse.__func__ is Text.reverse # <11>
True
绑定方法对象还有个__call__方法,用于处理真正的调用过程,这个方法调用__func__属性引用的原始函数,把函数的第一个参数设置为绑定方法的__self__属性,这就是形参self的隐式绑定方式
描述符用法建议:
标签:ring 不同 function one 影响 force shadow forward 其他
原文地址:https://www.cnblogs.com/beiluowuzheng/p/9246103.html