Pthon魔术方法(Magic Methods)-描述器

                           作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.描述器概述

1>.描述器定义

Python中,一个类实现了"__get__","__set__","__delete__"三个方法中的任何一个方法,就是描述器。

实现着三个中的某些方法,就支持了描述器协议:
  仅实现了"__get__",就是非数据描述器,即non-data descriptor
  实现了"__get__","__set__"就是数据描述器,即data descriptor
  "__delete__"方法有同样的效果,有了这个方法,也是数据描述器。 如果一个类属性设置为描述器实例,那么它被称为owner属主。 当该类的该类属性被查找,设置,删除时,就会调用描述器相应的方法。

2>.非数据描述器案例

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class A:
def __init__(self):
self.a1 = "a1"
print("A.init") def __get__(self, instance, owner):
"""
因为定义了"__get__"方法,类A就是一个描述器,使用类B或者类B的实例来对x属性读取,就是对类A的实例的访问,就会调用"__get__"方法
参数说明如下:
self指代当前实例对象,即调用者,在本例中它对应的是A的实例
instance是owner的实例,在本例中它的值可能有两种:
None表示不是B类的实例,对应调用B.x
<__main__.B object at 0x00000284CBF675C8>表示是B的实例,对应调用B().x
owner是属性的所属的类
"""
print("A.__get__ {} {} {}".format(self,instance,owner))
return self class B:
x = A() #此时我们可以说x是非数据描述器,因为A()类中仅实现了"__get__"方法
def __init__(self):
print("B.init")
self.x = "b.x" #增加实例属性"x",由于这里的实例属性名称和上面的非数据描述器名称一致,此时赋值即定义,实例x变量会将类中的描述器标识符覆盖(因为A类中没有"__set__"方法可调用)。 print("-" * 20)
print(B.x)
print(B.x.a1) print("=" * 20)
b = B()
print(b.x)
# print(b.x.a1) #由于此时"b.x"访问到的是实例的属性,而不是非数据描述器,因此报错"AttributeError: 'str' object has no attribute 'a1'"
A.init
--------------------
A.__get__ <__main__.A object at 0x000001536AD65588> None <class '__main__.B'>
<__main__.A object at 0x000001536AD65588>
A.__get__ <__main__.A object at 0x000001536AD65588> None <class '__main__.B'>
a1
====================
B.init
b.x

以上代码执行结果戳这里

3>.数据描述器案例

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class A:
def __init__(self):
self.a1 = "a1"
print("A.init") def __get__(self, instance, owner):
"""
因为定义了"__get__"方法,类A就是一个描述器,使用类B或者类B的实例来对x属性读取,就是对类A的实例的访问,就会调用"__get__"方法
"""
print("A.__get__ {} {} {}".format(self,instance,owner))
return self def __set__(self, instance, value):
"""
因为同时定义了"__get__"和"__set__"方法,类A就是一个数据描述器,
"""
print("A.__set__ {} {} {}".format(self,instance,value))
self.data = value class B:
x = A()            #这里就是一个数据描述器
def __init__(self):
print("B.init")
self.x = "b.x" #增加实例属性"x",由于这里的实例属性名称和上面的数据描述器名称一致,因此会调用上面的"__set__"方法 print("-" * 20)
print(B.x)
print(B.x.a1) print("=" * 20)
b = B()
print(b.x)
print(b.x.a1)
print(b.x.data)
b.x = 100 #这是调用数据描述器的"__set__"方法,或调用非数据描述器的实例覆盖
print(b.x)
B.x = 600 #赋值即定义,这是覆盖类属性,把描述器给替换了
print(B.x)
print(b.__dict__)
print(B.__dict__)
A.init
--------------------
A.__get__ <__main__.A object at 0x000001D054546708> None <class '__main__.B'>
<__main__.A object at 0x000001D054546708>
A.__get__ <__main__.A object at 0x000001D054546708> None <class '__main__.B'>
a1
====================
B.init
A.__set__ <__main__.A object at 0x000001D054546708> <__main__.B object at 0x000001D054546808> b.x
A.__get__ <__main__.A object at 0x000001D054546708> <__main__.B object at 0x000001D054546808> <class '__main__.B'>
<__main__.A object at 0x000001D054546708>
A.__get__ <__main__.A object at 0x000001D054546708> <__main__.B object at 0x000001D054546808> <class '__main__.B'>
a1
A.__get__ <__main__.A object at 0x000001D054546708> <__main__.B object at 0x000001D054546808> <class '__main__.B'>
b.x
A.__set__ <__main__.A object at 0x000001D054546708> <__main__.B object at 0x000001D054546808> 100
A.__get__ <__main__.A object at 0x000001D054546708> <__main__.B object at 0x000001D054546808> <class '__main__.B'>
<__main__.A object at 0x000001D054546708>
600
{}
{'__module__': '__main__', 'x': 600, '__init__': <function B.__init__ at 0x000001D054535438>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}

