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

@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. php 实现 html转js

    [php] <?php function htmltojs($str){ $re=''; $str=str_replace('\','\\',$str); $str=str_replace(&q ...

  2. Notes from Data Guard

    There are two types of Standby databases: 1, Physical standby database block-for-block basis the phy ...

  3. 也谈隐藏盘符等windows 的管理员的策略实现

    网上的文章都知道在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer 文件夹下有控制隐藏驱动器 ...

  4. 因DataTable的字段值为DBNull引发的异常

    1 问题重现 (1)新建项目DBNullExp.项目属性为"控制台应用程序": (2)在项目下新建数据集Schools(数据集文件的后缀名为.xsd): watermark/2/t ...

  5. YUV格式详细解释与FFMPEG的关系

    YUV主要的采样格式 主要的采样格式有YCbCr 4:2:0.YCbCr 4:2:2.YCbCr 4:1:1和 YCbCr 4:4:4.其中YCbCr 4:1:1 比较常用,其含义为:每个点保存一个 ...

  6. DatabaseFactory.CreateDatabase 方法操作数据库

    using Microsoft.Practices.EnterpriseLibrary.Data;using Microsoft.Practices.EnterpriseLibrary.Data.Sq ...

  7. AES中几种加密模式的区别:ECB、CBC、CFB、OFB、CTR

    AES: aes是基于数据块的加密方式,也就是说,每次处理的数据时一块(16字节),当数据不是16字节的倍数时填充,这就是所谓的分组密码(区别于基于比特位的流密码),16字节是分组长度 分组加密的几种 ...

  8. js中级四: 跨域

    原文链接:http://www.cnblogs.com/scottckt/archive/2011/11/12/2246531.html 什么是跨域? 首先什么是跨域,简单地理解就是因为JavaScr ...

  9. ajax请求后台返回map类型并如何展示

    前台jsp或者ftl文件接收返回结果: <input type="hidden" name="selectedModelListStr" id=" ...

  10. GoWeb编程之HelloWorld

    使用Go实现我们的第一个Web服务器程序 package main import "net/http" import "fmt" //打印HelloWorld ...