专栏文章 我觉得我还可以再抢救一下 - 继续学习无敌哥的装饰器系列

我去催饭 · 2021年11月30日 · 最后由 彩虹海的约定 回复于 2021年12月03日 · 6430 次阅读

*args**kwargs 傻傻分不清楚

上次发布后,后台有粉丝留言,说不太明白这个args 和*kwargs 的用法。其实这个问题经常在面试中被问到,以前是靠死记硬背,还经常记混,记得老师教过,这是可变参数,*args 是把参数变成元组,**kwargs 是转成字典。真是这样吗?还是实践一下吧:

''' *args又叫可变参数,
    会把参数解析成tuple,
    **kwargs又叫关键字参数,
    会把key=value这样的键值对
    参数解析成dict,至于叫什么随意,
    args和kwargs只是约定俗成,前面有
    *和**就行,比如我就叫*anything'''

def func(a,b=1,*anything):
    print(a)
    print(b)
    print("args:",anything)

def funca(**kwargs):
    print("kwargs:",kwargs)

def funcb(*anything,**anything):
    print("args=",args)
    print("anything=",anything)

if __name__ == "__main__":
   func(0,1,'无敌哥流批',[22,33],44)
   print("---我是分割线---")
   funca(a=1,b=2,c=3)
   print("---我是分割线---")
   funcb([0,1,"t"],1,None,a=1,b=2,c=3)

运行结果
0
1
args: ('无敌哥流批', [22, 33], 44)
---我是分割线---
kwargs: {'a': 1, 'b': 2, 'c': 3}
---我是分割线---
args= ([0, 1, 'test'], 1, None)
anything= {'a': 1, 'b': 2, 'c': 3}

结果和预想的一样,但是他们其实是有顺序的,固定参数>默认参数>可变参数>关键字参数,搞乱顺序,结果就是

SyntaxError: positional argument 
follows keyword argument

回到正题 上节课的作业


老实说,无敌哥这个作业给我看懵了,没明白他的意思,什么是@loop@loop(5) 又是怎么来的,这俩还是同一个装饰器么?直到看了他的解答才恍然大悟:

def run(times):
'''这个callable方法是核心'''
    if callable(times):
        def wrapper(*args, **kwargs):
            for i in range(5):
                times(*args, **kwargs)
                print(f"运行的第{i}次")

        return wrapper
    else:
        def decorator(func):
            def wrapper(*args, **kwargs):
                for i in range(times):
                    func(*args, **kwargs)
                    print(f"运行的第{i}次")

            return wrapper

        return decorator

'''没有加参数,callable是True,会运行5次'''
@run 
def func():
    print("func运行了")
'''此时callable是False,则会按照传参运行相应次数'''    
@run(1)
def func1():
    print("func1运行了")
if __name__=="__main__":
    func()
    print("---我是分割线---")
    func1()     

原来是利用了 callable 这个方法,来判断函数是否是可以调用的,相当于变相搞了一个 if else 的判断,如果是 True 的就走 A 路线,否则就是 B 路线,真是,妙啊。

时候不早了,先卷到这。接下来都是硬茬,有的啃了。诸位,咱们青山不改,绿水长流,下期见。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 17 条回复 时间 点赞

沙发自己坐,消灭 0 回复

用 callable 这个判断很巧妙,能判断装饰器是直接包住函数还是中间还有别的东西(如装饰器带有参数),学习了。

看见 python 代码,手痒,用类的实现方式也写了一个玩玩

class loop:

    def __init__(self, times):
        self.func = times if callable(times) else None
        self.times = times if isinstance(times, int) else 5

    def __call__(self, *args, **kwargs):
        if self.func is None:
            self.func = args[0]
            return self.__call__
        for i in range(self.times):
            self.func(*args, **kwargs)
            print(f"运行的第{i + 1}次")
frankxii 回复

一看就是资深大佬啊

是不是针对 callable 的注释写反了?没加参数时 callable 返回 True,参数为 1 时 callable 返回 False?

公子 回复

感谢指正

我去催饭 回复

我每天正事不干,光琢磨着怎么炫技去了😂
正常项目里面我可能会分成两个装饰器去写,尽量避免不同类型的入参和出参

def funcb(*anything,**anything):
    print("args=",args)
    print("anything=",anything)

还可以抢救下

五月朝露 回复

救不回来了,找个地儿埋了吧😂

frankxii 回复

是的,我感觉写俩好一点,至少我自己不乱,callable 这个概念我昨天还和无敌哥讨论了好久,有个弯儿一直没绕过来

挽尊

原来如此,函数传参的时候经常有这个,搞不懂是啥意思,真羡慕有老师的

可莉 回复

嘿嘿,可莉,嘿嘿嘿嘿~

def funcb(*anything,**anything):
运行结果:
...
args= ([0, 1, 'test'], 1, 22, None)

👆应为:

def funcb(*args,**anything):

没有 22 吧 args= ([0, 1, 'test'], 1, None)

rainbowzhouj 回复

没有,感谢勘误

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册