扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
题主你好,
成都创新互联公司是一家专注于成都网站设计、做网站与策划设计,阳春网站建设哪家好?成都创新互联公司做网站,专注于网站建设10余年,网设计领域的专业建站公司;建站业务涵盖:阳春等地区。阳春做网站价格咨询:18982081108
自定义函数时,如果函数中有多个参数,则这些参数之间使用 " 逗号 " 分隔.
我写了个小例子,如图:
写在最后: 参数之间除了 " 逗号 "外, " 逗号 "后面还有空格, 这些空格不是必须的, 只是为了增加可读性而加上的.
希望可以帮到题主, 欢迎追问
一、前言
在python中,函数参数的定义和传递有以下几种方式:
语法
意义
def func(name)
普通参数,可以根据位置匹配,也可以根据key来匹配
def func(name=value)
默认参数,当参数没有传递时,使用默认值
def func(*iteratable)
将所有剩下的未匹配的参数收集至一个tuple中
def func(**dictionary)
将剩下未匹配的参数收集值一个dict中
def func(*, name)
必须使用key来匹配参数
def func(*other, name)
必须使用key来匹配参数
func(value)
函数调用,参数值按传递的参数顺序匹配
func(name=value)
函数调用,参数值根据key来匹配
func(*iteratable)
函数调用,将iteratable容器中的参数展开,按位置匹配对应的函数参数
func(**dictionary)
函数调用,将dict中的参数展开,按key值来匹配对应的函数参数
在python中,参数可以按照顺序传递,在调用函数时,参数的值按照传递的顺序,从左到右依次匹配。并且还可以给参数传递默认值,这都很好理解,因为在C、C++、Java等许多语言中,函数的参数传递都是按照这种方法来传递的。
但python的参数定义和传递除了按照顺序传递以及可以给默认值外,它还有其它的一些特点,在进一步讲解之前,首先说明python中函数调用中参数匹配的顺序:
按照顺序,给没有key的参数赋值,意味着传递参数时,需按顺序匹配的参数必须出现在按key匹配的参数之前;
给按照key匹配的参数赋值;
将多余的按照顺序匹配但未匹配的参数值归入*name的tuple中;
将多余未匹配上的按照key进行匹配的参数值归入**name的dict对象中;
将为匹配上的且具有默认值的参数赋默认值
二、按key匹配参数
对于C、C++这种语言,在调用函数时,系统会首先将函数地址压入堆栈,其次按参数的从右往左的顺序,一次压入堆栈。因此,C、C++这种语言它们只支持按顺序匹配形参。而python的做法不同,参数除了可以按顺序匹配,还可以按照参数名称来匹配。如:
def func(name, age):
print(name, age)
对于这个函数,以下的调用时等价的:
func('rechar', 27) #按顺序匹配
func(name = 'rechar', age = 27) #按参数名称匹配,在运行时告诉系统参数name的值为‘rechar’,age的值为27
func(age = 27, name = 'rechar') #按参数名称匹配
func('rechar', age = 27) #name是按顺序匹配,age按名称匹配
在python中,当按照参数名称进行匹配参数是,参数传递的顺序是可以任意的,不要求按照函数定义中参数的顺序进行传递。在使用名称匹配时,如果需要混合使用按顺序匹配规则,则按顺序匹配的参数必须出现在按key匹配的参数前,否则会报错:
func(name = 'rechar', 27)
以上调用会报如下错误:
三、函数定义中的”*name“
python在给按顺序匹配和按key匹配的参数赋完值后,如果发现调用者传入的参数仍有未匹配上的会发生什么情况呢?看一下下面的例子:
func('rechar', 27, 32)
运行时我们看到如下错误:
Traceback (most recent call last):
File "E:\tmp\tt.py", line 5, in module
func('rechar', 27, 32)
TypeError: func() takes 2 positional arguments but 3 were given
哦,python会抱怨我们传递的参数太多了。那如果确实在一些情况下,我们无法保证传递的参数数量一定和函数需要的参数数相等怎么办呢?这是就是*iterable这种参数该登场的时候了,假如在定义函数定义是,我们增加了一个参数,这个参数以一个”*“开始,那么这个参数实际上是一个tuple类型。假如传递的参数比需要的多,那那些多余的参数会被放入这个tuple中。例如,
def func(name, age, *other):
print(name, age, other)
那么,
func('rechar', 27, 32)
这个调用的输出如下:
rechar 27 (32,)
四、函数定义中的”**name“
python在将所有未匹配上的非按名称匹配的参数装入参数中的tuple之后,假如还有未匹配上的按名称匹配的参数那情况会怎样呢?首先来看一下下面的示例:
def func(name, age):
print(name, age)
func(name = 'rechar', age = 27, pay='1800')
执行时,python又抱怨了:
Traceback (most recent call last):
File "E:\tmp\tt.py", line 5, in module
func(name = 'rechar', age = 27, pay='1800')
TypeError: func() got an unexpected keyword argument 'pay'
它说func这个函数没有名称为”pay“的参数,这种情况或许出现在我们函数重构之后,原来函数时有这个参数的。而这个函数调用可能在别处没有被修改。假设即使给了”pay“这个参数,程序的正确性不受影响,没错,这就是”**name“参数的用武之地了。
假如在函数定义中,给函数增加一个以”**“开头的参数,那么这个参数实际上是一个dict对象,它会将参数调用中所有没有被匹配的按名称传递的参数都放入这个dict中。例如,
def func(name, age,**other):
print(name, age, other)
func(name = 'rechar', age = 27, pay='1800')
那么运行结果输出,
rechar 27 {'pay': '1800'}
看到了吧,这里的other就将没有匹配的”pay=‘1800’“收入囊中了。
五、规定调用必须按名称匹配
当我们在定义函数时,如果第一个参数就是”*name“参数,那么可想而知,我们无法使用按顺序匹配的方式传递,因为所有的按顺序传递的参数值最终的归宿都会是这里的tuple当中。而为了给后续的参数传递值,我们只能使用按名称匹配的方法。
六、”**“参数只能出现在最后一个形参之后
想想为什么?其实很好理解,因为出现在”**“形参之后的形参,无论使用按顺序传递还是按名称传递,最终都无法到达参数值真正应该需要到的地方。所以python规定,如果需要”**“参数,那它必须是最后一个形参。否则python会报语法错误。
七、函数调用中的”*“
在表格中我们看到了有func(*iteratable)的调用,这个调用的意思是,iteratable必须是一个可迭代的容器,比如list、tuple;作为参数传递值,它最终传递到函数时,不是以一个整体出现,而是将其中的元素按照顺序传递的方式,一次赋值给函数的形参。例如,
li = ['rechar', 27]
func(*li)
这个函数调用与
func('rechar', 27)
是等价的。
八、函数调用中的”**“
知道”*“在函数调用中的效果之后,也就很好理解”**“的作用了。它是将传递进来的dict对象分解,每一个元素对应一个按名称传递的参数,根据其中的key对参数进行赋值。
cr变量代表的数据库连接游标
如:cr.execute('select object,name from res_request_link order by priority')
在前面已经多次提到函数这个概念,之所以没有解释什么是函数,是因为程序中的函数和数学中的函数差不多,如input()、range()等都是函数,这些都是Python的标准函数,直接使用就可以了。根据需要,用户也可以自定义函数。
12.1 函数
函数的结构:
def 函数名(参数):
函数体
return 返回值
例如:数学中的函数f(x)=2x+5在Python中可以定义如下:
def f(x):
y=2*x+5
return(y)
如果x取值为3,可以使用如下语句调用函数:
f(3)
下面给出完整的程序代码:
def f(x):
y=2*x+5
return(y)
res=f(3)
print(res)
运行结果:11
如上例中的x是函数f(x)的参数,有时也被称为形式参数(简称形参),在函数被调用时,x被具体的值3替换y就是函数的返回值,这个值3也被称为实际参数(简称实参)。
上例中的y是函数f(x)的返回值。并不是所有的函数都有参数和返回值。如下面的函数:
def func():
print('此为无参数传递、无返回值的函数')
func()
输出结果:此为无参数传递、无返回值的函数
可以看出,该函数func()无参数,故调用时不用赋给参数值。
函数也可以有多个参数,如f(x,y)=x²+y²,可用Python语言定义如下:
def f(x,y):
z=x**2+y**2
return z
print(f(2,3)) #调用函数f(x,y)
输出结果:13
也可以通过直接给参数列表中的参数赋值的方法,为参数添加默认值,如果用户赋予参数值,则按照用户赋值执行,否则使用默认值。例如:
def f(x,y=3):
z=x**2+y**2
return z
若调用时参数列表为(2,1),即x赋值为2,y赋值为1:
print(f(2,1))
输出结果为:5
若调用时参数列表为(2),即x赋值为2,y赋值省缺,则y使用默认值:
print(f(2))
输出结果为:13
回调函数,又称函数回调,是将函数作为另一函数的参数。
例如:
def func(fun,m,n):
fun(m,n)
def f_add(m,n):
print('m+n=',m+n)
def f_mult(m,n):
print('m*n=',m*n)
func(f_add,2,3)
func(f_mult,2,3)
输出结果:
m+n= 5
m*n= 6
在f_add(m,n)和f_mult(m,n)被定义前,func(fun,m,n)中的fun(m,n)就已经调用了这两个函数,即“先调用后定义”,这也是回调函数的特点。
如果无法预知参数的个数,可以在参数前面加上*号,这种参数实际上对应元组类型。譬如,参会的人数事先不能确定,只能根据与会人员名单输入:
def func(*names):
print('今天与会人员有:')
for name in names:
print(name)
func('张小兵','陈晓梅','李大海','王长江')
运行后,输出结果为:
今天与会人员有:
张小兵
陈晓梅
李大海
王长江
参数为字典类型,需要在参数前面加上**号。
def func(**kwargs):
for i in kwargs:
print(i,kwargs[i])
func(a='a1',b='b1',c='c1')
输出结果为:
a a1
b b1
c c1
一个有趣的实例:
def func(x,y,z,*args,**kwargs):
print(x,y,z)
print(args)
print(kwargs)
func('a','b','c','Python','is easy',py='python',j='java',ph='php')
输出结果:
a b c # 前三个实参赋给前三个形参
('Python', 'is easy') # *args接收元组数据
{'py': 'python', 'j': 'java', 'ph': 'php'} # **kwargs接收字典数据
12.2 变量的作用域
变量的作用域即变量的有效范围,可分为全局变量和局部变量。
局部变量
在函数中定义的变量就是局部变量,局部变量的作用域仅限于函数内部使用。
全局变量
在主程序中定义的变量就是全局变量,但在函数中用关键字global修饰的变量也可以当做全局变量来使用。
全局变量的作用域是整个程序,也就是说,全局变量可以在整个程序中可以访问。
下面通过实例去讨论:
程序1:
a=1 # a为全局变量
def a_add():
print('a的初值:',a) # 在函数中读取a的值
a_add() # 调用函数a_add()
a+=1 # 主程序语句,a增加1
print('a现在的值是:',a) # 主程序语句,读取a的值
运行结果:
a的初值: 1
a现在的值是: 2
这个结果和我们想象的一样,全局变量a既可以在主程序中读取,也可以在子程序(函数)中读取。
程序2:
a=1
def a_add():
a+=1
print('a的初值:',a)
a_add()
print('a现在的值是:',a)
运行程序1时出现如下错误提示:
UnboundLocalError: local variable 'a' referenced before assignment
意思是:局部变量'a'在赋值之前被引用。
从语法上来讲,该程序没有错误。首先定义了一个全局变量a并赋值为1,又定义了一个函数a_add(),函数内的语句a+=1就是出错的根源,虽然我们的初衷是想让全局变量a的值增加1,但从错误提示看,这个语句中的a并不是全局变量,而是局部变量。看来,在函数中读取全局变量的值是没有问题的(在程序1中已经得到了验证),但要在函数中改变全局变量的值是不行的(在程序2的错误提示a+=1中的a 是局部变量,而非全局变量)。
怎样解决这个问题?
程序3:
a=1
def a_add(x):
x+=1
return x
print('a的初值:',a)
a=a_add(a)
print('a现在的值是:',a)
运行结果:
a的初值: 1
a现在的值是: 2
结果的确是正确的,但在函数a_add(x)中没有调用变量a(没有出现变量a)。
程序4:
a=1
def a_add(a):
a+=1
return a
print('a的初值:',a)
a=a_add(a)
print('a现在的值是:',a)
运行结果:
a的初值: 1
a现在的值是: 2
对比程序4和程序3不难发现,其实程序4只是简单的把函数的参数x变成了a,这个a的实质和程序3中的x还是一样的。这进一步证实,函数中的a是局部变量,与主程序的全局变量a有着本质的区别。
程序5:
a=1
def a_add():
global a
a+=1
print('a的初值:',a)
a_add()
print('a现在的值是:',a)
运行结果:
a的初值: 1
a现在的值是: 2
程序5和程序2相比较,仅仅是在函数中添加了一个定义“global a”,此时的局部变量a就可以当做全局变量使用,由于它和全局变量a同名,自然也就不用区分a究竟是全局变量还是局部变量了,在主程序和该函数内都可以访问、修改变量a的值了。
虽然使用global可使变量使用起来非常方便,但也容易引起混淆,故在使用过程中还是谨慎为好。
12.3 函数的递归与嵌套
递归,就是函数调用它自身。递归必须设置停止条件,否则函数将无法终止,形成死循环。
以计算阶乘为例:
def func(n):
if n==1:
return 1
else:
return n*func(n-1) #func( )调用func( )
print(func(5))
运行结果为:120
嵌套,指在函数中调用另外的函数。这是程序中常见的一种结构,在此不再赘述。
匿名函数
Python中可以在参数前加上关键字lambda定义一个匿名函数,这样的函数一般都属于“一次性”的。
例如:
程序1:这是一个常规的函数定义和调用。
def f_add(x,y):
return x+y
print(f_add(2,3))
输出结果:5
程序2:使用lambda定义匿名函数。
f_add=lambda x,y:x+y
print(f_add(2,3))
输出结果:5
从上面的代码可以看出,使用lambda仅仅减少了一行代码。f_add=lambda x,y:x+y中的f_add不是变量名,而是函数名。程序1和程序2的print( )语句中的参数都是一样的——调用函数f_add( )。所以,匿名函数并没有太多的优点。
创建对象时调用的是构造方法__init__,参数会传递到构造方法中。将构造方法定义为__init__(self, *args, **kwargs)就可以接收任意参数。
说明:Tensorflow平台有太多的函数,需要及时查看每个函数里的有哪些参数、各种参数的使用说明。在jupyter notebook中有最常用的快捷键可以打开提示:
快捷键:在函数括号内, 按着shift , 双击tap !
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流