python实现装饰器、描述符


Posted in Python onFebruary 28, 2018

概要

本人python理论知识远达不到传授级别,写文章主要目的是自我总结,并不能照顾所有人,请见谅,文章结尾贴有相关链接可以作为补充

全文分为三个部分装饰器理论知识、装饰器应用、装饰器延申

  • 装饰理基础:无参装饰器、有参装饰器、functiontools、装饰器链
  • 装饰器进阶:property、staticmethod、classmethod源码分析(python代码实现)

装饰器基础

无参装饰器

'''
假定有一个需求是:打印程序函数运行顺序
此案例打印的结果为:
  foo1 function is starting
  foo2 function is starting
'''
from functools import wraps

def NoParamDec(func):
  #函数在被装饰器装时后,其函数属性也会改变,wraps作用就是保证被装饰函数属性不变
  @wraps(func)
  def warpper(*args, **kwargs):
    print('{} function is starting'.format(func.__name__))
    return func(*args, **kwargs)
  
  return warpper

#python黑魔法省略了NoParamDec=NoParamDec(foo1)
@NoParamDec
def foo1():
  foo2()

@NoParamDec
def foo2():
  pass

if __name__ == "__main__":

  foo1()

有参装饰器

'''
假定有一个需求是:检查函数参数的类型,只允许匹配正确的函数通过程序
此案例打印结果为:
('a', 'b', 'c')
-----------------------分割线------------------------
ERROS!!!!b must be <class 'str'> 
ERROS!!!!c must be <class 'str'> 
('a', 2, ['b', 'd'])

  
'''
from functools import wraps
from inspect import signature


def typeAssert(*args, **kwargs):
  deco_args = args
  deco_kwargs = kwargs
  
  def factor(func):
    #python标准模块类,可以用来检查函数参数类型,只允许特定类型通过
    sig = signature(func)
    #将函数形式参数和规定类型进行绑定
    check_bind_args = sig.bind_partial(*deco_args, **deco_kwargs).arguments
    
    @wraps(func)
    def wrapper(*args, **kwargs):
      #将实际参数值和形式参数进行绑定
      wrapper_bind_args = sig.bind(*args, **kwargs).arguments.items()
      for name, obj in wrapper_bind_args:
        #遍历判断是否实际参数值是规定参数的实例
        if not isinstance(obj, check_bind_args[name]):
          try:
            raise TypeError('ERROS!!!!{arg} must be {obj} '.format(**{'arg': name, 'obj': check_bind_args[name]}))
          except Exception as e:
            print(e)
      return func(*args, **kwargs)
    
    return wrapper
  
  return factor

@typeAssert(str, str, str)
def inspect_type(a, b, c):
  return (a, b, c)

if __name__ == "__main__":
  print(inspect_type('a', 'b', 'c'))
  print('{:-^50}'.format('分割线'))
  print(inspect_type('a', 2, ['b', 'd']))

装饰器链

'''
假定有一个需求是:
输入类似代码:
@makebold
@makeitalic
def say():
  return "Hello"

输出:
<b><i>Hello</i></b>
'''
from functools import wraps