以上代码执行结果戳这里

4>.属性查找顺序

  实例的"__dict__"优先于非数据描述器

  数据描述器优先于实例的"__dict__"

5>.新增方法

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class A:
def __init__(self):
print("A.init") def __get__(self, instance, owner):
print(1,self,instance,owner)
return self def __set_name__(self, owner, name):
"""
提供在这个方法,就是可以知道主类和属主类的类属性名。
"""
print(2,self,owner,name)
self.name = name class B:
test_name = A() #类属性创建时调用描述器的"__set_name__"方法 print("=" * 30)
print(B().test_name.name)
A.init
2 <__main__.A object at 0x000002082F5F5488> <class '__main__.B'> test_name
==============================
1 <__main__.A object at 0x000002082F5F5488> <__main__.B object at 0x000002082F5F5588> <class '__main__.B'>
test_name

以上代码执行结果戳这里

二.Python中的描述器

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class A: def __init__(self): #非数据描述器
self.foo = 100
self.bar = 200
"""
由于foo和bar标识符调用的类装饰器非数据描述器,因此可以自行修改,而test标识符是数据描述器,此处修改会调用
"property"类的"__set__"方法,不允许修改test标识符,因此会报错:"AttributeError: can't set attribute"。
"""
# self.test = 300 def getfoo(self): #非数据描述器
return self.foo @classmethod #非数据描述器
def foo(cls):
pass @staticmethod #非数据描述器
def bar():
pass @property #数据描述器
def test(self):
return 100 a = A()
print(a.__dict__)
print(A.__dict__)
{'foo': 100, 'bar': 200}
{'__module__': '__main__', '__init__': <function A.__init__ at 0x000002ABBE125678>, 'getfoo': <function A.getfoo at 0x000002ABBE1251F8>, 'foo': <classmethod object at 0x000002ABBE136788>, 'bar': <staticmethod object at 0x000002ABBE1367C8>, 'test': <property object at 0x000002ABBE072228>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

以上代码执行结果戳这里

三.小试牛刀

1>.实现StaticMethod装饰器,完成staticmethod装饰器的功能

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class StaticMethod:
def __init__(self,fn):
self.fn = fn def __get__(self, instance, owner):
return self.fn class A:
@StaticMethod
def show():
print("A.show static method") A.show()
A().show() #以上代码执行结果如下:
A.show static method
A.show static method

2>.实现ClassMethod装饰器,完成classmethod装饰器的功能

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie from functools import partial class ClassMethod:
def __init__(self,fn):
self._fn = fn def __get__(self, instance, cls):
res = partial(self._fn,cls)
return res class A:
@ClassMethod
def show(cls):
print(cls.__name__) print(A.__dict__)
A.show
A.show() #以上代码执行结果如下:
{'__module__': '__main__', 'show': <__main__.ClassMethod object at 0x000001BB1C666548>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
A

3>.对实例的数据进行校验

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Person:
def __init__(self,name:str,age:int):
self.name = name
self.age = age
"""
对上面的类的实例属性name,age进行数据校验 思路:
1>.写函数,在__init__中先检查,如果不合格,直接抛异常
2>.装饰器,使用inspect模块完成
3>.描述器
"""
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Person:
def __init__(self,name:str,age:int):
params = ((name,str),(age,int))
if not self.checkdata(params):
raise TypeError("传入参数树类型错误,请检查数据类型,要求传入参数为--->name:str,age:int")
self.name = name
self.age = age def checkdata(self,params):
for param,typ in params:
if not isinstance(param,typ):
return False
return True p1 = Person("Jason Yin","")

写检查函数参考案例(这种方法耦合度太高,不推荐使用)

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import inspect class TypeCheck:
def __init__(self,name,typ):
self.name = name
self.type = typ def __get__(self, instance, owner):
print("TypeCheck.get")
if instance:
return instance.__dict__[self.name]
return self def __set__(self, instance, value):
print("TypeCheck.set")
if not isinstance(value,self.type):
raise TypeError(value)
instance.__dict__[self.name] = value class PropsInject:
def __init__(self,cls):
self.cls = cls
sig = inspect.signature(cls)
params = sig.parameters
for name,parm in params.items():
print(name,parm)
if parm.annotation != parm.empty: #注入类属性
setattr(cls,name,TypeCheck(name,parm.annotation)) def __call__(self, *args, **kwargs):
return self.cls(*args,**kwargs) #新构建一个新的Person对象 @PropsInject
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age print(Person.__dict__)
p1 = Person("Jason Yin",18)
p2 = Person("Jason Yin","")

描述器版本参考案例

Pthon魔术方法(Magic Methods)-描述器的更多相关文章

  1. php中的魔术方法(Magic methods)和魔术常亮

    PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用. 魔术方法包括: __construct(),类的构造函数 __destruct(),类的析构函数 __cal ...

  2. Pthon魔术方法(Magic Methods)-反射

    Pthon魔术方法(Magic Methods)-反射 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.反射概述 运行时,区别于编译时,指的时程序被加载到内存中执行的时候. 反射 ...

  3. Pthon魔术方法(Magic Methods)-上下文管理

    Pthon魔术方法(Magic Methods)-上下文管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.上下文管理方法 __enter__: 进入与此对象相关的上下文.如果 ...

  4. Pthon魔术方法(Magic Methods)-可调用对象

    Pthon魔术方法(Magic Methods)-可调用对象 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.可调用对象方法 __call__: 类中定义一个该方法,实例就可以像 ...

  5. Pthon魔术方法(Magic Methods)-容器相关方法

    Pthon魔术方法(Magic Methods)-容器相关方法 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.容器相关方法汇总 __len__: 内建函数len(),返回对象的 ...

  6. Pthon魔术方法(Magic Methods)-运算符重载

    Pthon魔术方法(Magic Methods)-运算符重载 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Python运算符对应的魔术方法 1>.比较运算符 <: ...

  7. Pthon魔术方法(Magic Methods)-bool

    Pthon魔术方法(Magic Methods)-bool 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.bool方法 __bool__: 内建函数bool(),或者对象放在逻 ...

  8. Pthon魔术方法(Magic Methods)-hash

    Pthon魔术方法(Magic Methods)-hash 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.hash方法 __hash__: 内建函数hash()调用的返回值,返 ...

  9. Pthon魔术方法(Magic Methods)-可视化

    Pthon魔术方法(Magic Methods)-可视化 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.关于可视化的魔术方法简介 __str__: str()函数,format ...

随机推荐

  1. GWAS+自然选择:62个样本的GWAS分析,没信号,如何巧妙的发文章

    欢迎来到"bio生物信息"的世界 6天前,BMC Genomics 推了一篇文献"Population history and genetic adaptation of ...

  2. [转]Gnome桌面的录屏插件easyscreencast

    原文地址:https://www.linuxprobe.com/gnome-easyscreencast.html

  3. 【ML基础】t-SNE(t-distributed stochastic neighbor embedding)原理及推导

    前言 参考 1. t-SNE原理与推导: 完

  4. Kubernetes k8s 基于Docker For Windows

      开启和安装Kubernetes k8s 基于Docker For Windows   0.最近发现,Docker For Windows Stable在Enable Kubernetes这个问题上 ...

  5. 在ensp上的OSPF

    实验模拟 搭建实验拓扑 测试连通性 部署单区域OSPF网络 默认ospf 进程号为1 ,接着使用area命令创建区域并进入ospf区域视图  ,因为是单区域配置,所以使用骨干区域,即0区域 检查osp ...

  6. [转帖]插曲:大白话带你认识Kafka

    插曲:大白话带你认识Kafka 2019-11-18 21:58:27 从事Java 阅读数 2更多 分类专栏: java Kafaka   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA ...

  7. Python3 - Bytes类型

    Python3 新增 bytes 类型,是指一堆字节的集合,十六进制表现形式,两个十六进制数构成一个 byte ,以 b 开头的字符串都是 bytes 类型. 计算机只能存储二进制,字符.图片.视频. ...

  8. Golang-使用mysql

    一.安装mysql-driver驱动 go get github.com/go-sql-driver/mysql 二.安装完毕之后,就可以通过go语言操作mysql了 const ( _selectU ...

  9. 05 Cookie.Session

    Cookie:在浏览器中保存用户的信息   使用:由服务器创建,发送到浏览器保存,之后随着请求发回到服务器 1.创建cookie Cookie cookie = new Cookie("na ...

  10. 【1】TOPK最小的K个数(多种方法比较)

    (头条) 最小的第K个数也是和这题topK一样的思路 1.全排序  时间复杂度O(nlogn) 2.Partiton思想 时间复杂度O(n)  (因为不需要像快排一样对所有的分段都两两Partitio ...