今天让我们聊聊函数的那些事儿~
- Tip 1:函数的定义:
Python定义一个函数使用def保留字,语法形式如下:
def <函数名>(<参数列表>):
<函数体>
return <返回值列表>
- tip 2:形参和实参
形式参数(形参):在函数创建和定义过程中使用的参数。
实际参数(实参):在函数被调用的过程中传递进来的参数。
形参只是代表一个位置,一个变量名,没有具体的值。
实参是一个具体的值,是赋值到变量名中的值。
实参默认按位置顺序依次传递给形参,若参数个数不对,报错。
演示:
>>> def func(a,b):
print(a+b)
#这里的a,b就是形参
>>> func(1,2) #这里的1,2就是实参
3
一、函数的参数传递
Python传入参数的方法有:位置参数、默认参数、可变参数、关键字参数和命名关键字参数、以及各种参数调用的组合
1.位置参数()
普通的参数叫做位置参数,调用时需要按照位置顺序传递参数值。
实参默认按位置顺序依次传递给形参,若参数个数不对,报错。
位置参数是最简单的传入参数的方式,在其它的语言中也常常被使用。
演示一:
>>> def func(a,b):
print(a)
print(b)
>>> func(99,100)
99
100
>>> func(100,99)
100
99
#传入的实参99和100,是按照a和b的位置顺序来匹配赋值的
>>> func(99)
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
func(99)
TypeError: func() missing 1 required positional argument: 'b'
#参数的顺序和个数要和函数定义中一致,有两个形参就要有两个实参
演示二:
>>> def power(x, n):
s=1
while(n > 0):
n -= 1
s *= x
return s
>>> power(2,3)
8
#函数power(x,n)中有两个参数—— x和n,这两个参数都是位置参数,
调用函数的时候,传入的两个实参2和3,按照顺序,依次赋值给x和n。
2.可选参数(默认值参数)
作用:最大的好处就是降低调用函数的难度。
1.在定义函数时,如果有些参数存在默认值,即部分参数不一定需要调用程序输入,可以在定义函数时直接为这些参数指定默认值,则使用更少的参数调用。
2.当函数被调用时,如果没有传入对应的参数值,则使用函数定义时的默认值替代。
>>> def funcA(a,b=2): #b就是默认参数,默认值为2
print(a)
print(b)
>>> funcA(3) #对应第二条。因为按顺序调用赋值,则a=3,b没传入值就输出默认值2。
3
2
>>> funcA(3,4) #b有传入值时输出传入值
3
4
Tips:
- 必选参数(位置参数)在前,可选参数(默认参数)在后。
- 当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。
- 默认参数一定要指向不变对象!指向不变对象!指向不变对象!
(注意:python中的字符串,数字,元组都可以看做对象。)
(划重点)假如默认参数是可变对象(如列表,字典或大多数类的实例)是会有副作用的,或者说一个函数参数的默认值,仅仅在该函数定义的时候,被赋值一次.如此,只有当函数第一次被定义的时候,才将参数的默认值初始化到它的默认值(如一个空的列表)。* |
>>> def f(a,L=[]): #L是一个空列表
L.append(a)
return L
#函数参数不传递时默认值总是指向固定的内存空间,就是第一次计算的空间。
>>> print(f(1))
[1]
>>> print(f(2))
[1, 2]
>>> print(f(3))
[1, 2, 3]
如果不希望在后续调用之间共享默认值,则可以编写如下函数:
>>> def f(a,L=None):
if L is None:
L= []
L.append(a)
return L
>>> print(f(1))
[1]
>>> print(f(2))
[2]
>>> print(f(3))
[3]
3.可变参数(可变数量参数)
定义:可变参数就是允许在调用参数的时候传入多个(≥0个)参数(类似于列表、字典)
作用:就是可以一次给函数传很多的参数
特征:*args 、**kw
- 带有星号*的可变参数只能出现在参数列表的后面。调用时,这些参数被当作元组类型传递到函数中。
- 在函数定义时,也可以通过在参数前增加带双星号(**)的参数,向函数传递可变数量的参数。带有双星号的可变参数只能出现在参数列表的最后。调用时,**后的那些参数被当作字典类型传递到函数中。(*c,**d)
>>> def func(*args): #这种定义会把传递的参数包成元组
print(args,type(args))
>>>func(10,20)
#结果:
(10, 20) <class 'tuple'>
#举一个和上述过程相反的例子:
>>>def func(a,b):
print('a=%d, b=%d' % (a,b) )
>>>a = (10, 20)
>>>func(*a) #在调用函数使用`*`则会把元组解包成单个变量按顺序传入函数
#结果:
a=10, b=20
总结:*号在定义函数参数时,传入函数的参数会转换成元组,如果 *号在调用时则会把元组解包成单个元素。 |
另一种定义:
>>>def func(**kw): #使用**定义参数会把传入参数包装成字典dict
print(kw, type(kw) )
>>>func(a=10,b=20) #这种函数在使用时必须指定参数值,使用key=value这种形式
#结果:
{'b': 20, 'a': 10} <class 'dict'>
#相反的例子:
>>>def func(a,b):
print('a=%d, b=%d' % (a,b) )
>>>d = {'a':10, 'b':20 }
>>>func(**d) #在调用时使用**会把字典解包成变量传入函数。
a=10, b=20
总结:**号在定义函数参数时,传入函数的参数会转换成字典,如果 **号在调用时则会把字典解包成单个元素。 |
>>> def c(*nums):
sum = 0
for n in nums:
sum += n
return sum
>>> c(1,2,3)
6
>>> my = [1,2,3]
>>> c(*my)
6
4.关键字参数
关键字参数就是在调用函数,传入实参时指定形参的变量名。
Tip:
- 关键字(keyword)传递是根据每个参数的名字传递参数。
- 关键字不用遵守位置的对应关系。由于调用函数时指定了参数名称,所以参数之间的顺序可以任意调整。
- 关键字传递可以和位置传递混用。但位置参数要在关键字参数之前。
- 关键字参数的作用:扩展函数的功能
- 特征:
**kw
。表示它接收包含所有关键字参数的字典,且放在最后。
>>> def hello(name,age = 11,gender = 'F'):
#age = 11,gender = 'F'就是关键字参数
print('User Info:')
print('name is %s' % name)
print('age is %d' %age)
print('gender is %c'%gender)
>>> hello('Jim',age = 12)
User Info:
name is Jim
age is 12
gender is F
>>> hello('Jim', age=11, gender='M')
User Info:
name is Jim
age is 11
gender is M
>>> hello('Jim',gender='M',age = 11)
User Info:
name is Jim
age is 11
gender is M
>>> hello(age = 11,'Jim') #位置参数要出现在关键字参数之前
SyntaxError: positional argument follows keyword argument
可变参数允许传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
而关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。在调用函数时,可以只传入必选参数。
>>> def person(name,age,**kw):
print('name:', name, 'age:', age, 'other:', kw)
#函数person除了必选参数name和age外,还接受关键字参数kw。
#在调用该函数时,可以只传入必选参数:
>>> person('Michael', 30)
name: Michael age: 30 other: {}
#也可以传入任意个数的关键字参数:
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:
#定义一个字典数据
>>>dictArray = {'city': 'Beijing', 'job': 'Engineer'}
#调用函数
>>>person('Jack', 24, **dictArray )
#输出结果
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
#解释:**dictArray表示把dictArray这个dict的所有key-value用关键字参数传入
到函数的**kw参数,kw将获得一个dict。注意kw获得的dict是dictArray的一份拷贝,
对kw的改动不会影响到函数外的dictArray。
5.命名关键字参数(强制命名参数keyword-only)
- 和关键字参数
**kw
不同,命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数。如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了。 - 在带星号的参数后面申明参数会导致强制命名参数(Keyword-only)。调用时必须显式使用命名参数传递值,因为按位置传递的参数默认收集为一个元组,传递给前面带星号的可变参数。
- 如果不需要带星的可变参数,只想使用强制命名参数,可以简单地使用一个星号。例如:def my_func( *, a, b, c)
>>> def f(*c,a,b):
return a+b
>>> f(a=3,b=5)
8
>>> def f(*,a,b):
return a+b
>>> f(a=3,b=5)
8
>>> def f(*,a,b,rate=0.3):
return (a+b)*rate
>>> f(a=3,b=5)
2.4
>>> f(a=3,b=5,rate = 0.5)
4.0
例二
#这里星号分割符后面的city、job是命名关键字参数
>>> def person_info(name, age, *, city, job):
print(name, age, city, job)
>>> person_info("Alex", 17, city = "Beijing", job = "Engineer")
Alex 17 Beijing Engineer #看来这里不再被自动组装为字典
#函数定义中有了一个可变参数,后面跟着的命名关键字参数就不需要特殊分隔符`*`了。
#args是变长参数,而city和job是命名关键字参数
>>>def person_info(name, age, *args, city, job):
print(name, age, args, city)
>>>person_info("Liqiang", 43, "balabala", city = "Wuhan", job = "Coder")
Liqiang 43 balabala Wuhan Coder
函数与过程
- 函数:有返回值
- 过程:无返回值
- Python中只有函数,没有过程。如果没有return语句,默认返回None。
- 变量的返回值:
return语句用来退出函数并将程序返回到函数被调用的位置继续执行。return语句同时可以将0个、1个或多个函数运算完的结果返回给函数被调用处的变量。
>>> def func(a, b):
return a*b
>>> s = func("knock~",2)
>>> print(s)
knock~knock~
函数可以没有return,此时函数并不返回值。函数也可以用return
返回多个值,多个值以元组类型保存
>>> def func(a, b):
return b,a
>>> s = func("knock~",2)
>>> print(s,type(s))
(2, 'knock~') <class 'tuple'>
lambda表达式
lambda表达式就是一种简单的函数
形如 f = lambda 参数1,参数2: 返回的计算值
>>>add = lambda x,y: x+y
>>>print(add(1,2))
#结果:
3
列表.sort()与全局sorted()比较
- sort(): python list的内置方法
sort(key=None,reverse=False) 就地改变列表; 但其返回值为None
reverse:True反序;False 正序 - sorted(): python内置的全局方法
sorted(iterable,key=None,reverse=False),返回新的列表,对所有可迭代的对象
均有效
>>> x= [3,7,5]
>
>>> print(x.sort())
None
>>> print(sorted(x))
[3, 5, 7]
reverse() 和reversed()的区别
- reverse():列表list的内置方法
用于列表中数据的反转
>>> l = [1,2,3,4]
>>> l.reverse()
>>> print(l)
[4, 3, 2, 1]
其实,list.reverse()这一步操作的返回值是None
,结果需要通过打印被作用的列表后才能知道。
- reversed():Python 自带的一个方法,或说是一个类。
reversed(sequence) - > 反转迭代器的序列值
返回反向迭代器
即经过reversed()的作用后,返回的是一个把序列值经过反转后的迭代器
所以需要通过遍历,或者list,或者next()等方法,获取作用后的值。
#列表的反转
>>> bb = [1,2,4,6]
>>> reversed(bb)
<list_reverseiterator object at 0x000002CE2F8787B8>
>>> list(reversed(bb))
[6, 4, 2, 1]
#元组的反转
>>> aa = (1,2,3)
>>> reversed(aa)
<reversed object at 0x000002CE2F846D68>
>>> tuple(reversed(aa))
(3, 2, 1)
函数变量的作用域
- 局部变量
- 全局变量
- global:若确需在函数中直接修改全局变量的值,可以使用global关键字。
Tip:global关键字用来在函数或其他局部作用域中使用全局变量。但是如果不修改全局变量也可以不使用global关键字。
>>> a = 5 #这个a是全局变量
>>> def f4(b,c):
a = a+b*c #这个a是局部变量
return a #这个a也是局部变量
>>>print(a)
5 #返回全局变量a的值
>>> f4(2,3) #因为经过函数作用之后的a的值改变了,不等于5,则报错
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
f4(2,3)
File "<pyshell#12>", line 2, in f4
a = a+b*c
UnboundLocalError: local variable 'a' referenced before assignment
#global 的力量
>>> def f5(b,c):
global a
a = a+b*c
return a
>>> f5(2,3)
11
>>> print(a)
11