5.函数
5.1函数的定义
在Python中,函数定义的格式如下:
def funcname(parameter_list):
pass
其中,funcname是函数名称,parameter_list是函数的参数列表,pass表示函数体。
'''this is run.py'''
def print_info(a):
print(a)
f = 1.234
print_info(f)
运行结果:
1.234
在Python中可以使用help来查看函数的信息。
'''this is run.py'''
def print_info(a):
print(a)
help(print_info)
help(print)
运行结果:
Help on function print_info in module __main__:
print_info(a)
Help on built-in function print in module builtins:
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
在上述代码中,查看了print_info的函数信息和内置函数print的函数信息。
函数具有3大特点:功能性、模块性、复用性。
功能性:函数的作用是为了实现某一具体的功能。
模块性:将多行代码存放在函数代码块中,使用函数名来调用。
复用性:函数可以使用在多个场景。
5.2函数名、参数列表和返回值
函数名不能和已有的函数名重复,不然会覆盖之前的函数定义。
例1:
'''this is run.py'''
i = 1
print(i)
def print(a):
a += 1
print(i)
运行结果:
1
第一个print是builtins模块内的函数,第二个print是我们自己创建的函数。
例2:
'''this is run.py'''
i = 1
#打印参数a
def print_info(a):
print(a)
print_info(i)
#打印参数a+1
def print_info(a):
print(a+1)
print_info(i)
运行结果:
1
2
函数的参数列表可以是对象,也可以是空对象。在其他编程语言中,可以使用void表示函数无参数;但是在Python中并没有void。
'''this is run.py'''
#打印1
def print_info():
print(1)
print_info()
运行结果:
1
函数的返回值有3种情况:(1)返回None,即没有返回值;(2)返回一个变量;(3)返回多个变量。
例1:返回None
'''this is run.py'''
#打印a
def print_info(a):
print(a)
i = 1.2345
print_info(i)
运行结果:
1.2345
例2:使用return返回None
'''this is run.py'''
#打印a
def print_info(a):
print(a)
return #或return None
i = 1.2345
val = print_info(i)
print(val)
print(type(val))
运行结果:
1.2345
None
<class 'NoneType'>
例3:返回一个变量
'''this is run.py'''
#计算a加b,并返回
def add(a,b):
return a+b
val = add(1,2)
print(val)
运行结果:
3
例4:返回多个变量
'''this is run.py'''
#计算a+1、b+1,并返回
def add(a,b):
return a+1,b+1
#方法1:
val = add(1,2)
print(val)
#方法2
val1,val2 = add(1,2) #序列解包
print(val1,val2)
运行结果:
(2, 3)
2 3
当函数返回多个变量时,若用一个变量来获取返回值,则此返回值是一个元组;若用相同数量的变量来获取返回值,则按位置赋予相应的数值。
例5:将返回的多个变量打包
'''this is run.py'''
#计算a+1、b+1,并返回
def add(a,b):
return [a+1,b+1]
#方法1:
val = add(1,2)
print(val)
val1, val2 = add(3,4)
print(val1,val2)
运行结果:
[2, 3]
4 5
使用return时,不仅可以将多个变量打包成列表,还可以是元组、集合。
5.3序列解包
问题1:在5.2节中,当函数返回多个变量时,发现实际上返回的是元组,为什么?
答案:元组的赋值和返回是不需要加括号的。
'''this is run.py'''
tup = 1,2,3,'hello'
print(tup)
print(type(tup))
运行结果:
(1, 2, 3, 'hello')
<class 'tuple'>
问题2:为什么元组(返回值)可以将对应位置的元素分别赋值给别的变量。
答案:不仅是元组,字符串、列表、集合、字典等都可以将对应位置的元素依次赋值给变量(但变量个数应该和元素个数相等)。我们将这种操作称为“序列解包”,虽然称呼不是很准确,因为序列只包括字符串、列表、元组。
'''this is run.py'''
#元组
tup = 1,2,'hello'
print(tup)
print(type(tup))
a,b,c = tup
print(a,b,c)
#字符串
s = "hello"
a,b,c,d,e = s
print(a,b,c,d,e)
#列表
l = [1,2,3]
a,b,c = l
print(a,b,c)
#集合
set1 = {1,2,3,4}
a,b,c,d = set1
print(a,b,c,d)
#字典
d = {1:'hello',2:'world'}
a,b = d
print(a,':',d[a],b,':',d[b])
a,b = d.items()
print(a,b)
运行结果:
(1, 2, 'hello')
<class 'tuple'>
1 2 hello
h e l l o
1 2 3
1 2 3 4
1 : hello 2 : world
(1, 'hello') (2, 'world')
5.4函数参数
讲到函数,几乎所有的编程语言都会涉及到2个概念:形参、实参。
在函数定义中的参数,称为形参;在函数实际调用时传递的参数,称为实参。
在Python中,参数有多种分类:必须参数、关键字参数、默认参数、可变参数等,这些参数在实际调用中又该怎么使用呢?
注:在Python中,函数中的形参定义、关键字定义等,不能以数字开头,必须以字母和_开头。
5.4.1必须参数和关键字参数
必须参数:在函数实际调用中,必须要传递的参数。
'''this is run.py'''
def person(name,age,profession):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
person('Jack',28,'teacher')
运行结果:
my name is Jack
I am 28
I am a teacher
在函数的实际调用中,实参必须和函数定义中的形参一一对应,不能多、不能少、不能顺序颠倒。
关键字参数:在函数的实际调用中,实参可以使用形参列表中的变量名称,来指定具体的参数。使用关键字参数,其参数顺序可以颠倒;但必须参数要放在关键字参数的前面,否则函数不知道这个必须参数对应哪一个形参。
'''this is run.py'''
def person(name,age,profession):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
person('Jack',profession='teacher',age = 28)
运行结果:
my name is Jack
I am 28
I am a teacher
5.4.2默认参数
参数顺序颠倒的问题可以用关键字参数解决了;但若是在函数实际调用中,参数写少了,该怎么办呢?
默认参数:在定义函数时,使用赋值运算符(=)为参数指定默认值。若实际调用中,此参数未传递实际数值,则使用默认值。
'''this is run.py'''
def person(name,age = 18,profession = 'student'):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
person('Jack')
运行结果:
my name is Jack
I am 18
I am a student
在函数定义时,若有一个参数设置成默认参数,则其右侧所有参数都必须定义成默认参数。
'''this is run.py'''
def person(name,age = 18,profession):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
person('Jack',profession = 'teacher')
运行结果:
File ".\run.py", line 3
def person(name,age = 18,profession):
^
SyntaxError: non-default argument follows default argument
原因:若参数profession没有定义成默认参数,那解释器不知道哪个默认参数没有传递。
在函数调用时,若有一个默认参数没有传递数值而使用默认值;则其右侧的默认参数若想传递数值,必须使用关键字参数。
'''this is run.py'''
def person(name,age = 18,profession = 'student'):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
person('Jack','teacher')
运行结果:
my name is Jack
I am teacher
I am a student
原因:函数错认为'teacher'传递给默认参数age。
'''this is run.py'''
def person(name,age = 18,profession = 'student'):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
person('Jack',profession = 'teacher')
运行结果:
my name is Jack
I am 18
I am a teacher
5.4.3可变参数
可变参数:在函数定义中,参数名称前加上星号(*),表示此参数为可变参数,其传入的实际参数可以是任意个。
'''this is run.py'''
def person(*param):
print('my name is '+str(param[0]))
print('I am '+str(param[1]))
print('I am a '+str(param[2]))
person('Jack',28,'teacher')
运行结果:
my name is Jack
I am 28
I am a teacher
当person中,传递的'Jack',28,'teacher',实际上是一个元组,即param = ('Jack',28,'teacher')。
'''this is run.py'''
def person(*param):
print(param)
print(type(param))
person('Jack',28,'teacher')
运行结果:
('Jack', 28, 'teacher')
<class 'tuple'>
问题1:可变参数可以放在默认参数后吗?
答案:不可以。因为当默认参数未传递数值时,后面可变参数的第一个数值将代替默认参数传入。
问题2:可变参数可以放在默认参数前吗?
答案:可以。当默认参数不传递数值时,会使用默认值;当默认参数传递数值时,必须使用关键字参数来指定。
例1:
'''this is run.py'''
def func(param1,*param2,param3 = '默认参数'):
print(param1)
print(param2)
print(param3)
func('必须参数',1,2,3,'可变参数')
运行结果:
必须参数
(1, 2, 3, '可变参数')
默认参数
例2:
'''this is run.py'''
def func(param1,*param2,param3 = '默认参数'):
print(param1)
print(param2)
print(param3)
func('必须参数',1,2,3,'可变参数',param3 = '关键字参数 + 默认参数')
运行结果:
必须参数
(1, 2, 3, '可变参数')
关键字参数 + 默认参数
5.4.4关键字可变参数
关键字可变参数:在函数定义中,参数名称前加上星号(**),表示此参数为关键字可变参数,其传入的实际关键字参数可以是任意个。
'''this is run.py'''
def func(**param):
print(param)
print(type(param))
func(n1 = '伊芙琳',n2 = '卡兹克',n3 = '李青',n4 = '伊莉丝')
运行结果:
{'n1': '伊芙琳', 'n2': '卡兹克', 'n3': '李青', 'n4': '伊莉丝'}
<class 'dict'>
5.4.5参数解包
在前面学习了序列解包,这一节会学习当函数参数是一个序列、集合、字典,又该如何传递呢?
'''this is run.py'''
#错误实例
def person(name, age, profession):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
tup = ('Jack',18,'student')
person(tup)
运行结果:
Traceback (most recent call last):
File ".\run.py", line 10, in <module>
person(tup)
TypeError: person() missing 2 required positional arguments: 'age' and 'profession'
为什么不可以,在前面不是说name, age, profession = ('Jack',18,'student')。
原因:在函数实际传递参数时,解释器并不知道要将元组('Jack',18,'student')解包成3个参数。
在前面的”可变参数"章节中,其代码如下。
'''this is run.py'''
def person(*param):
print(param)
print(type(param))
person('Jack',28,'teacher')
运行结果:
('Jack', 28, 'teacher')
<class 'tuple'>
可以看到param = ('Jack', 28, 'teacher'),而*param = 'Jack',28,'teacher'。因此,星号(*)在这里的作用是解包。同理,在关键字可变章节中,**的作用是为字典解包。
'''this is run.py'''
def person(name, age, profession):
print('my name is '+str(name))
print('I am '+str(age))
print('I am a '+str(profession))
tup = ('Jack',18,'student')
person(*tup)
def func(Jungle,Topsolo):
print('Jungle',':',Jungle)
print('Topsolo',':',Topsolo)
#Jungle:打野;Topsolo:上单
dic = {'Jungle':'伊芙琳','Topsolo':'武器大师'}
func(**dic)
运行结果:
my name is Jack
I am 18
I am a student
Jungle : 伊芙琳
Topsolo : 武器大师
5.5变量作用域
在Python中,只有2种变量:全局变量,局部变量。
全局变量:在函数体外定义的变量。
局部变量:在函数体内定义的变量。
例1:
'''this is run.py'''
def add(a,b):
c = 1.234
return a + b
my_sum = add (1,2)
print('my_sum = %s'%my_sum)
print('c = %.6f'%c)
运行结果:
my_sum = 3
Traceback (most recent call last):
File ".\run.py", line 10, in <module>
print('c = %.6f'%c)
NameError: name 'c' is not defined
原因:由于c是在函数体内定义的局部变量,因此在函数体外无法打印。
例2:
'''this is run.py'''
c = 1.234
def add(a,b):
print('c = %.6f'%c)
c = a + b
print('c = %d'%c)
return c
add(1,2)
print('c = %.3f'%c)
运行结果:
Traceback (most recent call last):
File ".\run.py", line 21, in <module>
add(1,2)
File ".\run.py", line 16, in add
print('c = %.6f'%c)
UnboundLocalError: local variable 'c' referenced before assignment
若定义的局部变量和全局变量名称相同,则不能在局部变量定义之前去打印全局变量。
例2.1:修改
'''this is run.py'''
c = 1.234
def add(a,b):
c = a + b
print('c = %d'%c)
return c
add(1,2)
print('c = %.3f'%c)
运行结果:
c = 3
c = 1.234
全局变量的作用域是所有模块都有效;局部变量的作用域是只在函数体内有效。若函数体定义的局部变量名称和全局变量相同,则在函数体内局部变量会覆盖全局变量。
例3:低级变量覆盖高级变量
'''this is run.py'''
a = 1
def print_info1():
a = 2
print('print_info1中的局部变量 a = %d'%a)
def print_info2():
a = 3
print('print_info2中的局部变量 a = %d'%a)
print_info2()
print('全局变量 a = %d'%a)
print_info1()
运行结果:
全局变量 a = 1
print_info1中的局部变量 a = 2
print_info2中的局部变量 a = 3
例4:for语句
'''this is run.py'''
l = [1,2,3,4]
for a in l:
b = 1.234
print(a)
print('a = %d'%a,'b = %.3f'%b)
运行结果:
1
2
3
4
a = 4 b = 1.234
在很多其他的编程语言中,把流程控制语句内的变量也定义为局部变量;但是在Python中,在流程控制语句中定义的变量也是全局变量。
例5:global关键字
'''this is run.py'''
def func():
global a
a = 1
func() #函数必须执行才会有a
print('a = %d'%a)
运行结果:
a = 1
在函数体内,可以使用global关键字来创建全局变量;需要注意的是,函数一定要执行了,才会去创建全局变量。