1.描述器的表现

用到三个魔术方法,__get__(), __set__(), __delete__()
方法签名如下
object.__get__(self,instance,owner)
object.__set__(self,instance,value)
object.__delete(self,instance)
self指代当前实例,调用者
instance是owner的实例
owner是属性的所输的类
#描述器A调用并赋值给了类B的属性,当调用类B或者实例b时,去类A执行__get__()函数,类调用instance返回none,实例调用返回实例
#执行顺序和类,实例字典无关
class A:
def __init__(self):
print(2,'A init')
def __set__(self, instance, value):
print(3,self,instance,value)
def __get__(self, instance, owner): #return值,将会影响b.x or B.x的调用属性
print(4,self,instance,owner) class B:
x = A()
def __init__(self):
print(1,'B init') b = B() #output 2->1
b.x #output 4 <__main__.A object at 0x047B09D0> <__main__.B object at 0x047BB350> <class '__main__.B'>
B.x #output 4 <__main__.A object at 0x047B09D0> None <class '__main__.B'> 此时访问b.x.a1 B.x.a1都会报错 AttributeError:Nonetype
问题出在__get__的返回值,修改为 return self 返回A的实例就具备了a1属性 返回正常
class B:
x = A()
def __init__(self):
self.b1 = A()
print(1,'B init') b = B() #output 2->1
print(b.b1) #output <__main__.A object at 0x03000990> 没有触发__get__的打印
从运行结果可以看出,只有类属性是类的实例才行

2.描述其的定义

python中,一个类实现了__get__,__set__.__delete__的三个方法中的任意一个就是描述器
1.如果仅仅实现了__get__.就是非数据描述器 non-data descriptor
2.同时实现了__get__,__set__,就是数据描述器,data descriptor
如果一个类的类属性,设置为描述器,那么这个类被称为owner属主,method也是类的属性
class A:
def __init__(self):
self.a1 = 'a1'
print(2,'A init') def __get__(self, instance, owner):
print(4,self,instance,owner)
return self class B:
x = A()
def __init__(self):
self.x = 'b1' #如果描述器定义了__set__,此时b1就是value
print(1,'B init') b = B() #output 2->1
print(B.x) #output 4 <__main__.A object at 0x04EEB350> None <class '__main__.B'> ;;;; return <__main__.A object at 0x02E8B350>
print(B.x.a1) #output 4 <__main__.A object at 0x02E8B350> None <class '__main__.B'> ;;;;return a1
print(b.x) #return b1 访问到了实例的属性,而不是描述器
print(b.x.a1) #AttributeError 'str object has no Attribute
在非数据描述器中,owner实例的属性 会被实例调用,而不是访问__get__描述器
#添加了set方法 对比上个代码,;数据描述器
class A:
def __init__(self):
self.a1 = 'a1'
print(2,'A init')
def __set__(self, instance, value): #当定义了set魔术方法后,B实例定义的实例属性self.x = 'b1 不会在写进实例字典,而是调用set方法
print(3,self,instance,value)
# instance.__dict__['x']=value
def __get__(self, instance, owner): #return值,将会影响b.x or B.x的调用属性
print(4,self,instance,owner)
# return instance.__dict__['x']
return self
class B:
x = A()
def __init__(self):
print(1,'B init')
print("+++++++++++++++++++++++")
self.x = 'b1'
print("+++++++++++++++++++++++") b = B() #output 2->1->+ ->3-> + ;;实例化时候,self.x = 'b1'调用了set方法
print(b.x.a1) #return a1 直接调用get
b.x = 100 #return a1 直接调用set
print(b.__dict__) #实例字典为空
print(B.__dict__)

总结:实例的__dict__优先于非数据描述器;;;数据描述器优先于实例__dict__

2.1描述器查找顺序和__dict__的关系

class A:
def __init__(self):
self.a1 = 'a1'
print(2,'A init')
def __set__(self, instance, value):
print(3,self,instance,value)
self.data = value
print(self.data)
def __get__(self, instance, owner):
print(4,self,instance,owner)
return self
class B:
x = A()
def __init__(self):
print(1,'B init')
self.x = 'b.x'
self.y = 'b.y'
self.z = 'b.z' b = B() #output 2->1->+ ->3-> + ;;实例化时候,self.x = 'b1'调用了set方法
print(b.y) #return b.y
print(b.x)
print(B.__dict__)
print(b.__dict__)#output {'y': 'b.y', 'z': 'b.z'} ;;;self.x 这里的x是x = A()所以调用set方法,而self.y self.z追加到字典

2.3练习

