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 07 Python
python中常用的九种预处理方法分享
Sep 11 Python
Python实现去除列表中重复元素的方法小结【4种方法】
Apr 27 Python
flask入门之文件上传与邮件发送示例
Jul 18 Python
Window环境下Scrapy开发环境搭建
Nov 18 Python
解决Pycharm后台indexing导致不能run的问题
Jun 27 Python
Django之模板层的实现代码
Sep 09 Python
python如何通过pyqt5实现进度条
Jan 20 Python
Python 改变数组类型为uint8的实现
Apr 09 Python
Python实现Word表格转成Excel表格的示例代码
Apr 16 Python
PyQt5结合matplotlib绘图的实现示例
Sep 15 Python
Python学习之time模块的基本使用
Jan 17 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
获得Google PR值的PHP代码
2007/01/28 PHP
PHP笔记之:基于面向对象设计的详解
2013/05/14 PHP
php 注册时输入信息验证器的实现详解
2013/07/05 PHP
PHP迭代器的内部执行过程详解
2013/11/12 PHP
php中将一个对象保存到Session中的方法
2015/03/13 PHP
win7系统配置php+Apache+mysql环境的方法
2015/08/21 PHP
thinkPHP导出csv文件及用表格输出excel的方法
2015/12/30 PHP
JavaScript 对象模型 执行模型
2010/10/15 Javascript
JavaScript获取和设置CheckBox状态的简单方法
2013/07/05 Javascript
window.location.href = window.location.href 跳转无反应 a超链接onclick事件写法
2013/08/21 Javascript
Node.js开发指南中的简单实例(mysql版)
2013/09/17 Javascript
javascript中的if语句使用介绍
2013/11/20 Javascript
javascript中Date对象的getDay方法使用指南
2014/12/22 Javascript
js使用onmousemove和onmouseout获取鼠标坐标的方法
2015/03/31 Javascript
Jquery实现的简单轮播效果【附实例】
2016/04/19 Javascript
Bootstrap教程JS插件滚动监听学习笔记分享
2016/05/18 Javascript
AngularJS基础 ng-readonly 指令简单示例
2016/08/02 Javascript
jQuery模拟完美实现经典FLASH导航动画效果【附demo源码下载】
2016/11/09 Javascript
详解vue-cli快速构建项目以及引入bootstrap、jq
2017/05/26 Javascript
JavaScript常用数组操作方法,包含ES6方法
2020/05/10 Javascript
angular4应用中输入的最小值和最大值的方法
2019/05/17 Javascript
Vue实现星级评价效果实例详解
2019/12/30 Javascript
js实现div色块拖动录制
2020/01/16 Javascript
[02:53]DOTA2英雄昆卡基础教程
2013/11/25 DOTA
Python警察与小偷的实现之一客户端与服务端通信实例
2014/10/09 Python
Django基础之Model操作步骤(介绍)
2017/05/27 Python
详解Python进阶之切片的误区与高级用法
2018/12/24 Python
Python实例方法、类方法、静态方法的区别与作用详解
2019/03/25 Python
安装完Python包然后找不到模块的解决步骤
2020/02/13 Python
Django Form常用功能及代码示例
2020/10/13 Python
丹尼尔惠灵顿手表天猫官方旗舰店:Daniel Wellington
2017/08/25 全球购物
英国设计师珠宝网站:Joshua James Jewellery
2020/03/01 全球购物
服装发布会策划方案
2014/05/22 职场文书
合同范本之电脑出租
2019/08/13 职场文书
CSS实现多个元素在盒子内两端对齐效果
2021/03/30 HTML / CSS
Python实现数据的序列化操作详解
2022/07/07 Python