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

@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. centos 7.x systemctl 几个常用的操作

    # 开机启动 systemctl enable nginx # 禁止开机启动 systemctl disable nginx # 开启服务 systemctl start nginx # 停止服务 s ...

  2. 60. Search Insert Position 【easy】

    60. Search Insert Position [easy] Given a sorted array and a target value, return the index if the t ...

  3. 文件上传下载:commons-fileupload + Servlet 2.5

    数据库:MySQL 开发技术:JSP + Servlet 2.5 第三方的上传组件: commons-fileupload connons-io 上传页面1.form表单需要增加:enctype=&q ...

  4. MFC invalidate和RedrawWindow区别

    Invalidate()函数是强制系统进行重画,但是不一定就马上进行重画.因为Invalidate()只是通知系统,此时的窗口已经变为无效.强制系统调用WM_PAINT,而这个消息只是Post就是将该 ...

  5. request:getParameter和getAttribute区别

    getParameter 是用来接受用post个get方法传递过来的参数的.getAttribute 必须先setAttribute.(1)request.getParameter() 取得是通过容器 ...

  6. 个人博客开发之xadmin 布局和后台样式

    项目源码下载:http://download.vhosts.cn 一. xadmin 后台配置注册信息 1. 在apps 的blogs 和 users 两个app中添加adminx.py文件 vim ...

  7. TP3.2 引入微信类

    首先建立一个入口IndexController.php文件 <?php namespace Home\Controller; use Think\Controller; use Com\Wech ...

  8. 从获取点击事件根元素谈 target和currentTarget

    事情由来: 写了一个点击事件,想获取根元素,想的直接用current就行了,因为之前就是这么用的,但是之前的点击元素是没子元素的,current就是根元素,但是这次点击元素内部有子元素,current ...

  9. C2 CompilerThread0 如果抓到的java线程dump里占用CPU最高的线程是这个,99%可能是因为服务重启了

    "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f20c80b3800 nid=0x57c0 runnable ...

  10. vfptr(2)

    //i_vptr struct i_vptr { ; }; //vptr.h #include "i_vptr.h" #include <iostream> class ...