Python之描述器
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之描述器的更多相关文章
- Python 黑魔法 --- 描述器(descriptor)
Python 黑魔法---描述器(descriptor) Python黑魔法,前面已经介绍了两个魔法,装饰器和迭代器,通常还有个生成器.生成器固然也是一个很优雅的魔法.生成器更像是函数的行为.而连接类 ...
- python 简单了解一下 描述器
1.描述器是什么? 在Python中描述器也被称为描述符, 1)描述器实际上是任何新式类(新式类是继承自 type 或者 object 的类),这种类至少实现了3个特殊的方法__get__, __se ...
- python cookbook第三版学习笔记十三:类和对象(三)描述器
__get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...
- Python 面向对象(五) 描述器
使用到了__get__,__set__,__delete__中的任何一种方法的类就是描述器 描述器的定义 一个类实现了__get__,__set__,__delete__中任意一个,这个类就是描述器. ...
- python类:描述器Descriptors和元类MetaClasses
http://blog.csdn.net/pipisorry/article/details/50444769 描述器(Descriptors) 描述器决定了对象属性是如何被访问的.描述器的作用是定制 ...
- python描述器
描述器定义 python中,一个类实现了__get__,__set__,__delete__,三个方法中的任何一个方法就是描述器,仅实现__get__方法就是非数据描述器,同时实现__get__,__ ...
- (转)面向对象(深入)|python描述器详解
原文:https://zhuanlan.zhihu.com/p/32764345 https://www.cnblogs.com/aademeng/articles/7262645.html----- ...
- Python描述器引导(转)
原文:http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html 1. Python描述器引导(翻译) 作者: Raymond ...
- Python入门之面向对象编程(四)Python描述器详解
本文分为如下部分 引言——用@property批量使用的例子来引出描述器的功能 描述器的基本理论及简单实例 描述器的调用机制 描述器的细节 实例方法.静态方法和类方法的描述器原理 property装饰 ...
随机推荐
- Calcite - StreamingSQL
https://calcite.apache.org/docs/stream.html Calcite's SQL is an extension to standard SQL, not ano ...
- Copycat - command
client.submit(new PutCommand("foo", "Hello world!")); ServerContext connection.h ...
- adg的数据传输应用三大模式转换
1.最大可用性模式(Maximum Availability) 1)该模式提供了仅次于"最大保护模式"的数据保护能力: 2)要求至少一个物理备库收到重做日志后,主库的事务才能够提交 ...
- js数组方法汇总
下面主要汇总一下数组的方法 数组方法: 1.检测是否为数组的方法:Array.isArrray(); var arr=[1,2,3,4,5]; var str='string'; console.lo ...
- div总在页面最顶端
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx. ...
- C# Asp.net使用FormData对象实现ajax提交表单及上传图片
1.html代码: <form id="postForm"> 文件名:<input type="text" name="filena ...
- 七、Spring Boot 启动配置原理
几个重要的事件回调机制 配置在META-INF/spring.factories ApplicationContextInitializer SpringApplicationRunListener ...
- Nginx与安全体系架构
- 【JMeter】前置处理器
BeanShell PreProcessor 使用BeanShell在请求进行之前进行操作.语法使用与BeanShell Sampler是一样的.但可使用的内置变量稍有不同 JDBC Pre ...
- 20180824 SSRS Line Chart 绘制
(很多时候我都会只记录遇到的问题点,很少详细的写整个过程) 1. 安装ReportBulider 客户端,不需要填写server url ,可以先放空,后面再维护. 安装包官网可以下载,是免费的,现在 ...