1. 空函数
如果想定义一个什么事也不做的空函数,可以用pass语句:
def nop(): pass
pass
语句什么都不做,那有什么用?实际上pass
可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass
,让代码能运行起来。
2. 容易混肴的函数使用方式
函数的赋值其实就是将函数名指向函数体。
2.1 函数名其实也是一个变量
例如,
def func(): return "Python" print (func()) print (func) func = 100 print (func) print (func())
输出结果
Python <function func at 0x7f4aa8a66f28> 100 Traceback (most recent call last): File "test.py", line 27, in <module> print (func()) TypeError: 'int' object is not callable
第一次输出是调用函数的输出结果
第二次输出是函数所在的物理存储位置
第三次输出是将函数名(变量)重新绑定一个值后的输出结果
第四次输出是因为“整型不能函数调用”,因为将func绑定100后,func已经成为整型变量,func()是进行的函数调用,所以报错!
2.2 函数的绑定规则
情形1
def func(a): return a b = func(6) print(b)
输出结果
6
情形2
def func(a): return a b = func print(b(6))
输出结果
6
情形3(该情况类似情形1)
def func(a=[100,200]): return a x = func() x.append(1) print("x:", x)
输出结果
X: [100, 200, 1]
情形4(该情况类似情形2)
def func(a=[100,200]): return a x = func x().append(1) print("x:", x())
输出结果
x: [100, 200, 1]
情形5
def func(a=[100,200]): return a x = func x().append(1) print("x:", x)
输出结果
x: <function func at 0x7f818e8d5f28>
该输出结果为函数所在的位置
情形6
def func(a=[100,200]): return a x = func x.append(1) print("x:", x)
输出结果
Traceback (most recent call last): File "test.py", line 24, in <module> x.append(1) AttributeError: 'function' object has no attribute 'append'
错误的原因是:函数“function”对象没有“append”属性
2.3 函数的绑定方式
def func(a=[100,200]): return a x = func() y = func() print(func) print(id(x)) print(id(y)) x.append(1) print("y:", y) y.append(2) print("x:", x) x.append(3) print("x:", x) print("y:", y)
输出结果
<function func at 0x7feed4d4ef28> 140663724970760 140663724970760 y: [100, 200, 1] x: [100, 200, 1, 2] x: [100, 200, 1, 2, 3] y: [100, 200, 1, 2, 3]
我们发现,x和y绑定的函数为一个地址,也即当x绑定值改变时,y值也随之也改变。
但是函数 func 与函数 x 、 y 的地址是不一样的;将140663724970760转成16进制时,得到
>>> hex(140098127414024) '0x7f6b231e4308'
所以地址是有区别的!
2.4 序列传参(元组传参)
代码片段1
def func(a,b): print(a+b) func(1,2) func(*(1,2))
输出结果
3 3
当形参是与实参不一致时
def func(a,b): print(a+b) func(*(1))
运行结果
Traceback (most recent call last): File "test01.py", line 4, in <module> func(*(1)) TypeError: func() argument after * must be an iterable, not int
本段代码产生错误的原因是元组形式错误导致的。在*后面必须是可迭代的,而不是整数
执行错误后,没有向下执行。
代码片段2
def func(a,b): print(a+b) func(*(1,))
运行结果
Traceback (most recent call last): File "test01.py", line 4, in <module> func(*(1,)) TypeError: func() missing 1 required positional argument: 'b'
该段代码运行错误是因为实参少于形参个数
代码片段3
def func(a,b): print(a+b) func(*(1,2,3))
运算结果
Traceback (most recent call last): File "test01.py", line 4, in <module> func(*(1,2,3)) TypeError: func() takes 2 positional arguments but 3 were given
该段代码运行错误的原因是因为实参多于形参个数
总结:当仅有元组传参时,实参个数必须与实参个数一致
代码片段4
def info(*var): print(var) for a in var: print(a) info(*(1,2,3)) print("------------") info(1,2,3)
输出结果
(1, 2, 3) 1 2 3 ------------ (1, 2, 3) 1 2 3
2.4 序列传参(列表传参)
def func(a,b): print(a+b) func(*[1,2])
输出结果
3
其实列表传参与元组传参在功能上没有明显区别
2.5 关键字传参
关键字传参只要关键字匹配就可以,顺序可以改变。
def func(a,b): print(a+b)
func(b=1,a=2)
func(a=1,b=2)
输出结果
33
当改变一下
def func(a,b): print(a+b) func(a=1,2)
输出结果
File "test01.py", line 4 func(a=1,2) ^ SyntaxError: positional argument follows keyword argument
语法错误:位置参数在关键字参数后
有位置参数的,位置参数必须写在关键参数前面;换句话说,一旦出现关键字参数,其后不能出现位置参数
def func(a,b): print(a+b) func(1,a=2)
输出结果
Traceback (most recent call last): File "test01.py", line 4, in <module> func(1,a=2) TypeError: func() got multiple values for argument 'a'
错误类型:函数的参数a得到了多个值。
很明显,当形参和实参位置赋值完毕后,再按照关键字参数再赋值,最后发现形参a有多个值,而没有报形参b值缺少。可以说明实参向形参传值时不存在形同形参覆盖问题。
2.6 字典传参
代码片段1
def print_str(a, b): print(b) print(a) d = {"b":2,"a":1} print_str(**d)
输出结果
2 1
代码片段2
def print_str(a, b): print(b) print(a) print_str("b":1,"a":2)
输出结果
File "test01.py", line 5 print_str("b":1,"a":2) ^ SyntaxError: invalid syntax
语法错误,这样写不是字典所要求的输入形式
代码片段3
def print_str(a, b): print(b) print(a) print_str({"b":1,"a":2})
输出结果
Traceback (most recent call last): File "test01.py", line 5, in <module> print_str({"b":1,"a":2}) TypeError: print_str() missing 1 required positional argument: 'b'
因为缺少参数 b 而报错,该段代码中将字典 {"b":1,"a":2} 作为一个参数传给了 a ;将其进行修改
代码片段4
def print_str(a, b): print(a) print(b) print_str({"b":1,"a":2},1)
输出结果
{'b': 1, 'a': 2} 1
不难看出,该段代码中将字典当作一个参数传给形参 a ,将实参 1 传给形参 b ,其实,这就是位置传参
其实字典传参的输入形式如代码5所示
代码片段5
def print_str(a, b): print(a) print(b) print_str(**{"b":1,"a":2})
输出结果
2 1
代码片段6
def print_str(**dict): print(dict) print_str(**{"b":1,"a":2})
输出结果
{'a': 2, 'b': 1}
代码片段7
def print_str(**dict): print(dict) print_str({"b":1,"a":2})
输出结果
Traceback (most recent call last): File "test01.py", line 4, in <module> print_str({"b":1,"a":2}) TypeError: print_str() takes 0 positional arguments but 1 was given
函数有0个位置参数,但是实参却有一个别给定
如果按照这个错误提示,当没有任何实参时,应该也不会报错
代码片段8
def print_str(**dict): print(dict) ") print_str( )
输出结果
{} 123
代码片段9
def print_str(**dict): print(dict) ") print_str("b":1,"a":2)
输出结果
File "test01.py", line 5 print_str("b":1,"a":2) ^ SyntaxError: invalid syntax
说明代码述写的时候已经出错
代码片段10
def print_str(**dict): print(dict) print_str(b=1,a=2)
输出结果
{'a': 2, 'b': 1}
代码片段11
def func(**kwargs): print(kwargs) print("参数个数:", len(kwargs)) for k,v in kwargs.items(): print(k, "->", v) func(x = 10, y = 20, z = 30) d = {"x":10, "y":20, "z":30} func(**d) func(**{"a":30, "b":40, "c":50, "d":60}) func(**{"a":"你好", "b":40, "c":50, "d":60})
输出结果
{'y': 20, 'z': 30, 'x': 10} 参数个数: 3 y -> 20 z -> 30 x -> 10 {'y': 20, 'z': 30, 'x': 10} 参数个数: 3 y -> 20 z -> 30 x -> 10 {'a': 30, 'c': 50, 'b': 40, 'd': 60} 参数个数: 4 a -> 30 c -> 50 b -> 40 d -> 60 {'a': '你好', 'c': 50, 'b': 40, 'd': 60} 参数个数: 4 a -> 你好 c -> 50 b -> 40 d -> 60
3 混合传参
混合传参形式是将位置传参、关键字传参、序列传参(元组传参、列表传参)、字典传参结合使用。
3.1 位置传参+序列传参
序列传参中的元组传参与列表传参没明显区别,本处仅以元组传参为例。
代码片段1
def print_info(arg1, *var): print(" 参数1:", arg1) print(" 其他参数:") print(var) for a in var: print(a) print_info(100,) print("------------") print_info(100) print("------------")
输出结果
参数1: 100 其他参数: () ------------ 参数1: 100 其他参数: () ------------
当实参满足第一个位置形参后,即使后面的元组参数是空也不报错
代码片段2
def print_info(arg1, *var): print(" 参数1:", arg1) print(" 其他参数:") print(var) for a in var: print(a) print_info(100, 1, 3) print("------------") print_info(100, *(1, 3)) print("------------")
输出结果
参数1: 100 其他参数: (1, 3) 1 3 ------------ 参数1: 100 其他参数: (1, 3) 1 3 ------------
当前面的实参个数满足位置参数个数后,后面的实参均默认为元组形参中的数值。
注意:以本代码为例,第一个位置参数不限于数字、字符串,也可以是元组或字典,为了更清楚地表达,见代码片段3
代码片段3
def print_info(arg1,arg2,arg3, *var): print(" 参数1:", arg1) print(" 参数1:", arg2) print(" 参数1:", arg3) print(" 其他参数:") print(var) for a in var: print(a) print_info((100,200),300,{"a":400,"b":500},600,700,800,900)
输出结果
参数1: (100, 200) 参数1: 300 参数1: {'b': 500, 'a': 400} 其他参数: (600, 700, 800, 900) 600 700 800 900
代码片段4
def print_info(arg1, *var): print(" 参数1:", arg1) print(" 其他参数:") print(var) for a in var: print(a) a = (1,2) b = [1,2] print_info(100,a,200,300,b) print("---------------------") print_info(100,*a,200,300,*b)
输出结果
参数1: 100 其他参数: ((1, 2), 200, 300, [1, 2]) (1, 2) 200 300 [1, 2] --------------------- 参数1: 100 其他参数: (1, 2, 200, 300, 1, 2) 1 2 200 300 1 2
注意述写时候的细节
3.2 位置传参+字典传参
def func(a, **kwargs): print (" 参数 a 是", a) print(kwargs) for key in kwargs: print(key, kwargs[key]) func(10, y = 100, x = 200, z = 1000)
输出结果
参数 a 是 10 {'x': 200, 'z': 1000, 'y': 100} x 200 z 1000 y 100
3.3 位置参数+序列参数+字典参数
函数代码均一样,如下段代码所示;该标题下的其他代码为函数调用代码,函数代码再调用代码中省略。
代码片段1
def print_args(arg1, *targs, **kwargs): print(" arg1 是", arg1) print(" targs:") for t in targs: print (t) print(" kwargs:") for key in kwargs: print(key, kwargs[key])
代码片段2
print_args(1)
输出结果
arg1 是 1 targs: kwargs:
代码片段3
print_args(1, b=")
输出结果
arg1 是 1 targs: kwargs: c 3 b 2
代码片段4
print_args(1,2,3,4,b=")
输出结果
arg1 是 1 targs: 2 3 4 kwargs: b 2 c 3
代码片段5
a = 20 b = 30 t = (20, 100, 200) print_args(10, b, *t, 4, b = 2, c = 3) print("-------------------------") print_args(10, b, *t, 4, **{"b":2, "c": 3})
输出结果
arg1 是 10 targs: 30 20 100 200 4 kwargs: b 2 c 3 ------------------------- arg1 是 10 targs: 30 20 100 200 4 kwargs: b 2 c 3
代码片段6
a = 20 b = 30 t = (20, 100, 200) print_args(10, b, b = 2, c = 3, *t, )
输出结果
arg1 是 10 targs: 30 20 100 200 kwargs: c 3 b 2
代码片段7
本段代码是将函数的形参顺序进行修改,修改后直接报错
File "test01.py", line 1 def print_args(arg1, **targs, *kwargs): ^ SyntaxError: invalid syntax
可以发现,形参的顺序是固定不变的。而实参的顺序是可以改变的。
4 特殊的规则
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city
和job
作为关键字参数。这种方式定义的函数如下:
def student(name, age, *, school, address): print('name:', name, 'age:', age, 'school', school, 'address', address) student("zhang", 24, address = "北京海淀", school = "清华")
student("zhang", 24, **{"school": "清华", "address":"北京海淀"})
输出
name: zhang age: 24 school 清华 address 北京海淀name: zhang age: 24 school 清华 address 北京海淀
*
后面的参数被视为命名关键字参数。
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了:
def person(name, age, *args, city, job): print(name, age, args, city, job)
命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:
>>> person('Jack', 24, 'Beijing', 'Engineer') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: person() takes 2 positional arguments but 4 were given
以上仅是联系的一部分。
也可以参考廖雪峰的本章节博客https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431752945034eb82ac80a3e64b9bb4929b16eeed1eb9000
5 函数是否可执行属性
#函数名本质是变量 >>> callable(print) True >>> callable(print()) False >>> callable(a) Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'a' is not defined >>> b = 1 >>> callable(b) False >>>