def html_deco(tag):
  def decorator(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
      return '<{tag}>{fn_result}<{tag}>'.format(**{'tag': tag, 'fn_result': fn(*args, **kwargs)})
    
    return wrapped
  
  return decorator

@html_deco('b')
@html_deco('i')
def greet(whom=''):
  # 等价于 geet=html_deco('b')(html_deco('i)(geet))
  return 'Hello' + (' ' + whom) if whom else ''

if __name__ == "__main__":
  print(greet('world')) # -> <b><i>Hello world</i></b>

装饰器进阶

property 原理

通常,描述符是具有“绑定行为”的对象属性,其属性访问已经被描述符协议中的方法覆盖。这些方法是__get__()、__set__()和__delete__()。如果一个对象定义这些方法中的任何一个,它被称为一个描述符。如果对象定义__get__()和__set__(),则它被认为是数据描述符。仅定义__get__()的描述器称为非数据描述符(它们通常用于方法,但是其他用途也是可能的)。

属性查找优先级为:

  • 类属性
  • 数据描述符
  • 实例属性
  • 非数据描述符
  • 默认为__getattr__()
class Property(object):
  '''
  内部property是用c实现的,这里用python模拟实现property功能
  代码参考官方doc文档
  '''

  def __init__(self, fget=None, fset=None, fdel=None, doc=None):
    self.fget = fget
    self.fset = fset
    self.fdel = fdel
    self.__doc__ = doc

  def __get__(self, obj, objtype=None):
    if obj is None:
      return self
    if self.fget is None:
      raise (AttributeError, "unreadable attribute")
    print('self={},obj={},objtype={}'.format(self,obj,objtype))
    return self.fget(obj)

  def __set__(self, obj, value):
    if self.fset is None:
      raise (AttributeError, "can't set attribute")
    self.fset(obj, value)

  def __delete__(self, obj):
    if self.fdel is None:
      raise (AttributeError, "can't delete attribute")
    self.fdel(obj)

  def getter(self, fget):
    return type(self)(fget, self.fset, self.fdel, self.__doc__)

  def setter(self, fset):
    return type(self)(self.fget, fset, self.fdel, self.__doc__)

  def deleter(self, fdel):
    return type(self)(self.fget, self.fset, fdel, self.__doc__)


class Student( object ):
  @Property
  def score( self ):
    return self._score
  @score.setter
  def score( self, val ):
    if not isinstance( val, int ):
      raise ValueError( 'score must be an integer!' )
    if val > 100 or val < 0:
      raise ValueError( 'score must between 0 ~ 100!' )
    self._score = val


if __name__ == "__main__":
  s = Student()
  s.score = 60  
  s.score

staticmethod 原理

@staticmethod means: when this method is called, we don't pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can't access the instance of that class (this is useful when your method does not use the instance).

class StaticMethod(object):
  "python代码实现staticmethod原理"
  
  def __init__(self, f):
    self.f = f
  
  def __get__(self, obj, objtype=None):
    return self.f


class E(object):
  #StaticMethod=StaticMethod(f)
  @StaticMethod
  def f( x):
    return x

if __name__ == "__main__":
  print(E.f('staticMethod Test'))

classmethod

@staticmethod means: when this method is called, we don't pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can't access the instance of that class (this is useful when your method does not use the instance).

class ClassMethod(object):
  "python代码实现classmethod原理"
  
  def __init__(self, f):
    self.f = f
  
  def __get__(self, obj, klass=None):
    if klass is None:
      klass = type(obj)
    
    def newfunc(*args):
      return self.f(klass, *args)
    
    return newfunc
  
class E(object):
  #ClassMethod=ClassMethod(f)
  @ClassMethod
  def f(cls,x):
    return x
  
if __name__ == "__main__":
  print(E().f('classMethod Test'))
Python 相关文章推荐
详解python中的装饰器
Jul 10 Python
详解DeBug Python神级工具PySnooper
Jul 03 Python
pytorch打印网络结构的实例
Aug 19 Python
Python3.6+selenium2.53.6自动化测试_读取excel文件的方法
Sep 06 Python
python 的 openpyxl模块 读取 Excel文件的方法
Sep 09 Python
PyCharm如何导入python项目的方法
Feb 06 Python
pyspark给dataframe增加新的一列的实现示例
Apr 24 Python
2020最新pycharm汉化安装(python工程狮亲测有效)
Apr 26 Python
基于nexus3配置Python仓库过程详解
Jun 15 Python
Python smtp邮件发送模块用法教程
Jun 15 Python
基于Python 的语音重采样函数解析
Jul 06 Python
利用Python将图片中扭曲矩形的复原
Sep 07 Python
python安装教程
Feb 28 #Python
深入理解Python 关于supper 的 用法和原理
Feb 28 #Python
TensorFlow实现RNN循环神经网络
Feb 28 #Python
python使用TensorFlow进行图像处理的方法
Feb 28 #Python
使用Python搭建虚拟环境的配置方法
Feb 28 #Python
Python OpenCV获取视频的方法
Feb 28 #Python
python实现多线程行情抓取工具的方法
Feb 28 #Python
You might like
实现了一个PHP5的getter/setter基类的代码
2007/02/25 PHP
phpstrom使用xdebug配置方法
2013/12/17 PHP
PHP的伪随机数与真随机数详解
2015/05/27 PHP
Laravel 5.5 异常处理 &amp; 错误日志的解决
2019/10/17 PHP
Avengerls vs KG BO3 第一场2.18
2021/03/10 DOTA
jQuery建立一个按字母顺序排列的友好页面索引(兼容IE6/7/8)
2013/02/26 Javascript
jQuery图片轮播的具体实现
2013/09/11 Javascript
使用js在页面中绘制表格核心代码
2013/09/16 Javascript
JQuery 使用attr方法实现下拉列表选中
2014/10/13 Javascript
javascript模拟评分控件实现方法
2015/05/13 Javascript
使用Node.js配合Nginx实现高负载网络
2015/06/28 Javascript
详解js数组的完全随机排列算法
2016/12/16 Javascript
详解从Node.js的child_process模块来学习父子进程之间的通信
2017/03/27 Javascript
Vue.js实现价格计算器功能
2020/03/30 Javascript
Vue中的$set的使用实例代码
2018/10/08 Javascript
基于vue的验证码组件的示例代码
2019/01/22 Javascript
js实现多张图片每隔一秒切换一张图片
2019/07/29 Javascript
vue中nextTick用法实例
2019/09/11 Javascript
基于p5.js 2D图像接口的扩展(交互实现)
2020/11/30 Javascript
[01:05:07]DOTA2-DPC中国联赛 正赛 DLG vs Dragon BO3 第一场2月1日
2021/03/11 DOTA
python logging类库使用例子
2014/11/22 Python
python使用pil生成缩略图的方法
2015/03/26 Python
Python实现字典依据value排序
2016/02/24 Python
python微信跳一跳系列之棋子定位颜色识别
2018/02/26 Python
Anaconda入门使用总结
2018/04/05 Python
VScode编写第一个Python程序HelloWorld步骤
2018/04/06 Python
解决python给列表里添加字典时被最后一个覆盖的问题
2019/01/21 Python
PyQt5实现QLineEdit添加clicked信号的方法
2019/06/25 Python
详解用pyecharts Geo实现动态数据热力图城市找不到问题解决
2019/06/26 Python
关于阿里云oss获取sts凭证 app直传 python的实例
2019/08/20 Python
Python urllib3软件包的使用说明
2020/11/18 Python
css3学习之2D转换功能详解
2016/12/23 HTML / CSS
化学系大学生自荐信范文
2014/03/01 职场文书
公务员党的群众路线教育实践活动学习心得体会
2014/10/30 职场文书
javascript实现计算器功能详解流程
2021/11/01 Javascript
Java线程的6种状态与生命周期
2022/05/11 Java/Android