• 180114 用装饰器实现在不改变函数调用者的代码基础上,实现在函数执行前后分别打印"before" 和 "after"


    def bef_aft(func):   #定义一个名为bef_aft的函数名 ,()里的是函数的参数,设置为func, 函数的参数分为实参和形参,有个参数传递的概念 ,下面有很多的解释
        def PRint(*arg,**kwarg):   #再度定义一个PRint函数,里面的参数列表()是*arg 和**kwarg;关于*arg **kwarg。下面有详细的解释
            print("before".center(50,"-"))  # 输出内容()字符串before ,center 是内置函数,居中对齐的意思,()50,"-" ??什么意思?50个——?
            res=func(*arg,**kwarg)  #res 等于什么? res是定义的变量吗? 又出现了kwarg ,kw是key word arg呢?最常见的是出现在参数列表里面,实际上这个符号没有任何意义只是一个名称,它指向传递给这个方法来使用的参数,就像你的名字一样,比如你叫张三,可能你同事都习惯叫你小张,你弟弟妹妹都叫你哥,实际上都是
            print(res)  #输出:res
            print("after".center(50,"-")) #好吧,这是和上上一行一起配合夹着res显示的。
            return res  #请问:print 和return 有什么区别??
        return PRint  #又见return  返回输出定义的PRint的内容。
    
    @bef_aft   #嵌入语法糖  
    def f1(arg):   #函数定义 f1 参数(arg)
        return arg + 1  #返回 arg+1
    
    
    @bef_aft  #一样嵌入语法糖
    def f2(arg1, arg2):  #函数定义f2 ()参数列表arg1,arg2
        return arg1 + arg2 #返回值 arg1+arg2
    
    
    f1(2)      #问题来了,这是什么意思? 函数f1 参数2??位置2
    f2(5,2)   #同上,5,2
    def f1(arg):
        return arg + 1
    def f2(arg1, arg2):
        return arg1 + arg2

    这要用装饰器来实现。

    先说说函数定义,我们都知道,下面的代码定义了一个函数funcA

    def funcA():
      pass
        
    显然,函数funcA没有参数(同时啥也不干:D)。

    ps因为()里的是空的,没有参数。。。

    下面这个函数funcB就有两个参数了,  ps 分别是a 和 b ,括号里是参数列表。
    def funcB(a, b):
      print a
      print b
    调用的时候,我们需要使用函数名,加上圆括号扩起来的参数列表,比如 funcB(100, 99),执行结果是:
    100
    99

    看下面一行

    很明显,参数的顺序和个数要和函数定义ps funcB中一致,如果执行funcB(100),Python会报错的:
    TypeError: funcB() takes exactly 2 arguments (1 given)

    我们可以在函数定义中使用参数默认值,比如
    def funcC(a, b=0):
      print a
      print b
    在函数funcC的定义中,参数b有默认值,是一个可选参数,如果我们调用funcC(100),b会自动赋值为0。

    OK,目前为止,我们要定义一个函数的时候,必须要预先定义这个函数需要多少个参数(或者说可以接受多少个参数)。一般情况下这是没问题的,但是也有在定义函数的时候,不能知道参数个数的情况(想一想C语言里的printf函数),在Python里,带*的参数就是用来接受可变数量参数的。看一个例子:

    ps,简单的例子如下:

    def funcD(a, b, *c):
      print a
      print b
      print "length of c is: %d " % len(c)    # ps :注意,这里我不是很懂,output是 4 ……哦,明白了!! 就是除了 1,2 之外的所有数字。
      print c     #同样不懂,返回了一个小括号的元组(3,4,5,6)

    调用funcD(1, 2, 3, 4, 5, 6)结果是
    1
    2
    length of c is: 4
    (3, 4, 5, 6)
    我们看到,前面两个参数被a、b接受了,剩下的4个参数,全部被c接受了,c在这里是一个tuple。我们在调用funcD的时候,至少要传递2个参数,2个以上的参数,都放到c里了,如果只有两个参数,那么c就是一个empty tuple

    好了,一颗星我们弄清楚了,下面轮到两颗星。

    上面的例子里,调用函数的时候,传递的参数都是根据位置来跟函数定义里的参数表匹配的,比如funcB(100, 99)和funcB(99, 100)的执行结果是不一样的。在Python里,还支持一种用关键字参数(keyword argument)调用函数的办法,也就是在调用函数的时候,明确指定参数值付给那个形参。比如还是上面的funcB(a, b),我们通过这两种方式调用
    funcB(a=100, b=99)

    funcB(b=99, a=100)
    结果跟funcB(100, 99)都是一样的,因为我们在使用关键字参数调用的时候,指定了把100赋值给a,99赋值给b。也就是说,关键字参数可以让我们在调用函数的时候打乱参数传递的顺序!

    另外,在函数调用中,可以混合使用基于位置匹配的参数和关键字参数,前题是先给出固定位置的参数,比如
    def funcE(a, b, c):
      print a
      print b
      print c

    调用funcE(100, 99, 98)和调用funcE(100, c=98, b=99)的结果是一样的。

    好了,经过以上铺垫,两颗星总算可以出场了:

    如果一个函数定义中的最后一个形参有 ** (双星号)def funcF(a, **b):前缀,

    所有正常形参之外的其他的关键字参数都将被放置在一个字典中传递给函数,比如:
    def funcF(a, **b):   #定义一个函数 funcF ,参数列表是()里的 a 和 **b
      print a    #输出 a
      for x in b:   #生成器 for in ,用一个x来遍历b里面的数据。
        print x + ": " + str(b[x])   #输出 ,x 然后 拼接 :号 再拼接 字符串类型的 列表。 这里有点不懂 + str(b[x])     b【x】??
    调用funcF(100, c='你好', b=200),执行结果
    100
    c: 你好
    b: 200
    大家可以看到,b是一个dict对象实例,它接受了关键字参数b和c。

    常规参数,*参数及**参数可以同时使用,具体怎么用?看看Python Reference Manual吧,关于Function definitions的那些章节。其实,笨想也能猜出来啊,o(∩_∩)o...

    1. 关于*arg和**kwarg

    首先我们可以定一个简单的函数, 函数内部只考虑required_arg这一个形参(位置参数)

    def exmaple(required_arg):
        print required_arg
    
    exmaple("Hello, World!")
    
    >> Hello, World!
    

    那么,如果我们调用函数式传入了不止一个位置参数会出现什么情况?当然是会报错!

    exmaple("Hello, World!", "another string")
    
    >> TypeError: exmaple() takes exactly 1 argument (2 given)
    
    
    def exmaple2(required_arg, *arg, **kwarg):
        if arg:
            print "arg: ", arg
    
        if kwarg:
            print "kwarg: ", kwarg
    
    exmaple2("Hi", 1, 2, 3, keyword1 = "bar", keyword2 = "foo")
    
    >> arg:  (1, 2, 3)
    >> kwarg:  {'keyword2': 'foo', 'keyword1': 'bar'}
    

    从上面的例子可以看到,当我传入了更多实参的时候

    • *arg会把多出来的位置参数转化为tuple
    • **kwarg会把关键字参数转化为dict

    再举个例子,一个不设定参数个数的加法函数

    def sum(*arg):
        res = 0
        for e in arg:
            res += e
        return res
    
    print sum(1, 2, 3, 4)
    print sum(1, 1)
    >> 10
    >> 2
    

    当然,如果想控制关键字参数,可以单独使用一个*,作为特殊分隔符号。限于Python 3,下面例子中限定了只能有两个关键字参数,而且参数名为keyword1keyword2

    def person(required_arg, *, keyword1, keyword2):
        print(required_arg, keyword1, keyword2)
    
    person("Hi", keyword1="bar", keyword2="foo")
    >> Hi bar foo
    

    如果不传入参数名keyword1keyword2会报错,因为都会看做位置参数

    person("Hi", "bar", "foo")
    
    >> TypeError: person() takes 1 positional argument but 3 were given
    

    调用函数时使用*arg和**kwarg

    直接上例子,跟上面的情况十分类似。反向思维。

    def sum(a, b, c):
        return a + b + c
    
    a = [1, 2, 3]
    
    # the * unpack list a 
    print sum(*a)
    >> 6
    
    def sum(a, b, c):
        return a + b + c
    
    a = {'a': 1, 'b': 2, 'c': 3}
    
    # the ** unpack dict a
    print sum(**a)
    >> 6

    *arg是非关键字参数,用于元组,
    **kwarg是关键字参数,用于字典
    其中的 KW 代表关键字

    可以看到,这两个是python中的可变参数。
    *args表示任何多个无名参数,它是一个tuple;
    **kwargs表示关键字参数,它是一个dict。并且同时使用*args和**kwargs时,必须*args参数列要在**kwargs前







  • 相关阅读:
    MySQL索引原理
    MyBatis执行流程的各阶段介绍
    实现单栈实现支持getMin的栈
    缓存LRU算法——使用HashMap和双向链表实现
    Hystrix资源隔离
    各种负载均衡策略-图解
    接口调用超时的实现原理
    Spring Bean生命周期的各阶段介绍
    TAR命令详解 复习
    INT MOD
  • 原文地址:https://www.cnblogs.com/cputn/p/8284920.html
Copyright © 2020-2023  润新知