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. Classic BAdi and New BAdi

    Former Member Classic BAdi and New BAdi ... 2007年04月27日 04:43 | 1.5k Views Hi all, I have a question ...

  2. postgrelsql base64加密,JS base64解密

    项目中做云桌面对接,需要在项目中查出用户的明文密码,拼接到云桌面登陆地址中,防止明文传输,做了base64加密解密,防止小白黑客盗取用户密码. postgrelsql base64加密SQL语句: 查 ...

  3. redis相关文章

    redis主从复制相关文章    <redis如何实现主从数据的同步>      <一篇文章让你明白Redis主从同步>      <redis-sentinel的理解实 ...

  4. PowerDNS + PowerDNS-Admin

    一.基础配置 1.1 环境说明 Centos 7.5.1804 PDNS MariaDB 1.2 关闭防火墙和 selinux setenforce sed -i 's/SELINUX=enforci ...

  5. Oracle RAC 创建实例出错(非+DATA目录)的简单处理

    今天进行oracle的rac测试 发现开发同事没有写好 oracle rac的设置.  创建完之后就会报错了 因为自己对oracle 的RAC 不太熟悉 不太会用.. 所以用 一个比较简单的办法. a ...

  6. Java基础知识点总结(二)

    “https://www.runoob.com/java” 1.Java运算符优先级 2.内置数据类型的包装类 抽象类Number是所有包装类(Integer.Long.Byte.Double.Flo ...

  7. C++ 工程师养成 每日一题third (子数列排序)

    题目: 定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的.牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数 ...

  8. c++11多线程记录6:条件变量(condition variables)

    https://www.youtube.com/watch?v=13dFggo4t_I视频地址 实例1 考虑这样一个场景:存在一个全局队列deque,线程A向deque中推入数据(写),线程B从deq ...

  9. day19——包、logging日志

    day19 包 文件夹下具有______init______.py文件就是一个包 方法 import 包.包.包 from 包.包.包 import 模块 需要在______init______.py ...

  10. Go基础编程实践(七)—— 并发

    同时运行多个函数 观察常规代码和并发代码的输出顺序. // 常规代码,顺序执行,依次输出 package main import ( "fmt" "time" ...