首先我们搞清楚__getattr__ ,__get__ 和 __getattribute__ 作用的不同点。

  • __getattr__在授权中会用到。
  • __getattribute__  当要访问属性时,就会一开始被调用,你可以定义,也可以不定义(默认)。
class B():
def __init__(self,x):
self.x = x
self.y =200 def __getattribute__(self, item):
if item == 'x':
return 'xxx'
else:
return object.__getattribute__(self,item) def __getattr__(self, item):
return 'defautvalue' def say(self):
print('this is B') b=B(100)
print(b.x) #查找顺序__getattribute__, __dict__, 父类的__dict__,__getattr__
b.say()

输出结果:

xxx
this is B

同时它也是描述符系统的心脏。

  • __get__是描述符中的一个属性(下面会具体讲)

1.描述符的用法

就是将某种特殊类型的类的实例指派给另一个类的属性(注意:这里是类属性,而不是对象属性)。而这种特殊类型的类就是实现了__get__,__set__,__delete__的新式类(即继承object)。

属性就是描述符,定义的描述符就是可以被重复使用的属性。当你需要属性时候,可以通过点属性表示号访问(通过默认的__get__,__set__,__delete__函数),如果是描述符,则需要通过描述符的类的属性来访问

属性和定义的描述符不同之处在于,描述符需要指定的类中重新编辑__get__,__set__,__delete__函数。

下面定义一个描述符,obj表示实例,下面就是c1,;type表示实例的类型,下面就表示C1。

class Dev():
def __get__(self,obj,type=None):
print('get',self,obj,type)
pass
def __set__(self,obj,val):
print('set',self,obj,val)
pass class C1():
foo = Dev()    #将某种特殊类型的类的实例指派给另一个类的属性 c1=C1()
c1.foo= ''
print(c1.foo)
print(C1.foo)

输出结果:

set <__main__.Dev object at 0x0053F3F0> <__main__.C1 object at 0x0053F350> 3
get <__main__.Dev object at 0x0053F3F0> <__main__.C1 object at 0x0053F350> <class '__main__.C1'>
None
get <__main__.Dev object at 0x0053F3F0> None <class '__main__.C1'>
None

  

给定类X和实例x,以及属性fool,

x.fool将会被__getattribute__转化为:

type(x).__dict__['foo'].__get__(x,type(x))   。  x.fool= val(如果x中没有fool属性)  会被转化成:type(x).__dict__['foo'].__set__(x,val)

当访问X.fool(如果X中没有fool值)转化为:X.__dict__['fool'].__get__(None,X)  。       X.fool=val会直接赋值,可以理解为调用了默认的__set__。

在python中__getattribute__如果还没有找到,就到__getattr__中寻找。

我们执行下面的程序:

 class simpleDescriptor(object):
def __get__(self, obj, type=None):
return 'getting' def __set__(self, obj, val):
return 'setting' class A(object):
foo = simpleDescriptor() print(A.__dict__)
print(A.foo)
a = A()
print(a.foo) #执行描述器里面的__get__
a.foo = 13 #执行描述器里面的__set__
print(a.__dict__)
print(a.foo)
print(A.foo)

得到结果:

{'__module__': '__main__', 'foo': <__main__.simpleDescriptor object at 0x0217F370>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
getting
getting
{}
getting
getting

第12行,因为foo为描述器,转换为X.__dict__['fool'].__get__(None,X)  ,调用描述器里面的__get__。

但是当我们执行

 class simpleDescriptor(object):
def __get__(self, obj, type=None):
return 'getting' def __set__(self, obj, val):
return 'setting' class A(object):
foo = simpleDescriptor() print(A.__dict__)
A.foo=3 #这个表示类属性的赋值
print(A.__dict__)
print(A.foo)
a = A()
print(a.foo)
a.foo = 13
print(a.__dict__)
print(a.foo)
print(A.foo)

得到结果

{'__module__': '__main__', 'foo': <__main__.simpleDescriptor object at 0x0043A4F0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
{'__module__': '__main__', 'foo': 3, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
3
3
{'foo': 13}
13
3

执行第12行,因为有类属性'foo'了,所以是直接赋值。

因为第12行,A.__dict__['foo']已经不是一个描述器了。当我们执行第17行a.foo=13 的时候,因为x.__dict__[‘fool’]中没有,转化成A.__dict__['foo'].__set__(a,val),因为不是描述器了,所以不根据描述器中的__set__赋值。

第16行,因为x.__dict__[‘fool’]中没有,所以转化成X.__dict__['fool'].__get__(None,X),以及不是描述器,所以不用描述器里面的__get__。

第19行,有实例属性'foo'了,可以直接显示。

所以很多时候取决于,类属性foo在类的__dict__存储中是不是被表明为描述器。

没有__set__的在书籍里被称为非数据描述符,不好理解,我可以这么理解,看下面这个程序:

 class simpleDescriptor(object):
def __get__(self, obj, type=None):
return 'getting' class A(object):
foo = simpleDescriptor() print(A.__dict__)
print(A.foo)
a = A()
print(a.foo)
a.foo = 13
print(a.__dict__)
print(a.foo)
print(A.foo)

执行结果为:

{'__module__': '__main__', 'foo': <__main__.simpleDescriptor object at 0x005C0210>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
getting
getting
{'foo': 13}
13
getting

第12行执行以后,因为实例属性中没有,所以要转化成A.__dict__['foo'].__set__(a,val),因为没有描述器中没有__set__,所以就会调用默认的给赋值。

到第13行的时候,因为被赋值,所以实例属性中有,可以直接被调用。

2.property函数的用法

property是一种有用的特殊的描述符。一般写在类定义中。property()一般形式为property(fget,fset,fdel,doc)

class HideX():

    def __init__(self, x):
self.x = x def get_x(self):
print('call get_x')
return self._x #!!! def set_x(self, val):
self._x = val
print('call set_x ') x = property(get_x, set_x) inst = HideX(10) # 就触发了set_x函数
print(inst.x) # 触发了get_x函数
inst.x = 20 # 调用的是property属性x,call set_x
print(inst.x) #

相当于把描述符中的__set__方法放到了类中来描述。当你调用inst.x, 其实是调用x中的__get__方法,也就是property.get_x()函数。结果为:

call set_x
call get_x
10
call set_x
call get_x
20

x是描述符,第一行的Hidex(10)调用了self.x=10,就相当于赋值,会调用__set__方法。

第二行,相当于显示,调用__get__方法。

也可以这么用:

 class Movie(object):
def __init__(self, title, rating, runtime, budget, gross):
self._budget = None self.title = title
self.rating = rating
self.runtime = runtime
self.gross = gross
self.budget = budget @property
def budget(self):
return self._budget @budget.setter
def budget(self, value):
if value < 0:
raise ValueError("Negative value not allowed: %s" % value)
self._budget = value def profit(self):
return self.gross - self.budget m = Movie('Casablanca', 97, 102, 964000, 1300000) #calls budget.setter(96400)
print (m.budget) # calls m.budget(), returns result
try:
m.budget = -100 # calls budget.setter(-100), and raises ValueError
except ValueError:
print ("Woops. Not allowed")

第11行的意思表示 budget =property(budget)

第第15行的意思表示 budget=property(fget=budget)

结果:

964000
Woops. Not allowed

3.property函数和描述符的比较

因为描述符是在单独的类中描述的,而且有传递实例和实例的类型,所以可以统一命名,property函数无法统一命名。

 from weakref import WeakKeyDictionary

 class NonNegative(object):
"""A descriptor that forbids negative values"""
def __init__(self, default):
self.default = default
self.data = WeakKeyDictionary() def __get__(self, instance, owner):
# we get here when someone calls x.d, and d is a NonNegative instance
# instance = x
# owner = type(x)
return self.data.get(instance, self.default) def __set__(self, instance, value):
# we get here when someone calls x.d = val, and d is a NonNegative instance
# instance = x
# value = val
if value < 0:
raise ValueError("Negative value not allowed: %s" % value)
self.data[instance] = value class Movie(object): #always put descriptors at the class-level
rating = NonNegative(0)
runtime = NonNegative(0)
budget = NonNegative(0)
gross = NonNegative(0) def __init__(self, title, rating, runtime, budget, gross):
self.title = title
self.rating = rating
self.runtime = runtime
self.budget = budget
self.gross = gross def profit(self):
return self.gross - self.budget m = Movie('Casablanca', 97, 102, 964000, 1300000)
print(m.budget) # calls Movie.budget.__get__(m, Movie)
m.rating = 100 # calls Movie.budget.__set__(m, 100)
try:
m.rating = -100 # calls Movie.budget.__set__(m, -100)
except ValueError:
print ("Woops, negative value")

结果是:

964000
Woops, negative value

  具体可以见下面:http://www.geekfan.net/7862/   (这个文章太牛逼了,初学者可以看十遍,关于描述符就很懂了)

描述符和property内建函数的更多相关文章

  1. 课时46:魔法方法:描述符(property的原理)

    目录: 一.描述符(property的原理) 二.课时46课后习题及答案 ********************************** 一.描述符(property的原理) ********* ...

  2. Python描述符以及Property方法的实现原理

    Python描述符以及Property方法的实现原理 描述符的定义: 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实了__get__(),__set__(),__delete__()中 ...

  3. Python描述符:property()函数的小秘密

    描述符:将某种特殊类型的类的实例指派给另一个类的属性(注意:这里是类属性,而不是对象属性).而这种特殊类型的类就是实现了__get__,__set__,__delete__这三个方法中的一个或多个的新 ...

  4. 利用描述符自定义property

    class Lazyproperty: def __init__(self,func): #传的func函数是被描述的类中的函数属性 self.func = func def __get__(self ...

  5. 使用描述符实现property功能

    # Author : Kelvin # Date : 2019/1/25 14:46 class Decproperty: def __init__(self, func): self.func = ...

  6. Python 描述符 (descriptor)

    1.什么是描述符? 描述符是Python新式类的关键点之一,它为对象属性提供强大的API,你可以认为描述符是表示对象属性的一个代理.当需要属性时,可根据你遇到的情况,通过描述符进行访问他(摘自Pyth ...

  7. python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解

     1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...

  8. python描述符理解

    Python中的描述符是一个相对底层的概念 descriptor Any object which defines the methods get(), set(), or delete(). Whe ...

  9. python2.7高级编程 笔记二(Python中的描述符)

    Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些" ...

随机推荐

  1. bin文件和elf文件

    ELF文件格式是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式,它有三种不同的类型: 可重定位的目标文件(Relocatable,或者Object File) 可执行文件(Executab ...

  2. android 弹出带输入框的对话框

    private void inputTitleDialog() { final EditText inputServer = new EditText(this);         inputServ ...

  3. RSA算法优化

    RSA算法优化 大数乘法 模乗优化 剩余定理(孙子定理) RSA加解密 python的RSA计算优化 #-*- coding: utf-8 -*- ''' /********************* ...

  4. iOS 指南针的制作 附带源码

    iOS  指南针的制作  附带源码 代码下载地址: http://vdisk.weibo.com/s/HK4yE   http://pan.baidu.com/share/link?shareid=7 ...

  5. 如何在网站中加入markdown

    在vue组件中加入markdown,模板使用的是webpack 我是这样做的: 因为是npm引入的,所以markdown是遵循CommonJS规范的,需要在webpack.base.conf.js里引 ...

  6. Spark 2.x不支持ALTER TABLE ADD COLUMNS,没关系,我们改进下

    SparkSQL从2.0开始已经不再支持ALTER TABLE table_name ADD COLUMNS (col_name data_type [COMMENT col_comment], .. ...

  7. PHP5.6通过CURL上传图片@符无效的兼容问题

    今天本来想试试一个图片云的API,于是本地做了个上传图片的测试,结果灰常郁闷的发现以前一直用的好好的CURL上传图片居然死活不起作用,本来几分钟搞定的事情,结果折腾了大半天才终于找到原因,居然是兼容性 ...

  8. ViewCompat.animate(view) floatEval.evaluate() argbEval.evaluate()

    ViewCompat.animate(ivHead) .translationX(60)// .setInterpolator(new CycleInterpolator(4))//循环执行// .s ...

  9. 一台服务器上同时启动多个 Tomcat

    在同一台服务器上启动多个 Tomcat 服务,需要修改 conf/server.xml文件里的三个部分,如下: 1.修改Http访问端口(默认为8080端口) <Connector port=& ...

  10. 传统flv网页视频(flvplayer)--兼容

    1.flv网页视频 项目中需要web打开视频,百度搜了好几个插件,本来是打算用H5 vedio,但是客户的浏览器用的比较老,如果用H5标签的话,IE9以下的浏览器是不支持的.所以费劲又去找了其他插件. ...