声明:本内容非盈利性质,也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站,会尽量附上原文链接,并鼓励大家看原文。侵删。
1.8 函数
1.8.1 定义函数
定义一个函数的基本格式如下:
'''
def 函数名([参数,参数,...])
函数体(复用的代码)
定义函数的注意事项:1.必须使用关键字def;2.函数体注意缩进;3.函数名与():必须有
'''
# 定义函数,产生随机数
import random
def generate_random():
for i in range(10):
ran = random.randint(1, 20)
print(ran)
generate_random() # 调用函数
# 查看函数的内存地址
print(generate_random) # 打印函数名。打印结果为:<function generate_random at 0x000001F032CCC3A0> 英文部分为函数名,后面的16进制数为内存地址
python函数返回值的类型由return后的表达式决定,如果没有返回则函数没有return。
def add(x, y):
result = x + y
print(result) # 函数中用print可以让结果在控制台显示,但在函数体外部则不能打印结果,该结果没有从函数体中输出
return result # 返回值是可以将结果传递出去的,这个结果是可以被后续调用的
x = add(2, 3) # 函数调用的返回结果赋给一个变量,然后打印可以看到结果
# return返回值可以是多个,并且多个返回值可以用一个变量去接收,将以元组的形式赋给变量
# return有多个返回结果,也可以用多个变量去接收
python是弱类型的,参数和返回值都没有直接的类型限制,这增加了灵活性,但也导致可读性不好,因此python中制定了专门的类型暗示(type hint)。如下:
def add(a:int, b:int) -> int: # a:int,用于提示参数的类型; -> int,用于提示函数返回值的类型
return a+b
# 这种语法只起提示作用,并没有强制限制类型
1.8.2 参数传递
C、java、python的函数(或方法)参数传递都满足基本数据类型是值传递,组合(或引用)数据类型是地址传递。
如以下示例中,实参如果是基本数据类型,则经过函数(或方法)处理后实参变量的值是不变的;实参如果是组合(或引用)数据类型,则经过函数(或方法)处理后实参变量的值会发生改变。
def sum1(num):
num += 1
return num
def sum2(nums):
i = 0
while(i<len(nums)):
nums[i] += 1
i += 1
a = 10
sum1(a)
print(a)
b = [1, 2, 3, 4, 5]
sum2(b)
print(b)
# python中的组合数据类型删除引用的方式是用del variablename;
调用函数时传递参数的方式有两种:位置传递和名称传递。如下:
def fact(n, m):
s=1
for i in range(1, n+1):
s *= i
return s//m
# 使用以下两种方式调用都是可以将n赋为10且将m赋为5的
fact(10, 5)
fact(m=5, n=10)
1.8.3 可选参数函数
python中定义函数时,可以设置一些非必填的参数,当不传递这些参数时,它们可以取默认值。如下:
'''
定义一个可选参数函数
'''
def fact(n, m=1):
s=1
for i in range(1, n+1):
s *= i
return s//m
# 调用时,以下两种形式都是可以的
fact(3,2)
fact(3)
注意,定义可选参数函数时,所可选的参数一定要定义在必选参数后面。
1.8.4 可变参数函数
java与C中的可变参数类似于语言中的数组类型。而python中没有数组这个类型,取而代之的是各种序列(列表、元组、字符串、字典、集合)。
'''
元组型可变参数:
'''
# 可变参数的类型(元组型参数)
def add(*x):
s = 0
for i in x:
s+=i
print(s)
print(x) # 打印结果为一个空元组
# 可变参数的赋值类似于 a,*b = 1,2,3,4 的过程。当定义可变参数时,系统会默认准备一个元组接受传参。
# 可变参数与不可变参数同时使用时,要放在后面。
# 要将一个元组整体传参给add的做法:
t1 = (1,1,2,3,4)
add(*t1) # 采用此方式可以将元组传参给可变参数函数。*会执行一个拆包动作将实参元组中的元素释放,执行时重新装包到形参元组中
add(1, 2, 3, 4)
'''
字典型可变参数:
'''
# 关键字参数(字典型参数)
# 关键字参数:def add(a,b=10),其中b即为关键字参数
# 对关键字参数进行传值后会覆盖其默认值
# 赋值时可以直接指明形参,则实参就会赋给指定的形参。
# 当关键字参数的个数不确定时,函数可以作如下定义:
def func(**x): # 形参**x的形式即为多个关键字参数
print(a, b, c)
# **X的参数一量定义,则系统会默认准备一个字典,因此接受赋值时必须按key=value的形式进行。如下:
func(a=1, b=2, c=3) # 多个关键字参数的调用须按关键字参数的形式调用
# 要将一个字典整体传参给func的做法:
dict1 = {'a': 1, 'b': 3, 'c': 5, 'd': 7}
func(**dict1) # 采用此方式可以将字典传参给一个多关键字参数函数。**会执行一个拆包动作将实参字典中的元素释放,执行时重新装包到形参字典中
#关键字参数的预定义值即为关键字形式的默认值,重新赋值默认值将被覆盖
'''
列表类型可变参数:
'''
# 列表类参数
def func(a, *args):
print(a, args)
func(2, [1, 2, 3, 4]) # 输出结果:2 ([1,2,3,4],),此时整个列表将被当成一个元组中的元素
# 列表型参数的拆包与元组型参数是相同的,即:
l = [1, 2, 3, 4]
func(2, *l) # 输出结果:2 (1,2,3,4)
'''
各种类型参数混合使用的情况:
'''
# 混合参数使用示例:
def bb(a, *b, **c):
print(a, b, c)
bb(1) # 输出结果:1 () {}
bb(1, 2, 3) # 输出结果:1 (2,3) {}
bb(1, x=100, y=2) # 输出结果:1 () {'x':100,'y':2}
bb(1, 2, x=100) # 输出结果:1 (2) {'x':100}
bb(1, x=100, 3, 4) # 会报错,因为3,4的传递没有按定义的顺序
1.8.5 匿名函数(lambda函数)
python中可以使用lambda函数实现匿名函数。如下:
# 匿名函数:lambda函数
# 格式:lambda 参数1,参数2,... :表达式
s = lambda a, b: a + b
print(s) #打印结果表明:lambda式赋予的变量即为函数名
#调用
result = s(1,2) # 将1,2赋给a,b,将返回值赋给result
print(result)
print(s(1, 2))
匿名函数可以用于传参:
# 匿名函数作参数
def func(x,y,func):
print(x,y)
print(func)
s = func(x,y)
print(s)
# 调用
func(1, 2, lambda a,b: a + b)
使用匿名函数可以使代码更简洁。下面是使用匿名函数的例子:
'''
max()函数用于列表元素比较大小:
'''
list1 = [3,6,5,9,7,8]
m = max(list1)
print(m)
# 但当列表中元素为字典时,max应如下使用:
list2 = [{'a': 3, 'b': 6, 'c': 7}, {'a': 5, 'b': 9, 'c': 2}, {'a': 7, 'b': 4, 'c': 1}]
m = max(list2, key=lambda x: x['a'])
# key是内置函数max的参数之一,它可以指定比较大小所采用的方式,这里我们指定取出字典中键a的值比较大小
'''
lambda函数与内置函数map实现列表中元素增值:
'''
list3 = [2,5,4,3,6,9,7,1]
result = map(lambda x: x + 2, list3)
print(result) # 直接输出返回一个map类型的对象
print(list(result)) # 可以通过list将map类型的对象转为list类型
'''
lambda函数与内置函数map实现奇数元素增值:
'''
result = map(lambda x: x if x % 2 == 0 else x + 1, list3)
print(list(result))
'''
lambda函数与reduce()结合对列表中元素进行加减乘除运算
'''
tuple1 = (3, 5, 7, 8, 9, 1)
result = reduce(lambda x, y: x + y, tuple1) # 将元组中所有的元素相加
# reduce函数的执行逻辑是:将3,5分别赋给x,y,相加的和作为x,又将下一个元素7作为y,依次进行直到结束
print(result)
tuple2 = (1,)
result = reduce(lambda x, y: x + y, tuple2) # 元组中只有一个元素也可以正常使用
# 以上将+号换成-*/也可以同样使用
'''
lambda函数与filter结合筛选数据
'''
list4 = [12, 6, 8, 98, 34, 36, 2, 0]
result = filter(lambda x: x > 10, list4)
print(list(result))
'''
lambda函数与内置排序函数sorted()结合:同样可以依照max函数那样依照指定方式排序
'''
list2 = [{'a': 3, 'b': 6, 'c': 7}, {'a': 5, 'b': 9, 'c': 2}, {'a': 7, 'b': 4, 'c': 1}]
stu = sorted(list2, key=lambda x: x['a'], reverse=True)
print(stu)
1.8.5内部函数(嵌套函数)
# 嵌套函数(内部函数):在函数体内再定义一个函数
a = 9
def func():
n = 100
list1 = [1, 2, 3, 4]
def inner_func(): # 作为内部定义的函数,其与func的局部变量是同一级的,可以对其进行操作
global a
nonlocal n
for index, i in enumerate(list1): # 单纯的遍历无法对列表中的元素作出更改,但用enumerate函数的遍历就可以
list1[index] = i + 5
n += 101
list1.sort()
a += 1
# 调用内部函数
inner_func()
print(list1)
print(n)
print(locals()) # locals可以查看当前函数声明的内容有哪些,以字典形式打印。即查看其所在函数体中所有的定义内容(包括变量与内部函数)
# 调用外部函数
func()
内部函数的特点:
- 内部函数可以访问外部函数的变量;
- 内部函数可以修改外部函数可变类型的变量;
- 内部函数要修改外部函数的局部变量,要通过nonlocal进行声明;内部函数修改全局变量,需要在内部函数的函数体中进行global声明;
- 内部函数只能在外部函数的内层进行调用;
- 不调用外部函数的情况下,内部函数在外部函数内层的调用不会生效。
函数嵌套的情况下,可能会涉及很多作用层次不同的变量。关于局部变量与全局变量、内部函数变量与外部函数变量的具体例子如下:
# 局部变量仅限于在函数内部使用
# 当一个局部变量与全局变量同名时,函数内部优先使用局部变量
# 局部变量可以在函数内部重赋值,但全局变量不能在函数内部随意作修改赋值
name = 'yueyue'
def func1():
global name #如果不作全局变量声明,则不能在函数内部对全局变量作赋值修改
print(name)
name += '会弹吉他'
def func2():
name = 'xiaoyueyue'
name += '弹吉他的小美女' #局部变量可以在函数内部作随意修改
print(name)
# 当全局变量为可变类型时无须声明全局变量
name = 'yueyue'
list1 = [1,2,3]
def func3():
global name # 如果不作全局变量声明,则不能在函数内部对全局变量作赋值修改
print(name)
name += '会弹吉他'
list1.append(8) #修改的list1为可变类型无须声明全局变量
print(list1)
# 同名的全局变量与局部变量不能同时在一个函数体内使用