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爬虫代理IP池实现方法
Jan 05 Python
Python matplotlib绘图可视化知识点整理(小结)
Mar 16 Python
Python爬虫工程师面试问题总结
Mar 22 Python
基于Python Numpy的数组array和矩阵matrix详解
Apr 04 Python
Python 实现取矩阵的部分列,保存为一个新的矩阵方法
Nov 14 Python
Python3.6.x中内置函数总结及讲解
Feb 22 Python
Python之修改图片像素值的方法
Jul 03 Python
3行Python代码实现图像照片抠图和换底色的方法
Oct 10 Python
python super用法及原理详解
Jan 20 Python
python GUI库图形界面开发之PyQt5多行文本框控件QTextEdit详细使用方法实例
Feb 28 Python
python学习笔记之多进程
Aug 06 Python
python map比for循环快在哪
Sep 21 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
PHP 输出缓存详解
2009/06/20 PHP
php中$_SERVER[PHP_SELF] 和 $_SERVER[SCRIPT_NAME]之间的区别
2009/09/05 PHP
php递归获取目录内文件(包含子目录)封装类分享
2013/12/25 PHP
php sybase_fetch_array使用方法
2014/04/15 PHP
php计算两个日期相差天数的方法
2015/03/14 PHP
PHP针对redis常用操作实例详解
2019/08/17 PHP
laravel5 Eloquent 实现事务方式
2019/10/21 PHP
escape、encodeURI、encodeURIComponent等方法的区别比较
2006/12/27 Javascript
解决jquery的datepicker的本地化以及Today问题
2012/05/23 Javascript
Bootstrap每天必学之警告框插件
2016/04/26 Javascript
JS如何判断json是否为空
2016/07/06 Javascript
浅谈JS使用[ ]来访问对象属性
2016/09/21 Javascript
js实现日历的简单算法
2017/01/24 Javascript
基于js 本地存储(详解)
2017/08/16 Javascript
微信小程序修改checkbox的样式代码实例
2020/01/21 Javascript
[48:48]VGJ.T vs Liquid 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
python编写弹球游戏的实现代码
2018/03/12 Python
Python中%是什么意思?python中百分号如何使用?
2018/03/20 Python
Python3实现从排序数组中删除重复项算法分析
2019/04/03 Python
python 模拟贷款卡号生成规则过程解析
2019/08/30 Python
通过 Django Pagination 实现简单分页功能
2019/11/11 Python
利用Python脚本批量生成SQL语句
2020/03/04 Python
django项目中新增app的2种实现方法
2020/04/01 Python
python matplotlib工具栏源码探析三之添加、删除自定义工具项的案例详解
2021/02/25 Python
Data URI scheme详解和使用实例及图片base64编码实现方法
2014/05/08 HTML / CSS
新加坡最受追捧的体验平台:Hapz
2018/01/01 全球购物
39美元购买一副眼镜或太阳镜:39DollarGlasses.com
2018/06/17 全球购物
Ray-Ban雷朋奥地利官网:全球领先的太阳眼镜品牌
2020/10/12 全球购物
竞聘演讲稿精彩开头和结尾
2014/05/14 职场文书
社会实践活动总结范文
2014/07/03 职场文书
2014机关干部学习“焦裕禄精神”思想汇报
2014/09/19 职场文书
婚礼上证婚人致辞
2015/07/28 职场文书
党组织关系的介绍信模板
2019/06/21 职场文书
Python之基础函数案例详解
2021/08/30 Python
bootstrapv4轮播图去除两侧阴影及线框的方法
2022/02/15 HTML / CSS
Python字符串常规操作小结
2022/04/03 Python