一、函数
1、函数
函数是python中组织代码的最小单元
函数是实现模块化编程的基本组件
Python使用def语句定义函数
每个Python函数都有一个返回值,默认为None,也可以使用“return value”明确定定义返回值
def语句会创建一个函数对象,并同时创建一个指向函数的对象引用
函数也是对象,可以存储在组合数据类型中,也可以作为参数传递给其它函数
callable()可用于测试对象是否可调用
函数通过def定义,接着函数名,函数名后面用一对小括号列出参数列表,使用一个冒号开始函数体。
函数体是正常的Python语句,可以组合任意结构
return语句表示函数的返回值
函数有输入(参数)和输出(返回值),函数其实是一个代码单元,把输入转化为输出。
定义函数的时候并不会执行函数体,当调用函数时才会执行函数体
函数通过函数名来调用,函数名后面一对小括号里面传入实参
In [2]: def f1(): ...: print("hello")
...:
In [3]: f1
Out[3]: <function __main__.f1>
In [4]: f1()
hello
In [5]: a = f1
In [6]: b = f1()
hello
In [7]: a
Out[7]: <function __main__.f1>
In [8]: b
In [9]: type(a)
Out[9]: function
In [10]: type(b)
Out[10]: NoneType
In [15]: callable(a) # 实现了__call__()方法就可调用
Out[15]: True
2、对于Python的函数,我们需要记住的是
1)函数的默认返回值是None。
2)python是一个自上而下逐行解释并执行的语言。因此,函数的定义必须在函数被调用之前。同名的函数,后定义的会覆盖前面定义的。
3)程序执行的时候,遇到函数定义只会先将函数整体读进内存,并不立刻执行。等到函数被调用的时候才执行函数体。
4)python函数的参数传递的是值传递还是引用传递。
函数参数传递本质上和变量整体复制一样,只是两个变量分别为形参a和实参b。那么,a=b后,a变了,b值是否跟着变呢?这取决于对象内容可变不可变
值传递:
指在调用函数时,将实际参数复制一份传递给函数,函数对参数进行修改将不会影响到实际参数
适用于不可变对象(如int, str,tuples等)作为参数传递时,例如元组
引用传递:
指调用函数时,将实际参数的地址传递给函数,函数对参数进行修改,将影响实际参数
适用于可变对象(如list,dict,类的实例等)作为参数传递时,例如列表
浅复制:(也叫影子复制)
只复制父对象,不会复制对象的内部的子对象
深复制:
复制对象及其子对象
赋值是引用传递
In [74]: l1 = [1, 2, 3]In [75]: l2 = l1In [76]: l2Out[76]: [1, 2, 3]In [77]: l3 = l1.copy()In [78]: l3Out[78]: [1, 2, 3]In [79]: id(l3) # l3和l1应用的是不同内存对象Out[79]: 140149284135624In [80]: id(l1)Out[80]: 140149295996616In [81]: id(l2) # l2和l1引用的是同一个内存对象Out[81]: 140149295996616
二、函数的参数
对于函数,最重要的知识点莫过于参数了。
参数分为形式参数(形参)和实际参数(实参)。
def f1(a, b, c): pass f1(1, 2, 3)
其中,a,b,c就是形参,1,2,3就是实参,也就是实际要传递的参数。
In [27]: def add(x, y): ...: print(x + y) ...: return x + y ...: In [28]: add(3, 5)8Out[28]: 8In [29]: add(3)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-29-c9889e45a711> in <module>()----> 1 add(3)TypeError: add() missing 1 required positional argument: 'y'In [30]: add(3, 5, 8)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-30-0b52ebc12bc6> in <module>()----> 1 add(3, 5, 8)TypeError: add() takes 2 positional arguments but 3 were givenIn [42]: add(3, "5")---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-42-d73b87d3f8c1> in <module>()----> 1 add(3, "5")<ipython-input-31-4950d8722875> in add(x, y) 1 def add(x, y):----> 2 ret = x + y 3 print('{} + {} = {}'.format(x, y, ret)) 4 return retTypeError: unsupported operand type(s) for +: 'int' and 'str'
函数调用时,传入的实参必须和函数定义时的行参想匹配,如果不匹配会抛出TypeError.
Python中的形式参数有以下几种:
1、位置参数
通常在传递参数的时候我们按照参数的位置,逐一传递,这叫“位置参数”。
In [31]: def add(x, y): ...: ret = x + y ...: print('{} + {} = {}'.format(x, y, ret)) ...: return ret ...: In [32]: add(3, 5) 3 + 5 = 8Out[32]: 8
2、关键字参数
而有时候我们会用“形参名”=“值”的方式传递参数,这叫“关键字参数或指定参数”。
In [33]: add(x=3, y=5)3 + 5 = 8Out[33]: 8In [34]: add(y=3, x=5) # 关键字参数和顺序无关5 + 3 = 8Out[34]: 8
位置参数和关键字参数混用时关键字参数必须在位置参数后面
In [37]: add(3, y=5)3 + 5 = 8Out[37]: 8In [38]: add(x=3, 5) File "<ipython-input-38-165b39de39ac>", line 1 add(x=3, 5) ^SyntaxError: positional argument follows keyword argumentIn [39]: add(3, x=5)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-39-b0f1746a3413> in <module>()----> 1 add(3, x=5)TypeError: add() got multiple values for argument 'x'
3、默认参数
默认参数是为某些参数设定一个默认值,既可以减少参数传入量,也可以享受使用默认值的便利。
默认参数必须位于参数列表的最后部分!
默认参数是在函数定义时,指的是形参!
In [46]: def inc(base, x=1): ...: return base + x ...: In [47]: inc(3, 2)Out[47]: 5In [48]: inc(3)Out[48]: 4In [49]: inc(3, x=4)Out[49]: 7In [56]: inc(base=4)Out[56]: 5In [57]: inc(base=4, x=5)Out[57]: 9In [50]: def inc(x=1, base): ...: return base + x ...: File "<ipython-input-50-ac010ba50fd9>", line 1 def inc(x=1, base): ^SyntaxError: non-default argument follows default argument
4、可变(动态)参数
Python的动态参数有两种,分别是*args和**kwargs,这里面的关键是一个和两个星号,而不是args和kwargs,实际上你可以使用*any或**whatever的方式,但就如self一样,潜规则我们使用*args和**kwargs。
可变参数是在函数定义时,指的是形参!
*args:位置可变参数;函数定义时参数名前加一个星号
一个星号表示接受任意个动态参数。调用时,会将实际参数打包成一个元组传入函数。
此时只能通过位置参数传参
In [63]: def sum(*args): ...: ret = 0 ...: for i in args: ...: ret += i ...: return ret ...: In [64]: sum()Out[64]: 0In [65]: sum(1)Out[65]: 1In [66]: sum(1, 2, 3, 8)Out[66]: 14
**kwargs:关键字可变参数;函数定义时参数名前加两个星号
两个星表示接受键值对的动态参数,数量任意。调用的时候会将实际参数打包成字典。
此时只能通过关键字参数传参
例如:
In [73]: def connect(**kwargs): ...: print(type(kwargs)) ...: for k, v in kwargs.items(): ...: print('{} => {}'.format(k, v)) ...: In [74]: connect(host="localhost", port=3300)<class 'dict'>host => localhostport => 3300In [79]: connect("localhost", port=3300)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-79-6aeadd0e08a5> in <module>()----> 1 connect("localhost", port=3300)TypeError: connect() takes 0 positional arguments but 1 was given
万能参数:
当*args和**kwargs组合起来使用,理论上能接受任何形式和数量的参数,在很多代码中我们都能见到这种定义方式。需要注意的是,*args必须出现在**kwargs之前。
In [83]: def fn(*args, **kwargs): ...: print(args) ...: print(kwargs) ...: In [84]: fn(2, 3, 5, x=11, y="xxj")(2, 3, 5){'x': 11, 'y': 'xxj'}In [85]: def fn(**kwargs, *args): ...: print(args) ...: print(kwargs) File "<ipython-input-85-ae6e58836952>", line 1 def fn(**kwargs, *args): ^SyntaxError: invalid syntax
可变参数和普通参数混合使用:
In [91]: def fn(x, y, *args, **kwargs): ...: print(x) ...: print(y) ...: print(args) ...: print(kwargs) ...: In [92]: fn(2, 3, 4, 5, 6, a=1,b=2)23(4, 5, 6){'a': 1, 'b': 2}In [95]: fn(2, 3)23(){}In [97]: fn(2, y=3)23(){}In [99]: fn(2, 3, 4)23(4,){}In [100]: fn(2, 3, a=23)23(){'a': 23}In [107]: def fn(*args, x): # 位置可变参数是否可以在普通参数之前 ...: print(args) ...: print(x) ...: In [108]: fn(1, 2, 3)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-108-9ab6b0a400c3> in <module>()----> 1 fn(1, 2, 3)TypeError: fn() missing 1 required keyword-only argument: 'x'In [109]: fn(1, 2, x=3)(1, 2)3In [110]: fn(1, x=3)(1,)3In [111]: fn(x=3)()3In [112]: def fn(**kwargs, x): # 关键字可变参数是否可以在普通参数之前 ...: print(kwargs) ...: print(x) ...: File "<ipython-input-112-3226e57525e0>", line 1 def fn(**kwargs, x): ^SyntaxError: invalid syntax
位置可变参数也可以在普通参数之前,但是在位置可变参数之后的普通参数变成了keyword-only参数(只能以关键字参数登入)
关键字可变参数不可以在普通参数之前(为什么?)
可变参数和默认参数混合使用:
In [113]: def fn(x=5, *args): ...: print(x) ...: print(args) ...: In [114]: fn(1)1()In [115]: fn(1,2)1(2,)In [116]: fn(1,2,3,4)1(2, 3, 4)In [117]: fn(x=3, 4) File "<ipython-input-117-2d95cecddace>", line 1 fn(x=3, 4) ^SyntaxError: positional argument follows keyword argumentIn [121]: def fn(*args, x=5): # 位置参数在默认参数之前没限制 ...: print(x) ...: print(args) ...: In [122]: fn()5()In [123]: fn(1, 2)5(1, 2)In [124]: fn(1)5(1,)In [125]: fn(1, x=3)3(1,)In [127]: def fn(**kwargs, x=5): ...: print(x) ...: print(kwargs) ...: File "<ipython-input-127-069a751edec8>", line 1 def fn(**kwargs, x=5): ^SyntaxError: invalid syntaxIn [128]: def fn(x=5, **kwargs): ...: print(x) ...: print(kwargs) ...: ...: In [129]: fn()5{}In [130]: fn(1)1{}In [131]: fn(1, a=2)1{'a': 2}In [132]: fn(x=1, a=2)1{'a': 2}
可变位置参数在默认参数之后,默认参数不能使用关键字传参
可变关键字参数不能在默认参数之前
当默认参数和可变参数一起出现时,默认参数相当于普通参数
小结:
函数的参数规则这么多,头都大了;这里我们建议的函数参数使用用法:
1)默认参数靠后
2)可变参数靠后
3)默认参数和可变参数不同时出现
不遵守不一定错,遵守代码可读性高
当我们需要同时使用默认参数和可变参数时怎么办?
我们通常这样处理:
In [139]: def connect(host='127.0.0.1', port=3306, user='root', password='', **kwargs): ...: pass ...: In [140]: def connect(**kwargs): ...: host = kwargs.pop('host', '127.0.0.1') ...:
三、参数解构
1、参数解构
调用函数时,传入实参时加一个星号,可以把iterable解构成位置参数
调用函数时,传入实参时加两个个星号,可以把dict解构成关键字参数
In [141]: def add(x, y): ...: ret = x + y ...: print('{} + {} = {}'.format(x, y, ret)) ...: In [142]: add(1, 2)1 + 2 = 3In [145]: t = [1, 2]In [146]: add(*t)1 + 2 = 3In [143]: t = [1, 2, 3]In [144]: add(*t)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-144-d90b03560d6e> in <module>()----> 1 add(*t)TypeError: add() takes 2 positional arguments but 3 were givenIn [147]: t = [(1, 2), (3, 4)]In [148]: add(*t) In [148]: add(*t)(1, 2) + (3, 4) = (1, 2, 3, 4)# 字典解构成关键字参数In [153]: d = {'x':1, 'y':2}In [154]: add(**d)1 + 2 = 3In [155]: add(*d)x + y = xy
2、参数解构和可变参数混用
In [161]: def sum(*args): ...: ret = 0 ...: for i in args: ...: ret += i ...: return ret ...: In [162]: sum(*(1, 2))Out[162]: 3In [163]: sum(*[1, 2])Out[163]: 3In [165]: sum(*range(5))Out[165]: 10
3、参数解构的限制
关键字参数解构,key必须是str
In [167]: def fn(**kwargs): ...: print(kwargs) ...: In [168]: fn(**{'a':1}){'a': 1}In [170]: fn(**{12:1})---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-170-99986e5a722a> in <module>()----> 1 fn(**{12:1})In [211]: def fn(*args, **kwargs): ...: print(args) ...: print(kwargs) ...: In [212]: fn(*[1, 2, 3])(1, 2, 3){}In [214]: fn(*[1, 2, 3], **{'a':1, 'b':2})(1, 2, 3){'a': 1, 'b': 2}
4、keyword-only参数
星号之后的参数只能通过关键字参数传入
可变位置参数之后的参数也是keyword-only参数
只能通过关键字参数传入的参数就交keyword-only参数
keyword-only参数可以有默认值
In [179]: def fn(*, x): ...: print(x) ...: In [180]: fn(1)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-180-cb5d79cf2c77> in <module>()----> 1 fn(1)TypeError: fn() takes 0 positional arguments but 1 was givenIn [181]: fn(x=1)1In [182]: fn(1, x=2)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-182-8eb9b719c886> in <module>()----> 1 fn(1, x=2)TypeError: fn() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were givenIn [183]:
keyword-only参数与其它参数混用:
In [188]: def fn(x, *, y): ...: print(x) ...: print(y) ...: In [189]: fn(1, y=2)12In [193]: def fn(x=1, *, y=2): ...: print(x) ...: print(y) ...: ...: In [194]: fn()12In [199]: def fn(x=1, *, y): # ...: print(x) ...: print(y) ...: ...: In [200]: fn(y=3)13In [201]: fn(3)---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-201-f005f2a6106f> in <module>()----> 1 fn(3)TypeError: fn() missing 1 required keyword-only argument: 'y'In [202]: In [205]: def fn(*, x, y): # *号后可以有多个keyword-only参数 ...: print(x) ...: print(y) ...: In [206]: fn(x=1, y=2)12In [207]: def fn(x, y, *): # *号不能写在最后 ...: print(x) ...: print(y) ...: File "<ipython-input-207-6c060b52ac76>", line 1 def fn(x, y, *): ^SyntaxError: named arguments must follow bare *