python3装饰符@学习及实现函数输入参数的类型检查

在网上看了几篇文章,要么解释不清,要么代码运行不了。综合几篇文章,排完代码的坑,分享一下。

不带参数的单一使用

先声明这段代码我没运行,因为比较简单,看看就行了。

def spamrun(fn):
    def sayspam(*args):
        print("spam,spam,spam")
        fn(*args)
    return sayspam
@spamrun
def useful(a,b):
    print(a*b)
   
if __name__ == "__main__"
    useful(2,5)

作者:陆_离
链接:https://www.jianshu.com/p/7a644520418b
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

我们可以简单的把这个代码理解为:

if __name__ == "__main__"
    useful = spamrun(useful)
    useful(a,b)

不带参数的多次使用

def spamrun(fn):
    print("run:"+fn.__name__)
    def sayspam(*args):
        print("spam,spam,spam")
        fn(*args)
    return sayspam


def spamrun1(fn):
    print("run1:"+fn.__name__)
    def sayspam1(*args):
        print("spam1,spam1,spam1")
        fn(*args)
    return sayspam1
        
@spamrun
@spamrun1
def useful(a,b):
    print(a*b)
   
if __name__ == "__main__":
    useful(2,5)

运行结果为:

run1:useful
run:sayspam1
spam,spam,spam
spam1,spam1,spam1
10

运行过程是这样的:

        useful(2,5)  变为   spamrun(spanmrun1(useful)(2,5))

        首先解析  spamrun ,发现里边的参数无法给参数 fn 赋值,只能先把里边的 spanmrun1(useful)  先解析出来,看是什么东西。

        于是解析  spanmrun1(useful)  ,输出了 run1:useful  .整个式子变成  spamrun(sayspam1(2,5))  。这时回到外层继续运行spamrun。输出  run:sayspam1 。并且返回 syspam() 。整个式子变成 sayspam(2,5) 。

         这时候,运行,打印 spam,spam,spam 。注意这时的 fn=sayspam1 ,所以继续运行 sayspam1(2,5) .

         最后是运行 print(a*b) 语句。

带参数的多次使用

检查函数的输入输出是否符合标准,比如我们希望的输入是(int,(int,float))输出是(int,float),这个例子在官网里有,但是在3.6版本中使用有些问题,这里进行了一些改动,如果要进一步了解可以看下functionTool。

import functools

def accepts(*types):
    def check_accepts(f):
        def new_f(*args, **kwds):
            assert len(types) == (len(args) + len(kwds)), \
                "args cnt %d does not match %d" % (len(args) + len(kwds), len(types))
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                    "arg %r does not match %s" % (a, t)
            return f(*args, **kwds)

        functools.update_wrapper(new_f, f)
        return new_f

    return check_accepts


def returns(rtype):
    def check_returns(f):
        def new_f(*args, **kwds):
            result = f(*args, **kwds)
            assert isinstance(result, rtype), \
                "return value %r does not match %s" % (result, rtype)
            return result

        functools.update_wrapper(new_f, f)
        return new_f

    return check_returns


@accepts(int, (int, float))
@returns((int, float))
def func(arg1, arg2):
    return arg1 * arg2  
    
if __name__ == "__main__":
    a = func(1, 'b')
    print(a)

运行结果:

Traceback (most recent call last):
  File "test111.py", line 39, in <module>
    a = func(1, 'b')
  File "test111.py", line 10, in new_f
    "arg %r does not match %s" % (a, t)
AssertionError: arg 'b' does not match (<class 'int'>, <class 'float'>)

还是和上面一样的一步步分析。

       之前看到有的代码直接用  new_f.__name__=f.__name__   是不行的,这里用的  update_wrapper()  。我没有深究,大概的意思就是这样能把整个函数的运行上下文都给传过去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值