• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

Python descriptor(描述符)的实现

python 搞代码 4年前 (2022-01-09) 16次浏览 已收录 0个评论

问题

问题1

Python是一种动态语言,不支持类型检查。当需要对一个对象执行类型检查时,可能会采用下面的方式:

class Foo(object):
 def __init__(self,a):
 if isinstance(a,int):
  self.__a = a
 else:
  raise TypeError("Must be an int")

 def set_a(self,val):
 if isinstance(val,int):
  self.__a = val
 else:
  raise TypeError("Must be an int")

 def get_a(self):
 return self.__a

上述是一种类型检查的方法,但是如果需要类型检查的参数非常多的话就会变得非常繁琐,重复代码太多,Python这么简洁,优雅,肯定有更好的解决方法。另外上述方法也有缺陷。

f = Foo(1)
print f.get_a() #1
print f._Foo__a #1,还是能访问到= =
f._Foo__a = 'test'
print f.get_a() #test,还是改变了__a的值,而且不是int型
print f._Foo__a #test

问题2

在一个对象中,创建一个只读属性。

问题3

class Foo(object):
 a = 1
f = Foo()
print f.a #1,实例属性中没有a属性,所以到Foo.__dict__中<em>本文来源[email protected]搞@^&代*@码2网</em>查找
print Foo.a #1
print f.__dict__ #{}
f.a = 2  #增加一个名为a的实例属性
print f.a #2,搜索属性时先在实例字典中查找,然后再去类的字典中查找,在实例的__dict__中找到了..
print Foo.a #1
print f.__dict__ #{'a': 2}

如果不想给实例f增加实例属性,而是想对类属性操作怎么办呢。解决方案:

1) 使用class.attr改变值;

Foo.a = 2就不会给实例增加实例属性了。

2) 自定义属性访问,描述符;

class descriptor(object):
 def __init__(self,val):
 self.val = val
 def __get__(self,obj,type = None):
 print 'get',
 return self.val
 def __set__(self,obj,val):
 print 'set',val
 self.val = val
 def __delete__(self,obj):
 raise AttributeError("Can't delete attribute")
class Foo(object):
 a = descriptor(0)

f = Foo()
print f.a #get 0
print Foo.a #get 0
print f.__dict__ #{}
f.a = 2  #set 2,并没有增加实例属性
print f.a #get 2
print Foo.a #get 2
print f.__dict__ #{}

问题总结

上述三个问题均与属性访问有关,如果能够自定义属性访问,上述问题就能解决啦= =。其实问题3已经给出了解决方法,就是描述符…

描述符的定义和介绍

​ 描述符(Descriptor)是Python中非常重要的一部分,它广泛应用于Python的内核。

​ 一般来说,描述符就是一个带有绑定方法的对象,只不过照比其他对象多了几个特殊的描述符方法,即 __get__(),__set__(),__delete__()对描述符对象的属性访问主要通过描述符方法。

定义:一个对象如果定义了__get__(),__set__(),__delete__()方法中的任何一个,它就可以被称为描述符。

​ 对属性的访问默认是从对象的字典中获取(get),设置(set)和删除(delete)属性。

假设有实例a,a有属性x,获取a.x的值。

一般来说,a.x会按以下顺序查找属性,查找链: a.__dict__['x']type(a).__dict__['x']根据mro顺序在type(a)的父类中查找(不包括元类)

​ 但是,如果被查找的属性是一个描述符,并且为类属性,那么就会覆盖其默认行为,转而去调用描述符方法。注意,描述符仅适用于新式类和新式对象。

class descriptor(object):
 def __init__(self,val):
  self.val = val

 def __get__(self, obj, objtype):
  print 'get',
  return self.val

 def __set__(self, obj, val):
  print 'set'
  self.val = val

class Foo(object):
 x = descriptor(0)
 y = 0

f = Foo()
'''描述符覆盖默认的访问行为'''
print f.x   #get 0,调用的是描述符的__get__函数
print f.__dict__ #{}
f.x = 1    #set,调用的是描述符的__set__函数
print f.x   #get 1
print f.__dict__ #{},即使赋值也没有增加实例属性...,是不是覆盖了默认行为- -
f.y = 2    
print f.__dict__ #{'y': 2},因为没有与y同名的描述符对象...

搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Python descriptor(描述符)的实现

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址