在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样:

@typeassert(int, int)

... def add(x, y):

...     return x + y

add(2, 'hello')
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "contract.py", line 33, in wrapper
TypeError: Argument y must be <class 'int'>
 

我们可以自己实现这样一个装饰器。首先介绍下inspect.signature

sig=signature(test_func)

print(sig)

print(sig.parameters)运行结果:

(x, y, z=42)

OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y">), ('z', <Parameter "z=42">)])signature方法将函数的参数签名信息转换成一个可调用对象

我们还可以使用sig.bind_partial()方法来执行从指定类型到名称的部分绑定。比如下面的绑定,这里将z这个参数绑定成了字节形式。返回结果是一个有序字典,这个字典会将参数名以函数签名中相同顺序映射到指定的类型值上面去

bound_types=sig.bind_partial(int,z=bytes)

print(bound_types.arguments)

OrderedDict([('x', <class 'int'>), ('z', <class 'bytes'>)])

另外一个是sig.bind方法。bind不允许忽略任何参数。Bind会将参数的值和变量名称绑定在一起,通过遍历就可以得出参数的名称和值。

bound_values=sig.bind(1,2,3)

print(bound_values)

for name,value in bound_values.arguments.items():

print(name,value)

运行结果:

<BoundArguments (x=1, y=2, z=3)>

x 1

y 2

z 3

通过bind_partial和bind方法我们就可以做到对参数的检查。bind_partial来对参数类型做强制绑定,然后用bind方法遍历出所有的参数进行参数核查。方法如下。

for name, value in bound_values.arguments.items():

if name in bound_types:

if not isinstance(value, bound_types[name]):

raise TypeError(

'Argument {} must be {}'.format(name, bound_types[name])

)

装饰器的代码如下:

def typeassert(*ty_args, **ty_kwargs):

def decorate(func):

# If in optimized mode, disable type checking

if not __debug__:

return func

# Map function argument names to supplied types

sig = signature(func)

bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments

@wraps(func)

def wrapper(*args, **kwargs):

bound_values = sig.bind(*args, **kwargs)

# Enforce type assertions across supplied arguments

for name, value in bound_values.arguments.items():

if name in bound_types:

if not isinstance(value, bound_types[name]):

raise TypeError(

'Argument {} must be {}'.format(name, bound_types[name])

)

return func(*args, **kwargs)

return wrapper

return decorate

@typeassert(int,z=int)

def para_check(x,y,z=3):

print(x,y,z)

if __name__=="__main__":

para_check(1,2,'str')

运行结果:由于z这个参数赋的是一个字符,因此抛出异常。

Traceback (most recent call last):

File "D:/py_prj/test2/cookbook.py", line 266, in <module>

para_check(1,2,'str')

File "D:/py_prj/test2/cookbook.py", line 254, in wrapper

'Argument {} must be {}'.format(name, bound_types[name])

TypeError: Argument z must be <class 'int'>

python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查的更多相关文章

  1. python cookbook第三版学习笔记二十:可自定义属性的装饰器

    在开始本节之前,首先介绍下偏函数partial.首先借助help来看下partial的定义 首先来说下第一行解释的意思: partial 一共有三个部分: (1)第一部分也就是第一个参数,是一个函数, ...

  2. python cookbook第三版学习笔记二:字典

    一般来说字典中是一个键对应一个单值的映射,如果想一个键值映射多个值,那么就需要将这些值放到另外的容器中,比如列表或者集合. 比如d={'a':[1,2]} Collections中的defaultdi ...

  3. python cookbook第三版学习笔记十九:未包装的函数添加参数

    比如有下面如下的代码,每个函数都需要判断debug的是否为True,而默认的debug为False def a(x,debug=False): if debug: print('calling a') ...

  4. python cookbook第三版学习笔记十:类和对象(一)

    类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair:     def __init__(self,x,y):         self.x=x         self. ...

  5. python cookbook第三版学习笔记十六:抽象基类

    假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...

  6. python cookbook第三版学习笔记十四:类和对象(五)代理类以及内存回收

    代理类: 代理类的作用其实有继承有些类似,如果你想将某个实例的属性访问代理到内部另外一个实例中去,可以用继承也可以用代理.来看下代理的应用: class A:     def spam(self,x) ...

  7. python cookbook第三版学习笔记九:函数

    接受任意数量参数的函数. 当传入函数的参数个数很多的时候,在函数定义的时候不需要为每一个参数定义一个变量,可以用*rest的方式来包含多余的参数. 如下面的代码,*rest包含了2,3,4这3个参数. ...

  8. python cookbook第三版学习笔记十一:类和对象(二)调用父类的方法

    在子类中调用父类的方法,可以下面的A.spam(self)的方法. class A(object):     def spam(self):         print 'A.spam' class ...

  9. python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性

    先介绍几个类中的应用__getattr__,__setattr__,__get__,__set__,__getattribute__,. __getattr__:当在类中找不到attribute的时候 ...

随机推荐

  1. Python技术公众号100天了

    公众号100天了,是个值得一提的日子! 我从2017年10月31日开始做这个公众号,到今天2018年2月7日,差不多100天时间 .虽然公众号很早就申请了,但直到去年10月31日,我才有真正把这个公众 ...

  2. EFM8单片机与I2C外设通信

    近期帮同学做一个项目,开发板是EFM8单片机,支持SPI和I2C协议(SMBus).非常久没搞过单片机了,并且条件限制,为了使单片机和外设成功通信.花了一个星期时间.刚開始使用SPI.发现代码逻辑都没 ...

  3. Mysql又一次整理笔记--woods备忘

    ==============================SQL备忘 CRUD 查询 多表 事件等=============================== ------------------ ...

  4. MySQL:cannot allocate the memory for the buffer pool

    InnoDB: The InnoDB memory heap is disabled InnoDB: Mutexes and rw_locks use GCC atomic builtins Inno ...

  5. Android学习之ItemTouchHelper实现RecylerView的拖拽以及滑动删除功能

    今天在群里见大神们提到控件的拖动以及滑动删除的效果实现,就在网上找了资料ItemTouchHelper学习,并实现其功能.不胜窃喜之至,忍不住跟大家分享一下,如今就对学习过程做下简介.帮助大家实现这样 ...

  6. gstreamer 10.5版本发布啦

  7. 【转】Monkey测试5-运行中停止monkey

    停止monkey自动测试步骤: 1.ps命令  查找uiautomator的进程 打开cmd命令行窗口 输入: adb shell ; ps | grep monkey; 返回来的第一个数字,即是mo ...

  8. jQuery一步一步实现跨浏览器的可编辑表格,支持IE、Firefox、Safari、

    脚 本 之 家 www.jb51.net 脚本云 专题 素材下载 电子书 软件下载 源码下载 服务器常用软件 a5交易 首页 网页制作 脚本专栏 脚本下载 网络编程 数据库 CMS教程 电子书籍 平面 ...

  9. Spring MVC列表多选框

    以下示例显示如何在使用Spring Web MVC框架的表单中使用列表框(Listbox).首先使用Eclipse IDE来创建一个WEB工程,实现一个让用户可选择自己所善长的技术(多选)的功能.并按 ...

  10. java算法学习

    最大公约数 欧几里得算法 描述:计算两个非负整数p和q的最大公约数: 若q是0,则最大公约数为p. 否则,将p除以q得到余数r,p和q的最大公约数即为q和r的最大公约数. 根据算法的自然描述,我们可以 ...