from functools import  partial
class StaticMethod:
def __init__(self,fn):
self.fn = fn
def __get__(self, instance, owner):
return self.fn
class ClassMethod:
def __init__(self,fn):
self.fn = fn
def __get__(self, instance, owner):
# return self.fn(instance)
return partial(self.fn,instance)
class Test:
@StaticMethod #st = staticmethod(st) 去调用__get__方法时,不用在考虑这个Test类了 装饰器到描述器执行,调用时加()就返回值
def st(): #st = return self.fn
print('st')
@ClassMethod
def ct(cls): #ct = return self.fn(instance)
print('ct')
t = Test()
Test.st()
t.st()
t.ct()
Test.ct()
class Typed:
def __init__(self,name,type):
self.name = name
self.type = type def __set__(self, instance, value):
if not isinstance(value,self.type):
raise TypeError(value)
instance.__dict__[self.name]=value def __get__(self, instance, owner):
if instance is not None:
return instance.__dict__[self.name]
return self
class Person:
name = Typed('name',str) # 1 演变其他的基础
age = Typed('age',int)
def __init__(self,name:str,age:int):
self.name = name #
self.age = age p = Person('tom',21)
print(p.__dict__) #output {'name': 'tom', 'age': 21}
print(p.age) #output 21
print(Person.age.name) #output age
print(Person.name.name) #output name
print(Typed.__dict__)
print(Person.__dict__)
# 1-->类属性调用了类实例,2-->有set,此处调用set方法,因为数据描述器,所以字典为空,自己追加字典值,
#get也需要从字典中获取值
import inspect
class Typed:
def __init__(self,name,type):
self.name = name
self.type = type def __set__(self, instance, value):
if not isinstance(value,self.type):
raise TypeError(value)
instance.__dict__[self.name]=value def __get__(self, instance, owner):
if instance is not None:
return instance.__dict__[self.name]
return self def typeassert(cls):
parmas = inspect.signature(cls).parameters
for k,v in parmas.items():
if v.annotation != v.empty:
setattr(cls,k,Typed(v,v.annotation))
return cls
@typeassert
class Person:
def __init__(self,name:str,age:int):
self.name = name #
self.age = age p = Person('tom',21)

Python之描述器的更多相关文章

  1. Python 黑魔法 --- 描述器(descriptor)

    Python 黑魔法---描述器(descriptor) Python黑魔法,前面已经介绍了两个魔法,装饰器和迭代器,通常还有个生成器.生成器固然也是一个很优雅的魔法.生成器更像是函数的行为.而连接类 ...

  2. python 简单了解一下 描述器

    1.描述器是什么? 在Python中描述器也被称为描述符, 1)描述器实际上是任何新式类(新式类是继承自 type 或者 object 的类),这种类至少实现了3个特殊的方法__get__, __se ...

  3. python cookbook第三版学习笔记十三:类和对象(三)描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  4. Python 面向对象(五) 描述器

    使用到了__get__,__set__,__delete__中的任何一种方法的类就是描述器 描述器的定义 一个类实现了__get__,__set__,__delete__中任意一个,这个类就是描述器. ...

  5. python类:描述器Descriptors和元类MetaClasses

    http://blog.csdn.net/pipisorry/article/details/50444769 描述器(Descriptors) 描述器决定了对象属性是如何被访问的.描述器的作用是定制 ...

  6. python描述器

    描述器定义 python中,一个类实现了__get__,__set__,__delete__,三个方法中的任何一个方法就是描述器,仅实现__get__方法就是非数据描述器,同时实现__get__,__ ...

  7. (转)面向对象(深入)|python描述器详解

    原文:https://zhuanlan.zhihu.com/p/32764345 https://www.cnblogs.com/aademeng/articles/7262645.html----- ...

  8. Python描述器引导(转)

    原文:http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html 1. Python描述器引导(翻译) 作者: Raymond ...

  9. Python入门之面向对象编程(四)Python描述器详解

    本文分为如下部分 引言——用@property批量使用的例子来引出描述器的功能 描述器的基本理论及简单实例 描述器的调用机制 描述器的细节 实例方法.静态方法和类方法的描述器原理 property装饰 ...

随机推荐

  1. cocos2dx 粒子系统

    参考文献: 1.http://blog.csdn.net/aa4790139/article/details/8126525 2.https://code.google.com/p/cocos2d-w ...

  2. sql server创建临时表的两种写法和删除临时表

    --创建.删除临时表 --第一种方式 create table #tmp(name varchar(255),id int) --第二种方式 select count(id) as storyNum ...

  3. 解决双系统(Window10+Ubuntu16.10)下ubuntu安装git时提示软件包git没有可安装候选问题

    选择升级系统: sudo apt-get update 升级之后再输入: sudo apt-get install git 可成功安装.

  4. 洛谷P4064 加法 [JXOI2017] 贪心

    正解:贪心 解题报告: 传送门! 首先最小值最大显然考虑二分?然后就二分一个值mid,从左往右考虑,对于小于等于mid的点显然可以求出这个点至少要加几次,然后找到覆盖这个点的右端点max的区间区间加上 ...

  5. MySQL InnoDB加锁超时回滚机制(转)

    add by zhj: 看来我对MySQL的理解还有待深入,水还是挺深的啊,MySQL给记录加锁时,可以通过innodb_lock_wait_timeout参数设置超时时间, 如果加锁等待超过这个时间 ...

  6. 让对象支持with语句

    一.with语句的好处 with语句的好处在于,它可以自动帮我们释放上下文,就比如文件句柄的操作, 如果你不使用with语句操作,你要先open一个文件句柄,使用完毕后要close这个文件句柄, 而使 ...

  7. webmin账户重置密码

    locate changepass.pl(如果你不常使用locate的话那,先sudo updatedb)找到路径,在/usr/libexec/webmin/下面,转到这个目录下面./changepa ...

  8. WebSocket 学习教程(一):理论

    一.WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通 ...

  9. oracle-安装-init.sh

    !#/bin/bashgroupadd -g 1001 oinstallgroupadd -g 1002 dbagroupadd -g 1003 opergroupadd -g 1004 asmadm ...

  10. linux中cmake语法的学习

    在linux 下进行开发很多人选择编写makefile 文件进行项目环境搭建,而makefile 文件依赖关系复杂,工作量很大,搞的人头很大.常常,写代码,效率才是王道.这里还有自动化的项目构建工具C ...