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前

												

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

  1. 谈谈Python中的decorator装饰器,如何更优雅的重用代码

    众所周知,Python本身有很多优雅的语法,让你能用一行代码写出其他语言很多行代码才能做的事情,比如: 最常用的迭代(eg: for i in range(1,10)), 列表生成式(eg: [ x* ...

  2. python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解

     1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...

  3. python 内置函数和函数装饰器

    python内置函数 1.数学相关 abs(x) 取x绝对值 divmode(x,y) 取x除以y的商和余数,常用做分页,返回商和余数组成一个元组 pow(x,y[,z]) 取x的y次方 ,等同于x ...

  4. python函数(3):装饰器

    昨天学了很多函数方面的概念和知识其中有一个闭包的函数.很多人都对闭包的作用不是很清楚,今天我们就来认识一个新的知识点装饰器.它就是闭包函数的一个经典应用. 预习: 编写装饰器,为多个函数加上认证的功能 ...

  5. python装饰器小计

    1.装饰器:本质是函数,是用来给其他函数添加附加扩展功能的函数,其返回值是一个函数(函数指针) 2.装饰器作用:不改变函数源代码和函数调用方式的前提下添加函数的附加功能. 3.装饰器储备知识点: A. ...

  6. Python全栈开发之---装饰器

    1.装饰器的形成过程 import time def func1(): print('in func1') def timer(func): def inner(): start = time.tim ...

  7. python之装饰器函数

    本章内容 引入 装饰器的形成过程 开放封闭原则 谈装饰器主要功能和装饰器固定结构 带参数的装饰器 多个装饰器装饰一个函数 引入 作为一个会写函数的python开发,我们从今天开始要去公司上班了.写了一 ...

  8. Python小白学习之路(二十四)—【装饰器】

    装饰器 一.装饰器的本质 装饰器的本质就是函数,功能就是为其他函数添加附加功能. 利用装饰器给其他函数添加附加功能时的原则: 1.不能修改被修饰函数的源代码        2.不能修改被修饰函数的调用 ...

  9. python三大神器之装饰器

    装饰器的形成过程 假如你要写一个计算函数执行时间的函数,代码如下: import time def func1(): print('in func1') def timer(func): def in ...

随机推荐

  1. 学习Spring必学的Java基础知识(1)----反射(转)

    引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓"登高必自卑,涉远必自迩".以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系 ...

  2. HTML: width,height

    在进行前端页面开发时,width(width,offsetWidth,scrollWidth,clientWidth)height(height,offsetHeight,scrollHeight,c ...

  3. Springboot 之 解决IDEA读取properties配置文件的中文乱码问题

    问题描述 当在.properties的配置文件中有中文时,读取出来的总是乱码.比如我的application.properties配置文件的内容如下: server.port=9090 test.ms ...

  4. linux系统下安装单台Redis

    注意:搭建redis前一定要安装gcc redis安装方式一 1.安装gcc命令:yum install -y gcc #安装gcc [root@localhost src]# yum install ...

  5. C#设计模式之二十二访问者模式(Visitor Pattern)【行为型】

    一.引言   今天我们开始讲"行为型"设计模式的第九个模式,该模式是[访问者模式],英文名称是:Visitor Pattern.如果按老规矩,先从名称上来看看这个模式,我根本不能获 ...

  6. 【JMeter】选项-函数助手对话框应用举例

    String date="${__javaScript((new Date()).getFullYear()+'-'+((new Date()).getMonth()+1)+'-'+(new ...

  7. 【NOIP模拟】cut

    Description F大爷热爱切树.今天他找到一棵黑白树,不到两秒钟,F大爷就把这棵树切掉了.已知原先树上共n个点,每个点都是黑点或者白点,F大爷切去若干条边后,分成的若干个连通子树中每块恰有一个 ...

  8. dropout理解:1神带9坑

    Dropout是深度学习中防止过拟合的一项非常常见的技术,是hinton大神在12年提出的一篇论文里所采用的方法.有传言hinton大神的数学功底不是很好,所以他所提出的想法背后的数学原理并不是很复杂 ...

  9. 外卖app的header组件开发

    1.webpack框架创建 # 全局安装 vue-cli $ npm install --global vue-cli # 创建一个基于 webpack 模板的新项目 $ vue init webpa ...

  10. 与apk签名有关的那些概念与命令

    一.概念篇 1.消息摘要-Message Digest 消息摘要:在消息数据上,执行一个单向的hash函数,生成一个固定长度的hash值,这个Hash值就是消息摘要,也成为数字指纹. 消息摘要特点: ...