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格式化压缩后的JS文件的方法
Mar 05 Python
详解Python中with语句的用法
Apr 15 Python
python实现彩票系统
Jun 28 Python
python取数作为临时极大值(极小值)的方法
Oct 15 Python
解决python3 pika之连接断开的问题
Dec 18 Python
解决python Markdown模块乱码的问题
Feb 14 Python
Python matplotlib学习笔记之坐标轴范围
Jun 28 Python
解决pycharm中导入自己写的.py函数出错问题
Feb 12 Python
Django数据结果集序列化并展示实现过程
Apr 22 Python
基于K.image_data_format() == 'channels_first' 的理解
Jun 29 Python
Python如何读写二进制数组数据
Aug 01 Python
解决pycharm导入numpy包的和使用时报错:RuntimeError: The current Numpy installation (‘D:\\python3.6\\lib\\site-packa的问题
Dec 08 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
通过ICQ网关发送手机短信的PHP源程序
2006/10/09 PHP
php读取msn上的用户信息类
2008/12/05 PHP
PHPMYADMIN导入数据最大为2M的解决方法
2012/04/23 PHP
解析PHP工厂模式的好处
2013/06/18 PHP
100多行PHP代码实现socks5代理服务器[2]
2016/05/05 PHP
mac系统下为 php 添加 pcntl 扩展
2016/08/28 PHP
PHP批斗大会之缺失的异常详解
2019/07/09 PHP
javascript 网页跳转的方法
2008/12/24 Javascript
JS中动态添加事件(绑定事件)的代码
2011/01/09 Javascript
js 链式延迟执行DOME
2012/01/04 Javascript
JavaScript判断浏览器类型的方法
2015/02/10 Javascript
无刷新上传文件并返回自定义值
2015/06/11 Javascript
详解AngularJS中自定义指令的使用
2015/06/17 Javascript
javascript瀑布流式图片懒加载实例解析与优化
2016/02/23 Javascript
jQuery+CSS3+Html5实现弹出层效果实例代码(附源码下载)
2016/05/16 Javascript
ES6概念 Symbol.keyFor()方法
2016/12/25 Javascript
React服务端渲染原理解析与实践
2021/03/04 Javascript
Python实现的ini文件操作类分享
2014/11/20 Python
Python与Java间Socket通信实例代码
2017/03/06 Python
python如何实现视频转代码视频
2019/06/17 Python
Python下opencv图像阈值处理的使用笔记
2019/08/04 Python
如何在python中写hive脚本
2019/11/08 Python
python获取时间戳的实现示例(10位和13位)
2020/09/23 Python
python 如何在测试中使用 Mock
2021/03/01 Python
一款基于css3的动画按钮代码教程
2014/11/23 HTML / CSS
css3使用animation属性实现炫酷效果(推荐)
2020/02/04 HTML / CSS
美国修容界大佬创建的个人美妆品牌:Kevyn Aucoin Beauty
2018/12/12 全球购物
奥地利票务门户网站:oeticket.com
2019/12/31 全球购物
十佳大学生事迹材料
2014/01/29 职场文书
儿童生日会策划方案
2014/05/15 职场文书
党员个人党性分析材料
2014/12/18 职场文书
会计试用期工作总结2015
2015/05/28 职场文书
2016个人廉洁自律承诺书
2016/03/25 职场文书
公司周年庆寄语
2019/06/21 职场文书
2019年员工晋升管理制度范本!
2019/07/08 职场文书
Vue图片裁剪组件实例代码
2021/07/02 Vue.js