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. Cassandra开发入门文档第四部分(集合类型、元组类型、时间序列、计数列)

    Cassandra 提供了三种集合类型,分别是Set,List,MapSet: 非重复集,存储了一组类型相同的不重复元素,当被查询时会返回排好序的结果,但是内部构成是无序的值,应该是在查询时对结果进行 ...

  2. Elasticsearch探索之路的障碍

    1.   unable to install syscall filter 解决方法: 这个警告主要输因为使用的linux版本过低造成的    暂时不用理会

  3. idea内置tomcat中java代码热更新

    按照上图设置后,然后修改代码后按shift+F9快捷键,即可实现代码更新,这时在debug模式下会看到代码变更后的输出

  4. WeQuant教程—1.2 从简单的量化系统开始

    你大概知道量化的思想最早在古巴比伦人计算行星轨迹的时候就已经诞生(算术运算),后来借助古希腊的形式化逻辑的发展,人们日益能从量化的思想中提炼和描述自然规律并运用到生产之中.不过,基于量化的思想打造一个 ...

  5. Web API之Web Components

    本文参考<你的前端框架要被web组件替代了>. 于2011年面世的Web Components是一套功能组件,让开发者可以使用 HTML.CSS 和 JavaScript 创建可复用的组件 ...

  6. [Py] 简单的 Python 运行环境

    python:https://www.python.org/downloads/ pip:https://pip.pypa.io/en/stable/installing/#upgrading-pip ...

  7. replace into 详解 update mysql

    转replace 与 update 区分本文主要对比一下 Sqlite 中的 replace 语句和 update 语句 . 在本例中使用如下数据库表:   图1 该表的表名为student, 存储学 ...

  8. 【Spring Boot学习之九】缓存支持

    环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2 一.Spring Boot Cache以及整合EhCacheSpring从3.1开始定义了org.springfram ...

  9. Kubernetes 原理架构介绍(一)

    目录 一.Kubernetes 是什么 二.Kubernetes 设计架构 三.Kubernetes的核心技术概念和API对象 Cluster Master Node Pod Controller D ...

  10. ASP.NET Core 中间件Diagnostics使用 异常和错误信息

    ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...