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. 2018/04/24 PHP 设计模式之注册树模式

    之前学习了工厂模式和单例模式,明白了他们的意义. 但是我们在之后的使用中会发现一个问题,在新建一个实例的时候还是需要调用一个单例或者工厂,之后还是造成了代码和耦合和不好处理. 下面开始说一下: -- ...

  2. 启动虚拟机提示"Units specified don’t exist SHSUCDX can’t install"

    新建虚拟机快速分区后启动报"Units specified don’t exist SHSUCDX can’t install",试过网上说的 修改BIOS设置方法不起作用 修改虚 ...

  3. (1.14)mysql锁问题之MyIsam

    1.mysql锁概述 BDB被InnoDB代替了,MyIsam在8.0也被抛弃了 2.MyIsam表锁(读写是串行的) [2.1]查看表锁争用情况. MyIsam存储引擎只支持表锁. 查看表锁争用情况 ...

  4. 前端 HTML form表单标签 input标签 type属性 file 上传文件

     加上上传文件功能 input type='file' - 依赖form表单里一个属性 enctype="multipart/form-data" 加上这个属性表示把你上次文件一点 ...

  5. UIBezierPath使用

    效果图,Demo的例子是我自己做的,下面曲线的代码是从别处copy过来的 copy地址 -(void)touchesBegan:(NSSet<UITouch *> *)touches wi ...

  6. 运维自动化ansible基础

    云计算三种服务架构 IAAS: 不提供OS  只购买硬件(网络,存储,计算) PAAS: 提供硬件和OS和开发和运行环境  只需要开发应用软件 SAAS: 提供 硬件 os 软件   相当于直接购买软 ...

  7. 简明 ASP.NET Core 手册2018

    https://windsting.github.io/little-aspnetcore-book/book/ 中文版 https://nbarbettini.gitbooks.io/little- ...

  8. vuex操作

    import Vuex from 'vuex' //引入Vue.use(Vuex) //加载到Vue中//创建一个数据存储对象var store=new Vuex.Store({ //state可以当 ...

  9. [vue]webpack中使用组件

    https://blog.csdn.net/ywl570717586/article/details/79984909 vue.js中全局组件 全局组件的定义 <!DOCTYPE html> ...

  10. [py]super调用父类的方法---面向对象

    super()用于调用父类方法 http://www.runoob.com/python/python-func-super.html super() 函数是用于调用父类(超类)的一个方法. clas ...