将可能需要反复执行的代码封装为函数,并在需要该段代码功能的地方调用,不仅可以实现代码的复用,更重要的是可以保证代码的一致性,只需要修改该函数代码则所有调用均受到影响。
5.1 函数定义与调用
语法:
def 函数名([形式参数表]):
注释
函数体
** 注意事项**:
- 函数形参不需要声明类型,也不需要指定函数返回值类型。
- 即使该函数不需要接收任何参数,也必须保留一对空的圆括号。
- 括号后面的冒号必不可少
- 函数体相对于def关键字必须保持一定的空格缩进
-
Python允许嵌套定义函数
5.2 形参与实参
- 函数定义时括弧内为形参,一个函数可以没有形参,但是括弧必须要有,表示该函数不接收参数。
- 函数调用时向其传递实参,将实参的值或引用传递给形参。
- 一般来说,在函数内直接修改形参的值不影响实参。
- 但是,列表、字典、集合等可变序列作为函数参数时,在函数内部使用下标或序列自身直刺的方式为可变序列增加、删除元素或修改元素值时,修改后的结果是可以反映到函数之外的,即实参也得到了相应的修改。
- 对于绝大多数情况下,在函数内部直接修改形参的值不会影响实参。例如下面的示例:
>>> def addone(a):
print(a)
a+=1
print(a)
>>> a=3
>>> addone(a)
3
4
>>> a
3
>>> def modify(v):
v[0]=v[0]+1
>>> a=[1,2,3]
>>> modify(a)
>>> a
[2, 2, 3]
5.3 参数类型
- 在Python中,函数参数有很多种:可以为位置参数、默认值参数、关键参数、可变长度参数等。
- Python函数的定义非常灵活,在定义函数时不需要指定参数的类型,也不需要指定函数的类型,完全由调用者决定。
- 函数编写如果有问题,只有在调用时才能被发现,传递某些参数时执行正确,而传递另一些类型的参数时则出现错误。
5.3.1 位置参数
位置参数(positional arguments),调用函数时实参和形参的顺序必须严格一致,数量必须相同。
>>> def demo(a,b,c):
print(a,b,c)
>>> demo(3,4,5) #按位置传递参数
3 4 5
>>> demo(3,5,4)
3 5 4
>>> demo(1,2,3,4) #实参与形参数量必须相同
Traceback (most recent call last):
File "", line 1, in
demo(1,2,3,4) #实参与形参数量必须相同
TypeError: demo() takes 3 positional arguments but 4 were given
5.3.2 默认值参数
语法:
def 函数名(......,形参名=默认值)
函数体
- 在调用带有默认值参数的函数时,可以不用为设置了默认值的形参进行传值,此时函数将会直接使用函数定义时设置的默认值,当然也可以通过显式赋值来替换其默认值。在调用函数时是否为默认值参数传递实参是可选的。
- 需要注意的是,在定义带有默认值参数的函数时,任何一个默认值参数右边都不能再出现没有默认值的普通位置参数,否则会提示语法错误。
>>> def f(a=3,b,c=5):
print(a,b,c)
SyntaxError: non-default argument follows default argument
>>> def f(a,b,c=5):
print(a,b,c)
>>> #调用带有默认值参数的函数时,可以不对默认值参数进行赋值,也可以赋值,具有较大的灵活性
>>> def say(message,times=1):
print(message*times)
>>> say('hello')
hello
>>> say('hello',3)
hellohellohello
- 函数的默认值参数是在函数定义时确定值的,所以只会被初始化一次。
>>> i=3
>>> def f(n=i):
print(n) #参数n的值仅取决于i的当前值
>>> f()
3
>>> i=5 #函数定义后修改i的值不影响参数n的默认值
>>> f()
3
>>> i=7
>>> f()
3
>>> def f(n=i): #重新定义函数
print(n)
>>> f()
7
- 注:可以使用函数名.func_defaults查看默认参数的当前值。
5.3.3 关键参数
- 关键参数主要指实参,即调用函数时的参数传递方式。
- 通过关键参数传递,实参顺序和形参顺序可以不一致,但不影响传递结果,避免了用户需要牢记位置参数顺序的麻烦。
>>> def demo(a,b,c=5):
print(a,b,c)
>>> demo(3,7)
3 7 5
>>> demo(c=8,a=9,b=0)
9 0 8
5.3.4 可变长度参数
- 可变长度参数主要有两种形式:parameter和*parameter,前者用来接受多个实参并将其放在一个元组中,后者接收字典形式的实参。
>>> def demo(*p):
print(p)
>>> demo(1,2,3)
(1, 2, 3)
>>> #第二种形式:
>>> def demo(**p):
for item in p.items():
print(item)
>>> demo(x=1,y=2,z=3)
('x', 1)
('y', 2)
('z', 3)
- 几种不同类型的参数可以混合使用,但是不建议这样做
>>> def func_4(a,b,c=4,*aa,**bb):
print(a,b,c)
print(aa)
print(bb)
>>> func_4(1,2,3,4,5,6,7,8,9,xx='1',yy='2',zz=3)
1 2 3
(4, 5, 6, 7, 8, 9)
{'xx': '1', 'yy': '2', 'zz': 3}
5.3.5 参数传递的序列解包
- 传递参数时,可以通过在实参序列前加星号将其解包,然后传递给多个单变量形参。
>>> def demo(a,b,c):
print(a+b+c)
>>> seq=[1,2,3]
>>> demo(*seq)
6
>>> tup=(1,2,3)
>>> demo(*tup)
6
5.4 变量类型与其作用域
- 变量起作用的代码范围称为变量的作用域,不同作用域内变量名可以相同,互不影响。
- 在函数内部定义的普通变量只在函数内部起作用,称为局部变量。当函数执行结束后,局部变量自动删除,不再可以使用。
- 局部变量的引用比全局变量速度快,应优先考虑使用。
5.4.1 局部变量
- 注意:在某个作用域内任意位置只要有为变量赋值的操作,该变量在这个作用域内就是局部变量,除非使用global进行了声明。
>>> x=3
>>> def f():
print(x) #此语句会引发异常,因为变量x现在还不存在
x=5 #有赋值操作,因此在整个作用域内x都是局部变量
print(x)
>>> f()
Traceback (most recent call last):
File "", line 1, in
f()
File "", line 2, in f
print(x) #此语句会引发异常,因为变量x现在还不存在
UnboundLocalError: local variable 'x' referenced before assignment
- 注意:如果局部变量与全局变量具有相同的名字,那么该局部变量会在自己的作用域内暂时隐藏同名的全局变量。
>>> def demo():
x=3
print(x)
>>> x=5
>>> x
5
>>> demo()
3
>>> x
5
5.4.2 全局变量
- 如果在函数内部修改一个定义在函数外的变量值,必须使用global明确声明,否则会自动创建新的局部变量。
- 一个变量已在函数外定义,如果在函数内需要为这个变量赋值,并要将这个赋值结果反映到函数外,可以在函数内使用global将其声明为全局变量。
- 如果一个变量在函数外没有定义,在函数内部使用global,直接将一个变量定义为全局变量,该函数执行后,将增加一个新的全局变量。
5.4.3 案例
>>> def demo():
x=3
print(x)
>>> x=5
>>> x
5
>>> demo()
3
>>> x
5
>>> def demo():
global x #声明或创建全局变量
x=3 #修改全局变量的值
y=4 #局部变量
print(x,y)
>>> x=5 #在函数外定义了全局变量x
>>> demo() #调用修改了全局变量x的值
3 4
>>> x
3
>>> y #局部变量在函数运行完自动删除
Traceback (most recent call last):
File "", line 1, in
y #局部变量在函数运行完自动删除
NameError: name 'y' is not defined
>>> del x #删除全局变量
>>> x
Traceback (most recent call last):
File "", line 1, in
x
NameError: name 'x' is not defined
>>> demo() #本次调用创建了全局变量
3 4
>>> x
3
>>> y
Traceback (most recent call last):
File "", line 1, in
y
NameError: name 'y' is not defined
5.5 lambda表达式
- lambda表达式可以用来声明匿名函数,即没有函数名字的临时使用的小函数,只可以包含一个表达式,且该表达式的计算结果为函数的返回值,不允许包含其他复杂的语句,但在表达式中可以调用其他函数。
>>> f=lambda x,y,z:x+y+z #可以为lambda表达式起名字
>>> f(1,2,3) #像函数一样调用
6
>>> g=lambda x,y=2,z=3:x+y+z #参数默认值
>>> g(1)
6
>>> print(g(2,z=4,y=5)) #关键参数
11
>>> L=[(lambda x:x**2),(lambda x:x**3),(lambda x:x**4)]
>>> print(L[0](2),L[1](2),L[2](2))
4 8 16
>>> D={'f1':(lambda:2+3),'f2':(lambda:2*3),'f3':(lambda:2**3)}
>>> print(D['f1'](),D['f2'](),D['f3']())
5 6 8
5.6 高级话题
- 内置函数map可以将一个函数作用到一个序列或迭代器对象上。
>>> list(map(str,range(5)))
['0', '1', '2', '3', '4']
>>> def add5(v):
return v+5
>>> list(map(add5,range(10)))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
- 内置函数filter将一个函数作用到一个序列上,返回该序列中使得该函数返回值为True的那些元素组成的列表、元组或字符串。
>>> seq=['foo','x41','?!','***']
>>> def func(x):
return x.isalnum()
>>> filter(func,seq)
>>> list(filter(func,seq))
['foo', 'x41']
- 作业
- 一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如,6的因子为1、2、3,而6=1+2+3,因此6是完数。编程,找出1000之内的所有完数,并输出该完数及对应的因子。
- 判断101—200之间有多少个素数,并输出所有素数。(注:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数。)
- 请输出1000以内的斐波那契数列。