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

@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. nginx 根据域名反向代理

    #nginx 反向代理域名,会根据启动时候解析出来的ip,加载到内存中,后面不会改变可以绑定hosts或者指定resolve服务器来固定ip #注意proxy最后的斜杠,如果不加会将请求过来的目录带到 ...

  2. 未能加载文件或程序集“WebGrease, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

    方法一:在web.config的configuration接点中添加,最好是添加在configuration节点的最后 <runtime> <assemblyBinding xmln ...

  3. CREATE SEQUENCE添加自增序列及NEXT VALUE FOR返回序列号

    From :https://msdn.microsoft.com/zh-cn/library/ff878091.aspx 语法: CREATE SEQUENCE [schema_name . ] se ...

  4. Xilinx DDR3 IP核使用问题汇总(持续更新)和感悟

    一度因为DDR3的IP核使用而发狂. 后来因为解决问题,得一感悟.后面此贴会完整讲述ddr3 ip的使用.(XILINX K7) 感悟:对于有供应商支持的产品,遇到问题找官方的流程.按照官方的指导进行 ...

  5. Skyscrapers Aren’t Scalable

     Skyscrapers Aren't Scalable Michael Nygard WE oFTEn HEAR SoFTWARE EnginEERing CoMpAREd to building ...

  6. [c#.Net] NUnit Test 体验

    NUit Test Adapter 安装,两种方式 第一种:从扩展和更新里搜索nunit,进行下载安装

  7. angular总结控制器的三种主要职责: 为应用中的模型设置初始状态 通过$scope对象把数据模型或函数行为暴露给视图 监视模型的变化,做出相应的动作

    m1.双向数据绑定: <body> <div ng-app ng-init="user.name='world'"> <h1>使用NG实现双边数 ...

  8. 使用pycharm手动搭建python语言django开发环境(三) 使用django的apps应用 添加应用静态文件

    1)在django 工程目录中使用cmd命令行 敲入"python manage.py startapp app名称(例子为blog)" 2)在django工程目录中应该生成了bl ...

  9. plsql programming 12 集合(忽略, 个人感觉用不到)

    关联数组, 嵌套表, varray 个人并不推荐使用集合, 因为操作有别于普通字段. 集合中每一个元素的数据类型都是相同的, 因此这些元素都是同质的(同质元素) 这一章的内容先忽略吧, 因为个人感觉用 ...

  10. response.sendRedirect 的功能是地址重定向(页面跳转)

    response.sendRedirect 的功能是地址重定向(页面跳转) 1.response.sendredirect(url); 新的页面并不能处理旧页面的pagecontext(request ...