Python基础之函数(七)
使用def 语句定义一个函数a语法# 函数体# 进行一些操作return result # 可选的返回值说明def:用于声明一个函数,告诉 Python 这是一个函数的定义。:函数名,一个有效的标识符,规则和变量名一致。parameters:形参,可以是0 ~ n 个,参数之间用逗号分隔。函数体:定义函数执行的具体操作。return:指定函数的返回值,没有则返回None。return:之后的代码不
Python基础之函数(七)
文章目录
一、函数
函数的定义
使用def 语句定义一个函数a
- 语法
def function_name(parameters):
# 函数体
# 进行一些操作
return result # 可选的返回值
-
说明
- def:用于声明一个函数,告诉 Python 这是一个函数的定义。
- function_name:函数名,一个有效的标识符,规则和变量名一致。
- parameters:形参,可以是0 ~ n 个,参数之间用逗号分隔。
- 函数体:定义函数执行的具体操作。
- return:指定函数的返回值,没有则返回None。
- return:之后的代码不会执行。
-
示例
def add(a, b):
return a + b
二. 函数的调用
2.1 基本用法
-
语法
函数名(实际调用传递参数)
-
说明
函数调用是一个表达式,意思是他可以参与运算
-
示例
add(3, 5) # 直接调用
add(3, 5) + 6 #直接参与运算
2.2 调用传参
函数调用时传递参数的方式有多种,包括位置传参、关键词传参、多个参数解包、参数默认值等。
2.2.1 位置传参
最常见的传参方式,参数按定义的顺序依次传入函数。
示例:
# 传入的实参顺序和形参顺序必须一致
def fn(x1,x2):
print("x1:",x1)
print("x2:",x2)
fn(1,2)
fn(2,1)
fn(x2 = 1,x1 = 2)
fn(1,x2 = 2) #可行,位置参数+关键字参数
#fn(x2 = 2,1) #报错,关键字参数+位置参数,不可行
def fn4(x1,x2,x3):
print(x1,x2,x3)
fn4(1,2,x3=3) #可行
# fn4(x1=-1,2,x3=3) #报错
2.2.2 关键词传参
通过指定参数的名称来传值,无顺序限制,代码可读性较高。
示例:
def greet(name, age):
print(f"Hello, {name}. You are {age} years old.")
greet(age=30, name="Alice")
def fn(x1,x2):
print("x1:",x1)
print("x2:",x2)
fn(1,2)
fn(2,1)
fn(x2 = 1,x1 = 2)
fn(1,x2 = 2) #可行,位置参数+关键字参数
2.2.3 参数默认值
定义函数时可为某些参数指定默认值,如果不传参则使用默认值。默认值参数必须位于无默认值参数的后面。
示例:
# 参数默认值
def fn(a,b,c=100):
print(a,b,c)
fn(1,2) # 1 2 100
fn(1,2,3) # 1 2 3
fn(1,None,c=3) # 1 None 3
# # 设计函数时候,默认值参数必须放在最后面,否则会报错
# def fn(a,b=100,c):
# print(a,b,c)
# fn(1,2,3) #报错
2.2.4 可变位置参数和可变关键词参数
- 使用 ∗ a r g s *args ∗args 可让函数接受任意数量的位置参数。 ∗ a r g s *args ∗args 会将多余的位置参数收集成一个元组。
示例:
# 元组解包参数 *args
def fn(x1,x2,*x):
print(x1,x2)
print(x,type(x))
fn(10,20) # 10 20 ((),) <class 'tuple'>
fn(1,2,3,4,5,6) # 1 2 (3, 4, 5, 6) <class 'tuple'>
def fn2(*x):
print(x,type(x))
fn2() # () <class 'tuple'>
fn2(1,2,3,4,5,6) # (1, 2, 3, 4, 5, 6) <class 'tuple'>
- 使用 ∗ ∗ k w a r g s **kwargs ∗∗kwargs 可以让函数接受任意数量的关键词参数。 ∗ ∗ k w a r g s **kwargs ∗∗kwargs 会将多余的关键词参数收集成一个字典。
示例:
# 可变关键词参数(字典解包参数) **args
def fn(a, b, c, **args):
print(a, b, c)
print(args)
fn(10,20,30,name='zhangsan',age=18)
# 10 20 30
#{'name': 'zhangsan', 'age': 18}
fn(1,2,3)
# 1 2 3 {}
2.2.5 多参数解包
Python 允许在调用函数时解包序列或字典,使其作为位置参数或关键词参数传递给函数。
2.2.5.1 解包位置参数
使用 ∗ * ∗ 解包
# 传入时解包
# *x,解包序列
def fn(a,b,c):
print(a,b,c)
x = [1,2,3]
fn(x[0],x[1],x[2])
fn(*x) # *x就是解包x序列的元素,按顺序传入函数
# x2 = [4,5,6,7]
# fn(*x2) #报错,解包后数据多了,反之少了也不行
x3 =(4,5,6)
fn(*x3) #只要有下标的序列都可以解包
2.2.5.2 解包关键词参数
使用 ∗ ∗ ** ∗∗ 解包
# 传入时解包
# **解包字典
def fn(a,b,c):
print(a,b,c)
s = {'a':1,'c':2,'b':3}
fn(a=s['a'],b=s['b'],c=s['c']) #1 3 2
fn(**s) #1 3 2
fn(**{'a':1,'b':2,'c':3}) #1 2 3
2.2.6 参数混合使用
在函数调用时,可以混合使用位置参数和关键词参数,但位置参数必须放在关键词参数前面。
示例:
# (*) 参数
# (*) 本身不是一个参数,*只是一个占位符,它后面的参数必须是关键字参数
def fn(a, b, *, c = 10):
print(a, b, c)
# fn(1, 2, 3) #报错,要求*后面的参数必须是关键字,也就是c要是关键字参数
fn(1, 2, c=3) #正确,1 2 3
fn(1, b=2, c=3) #正确,1 2 3
fn(1,2) #正确,1 2 10
三. 可变和不可变参数
在 Python 中,实参可以是可变类型或不可变类型。它们的区别主要体现在值传递和引用传递的行为上。
3.1 不可变类型
不可变类型包括: i n t 、 f l o a t 、 s t r 、 t u p l e 、 f r o z e n s e t int、float、str、tuple、frozenset int、float、str、tuple、frozenset 等。
传递方式是值传递: 传递给函数的是该对象的值,函数内部修改该值不会影响外部变量的值。
示例:
def modify(x):
print('修改之前:', x, id(x))
x = 10 # 修改了 x 的值,但不会影响外部变量
print('修改之后:',x, id(x))
a = 5
modify(a)
print('原始数据:',a, id(a))
打印结果:
修改之前: 5 140722275297848
修改之后: 10 140722275298008
原始数据: 5 140722275297848
3.2 可变类型
可变类型包括: l i s t 、 d i c t 、 s e t list、dict、set list、dict、set 等。这些类型的对象可以在原地修改。
传递方式是引用传递 :传递给函数的是对象的引用(即内存地址),在函数内部修改该参数的内容会直接影响外部变量。
示例:
def modify(lst):
print('修改之前:', lst, id(lst))
lst.append(4) # 修改了 lst 对象的内容
print('修改之后:',lst, id(lst))
a = [1, 2, 3]
modify(a)
print('外部原始数据:',a, id(a))
打印结果:
修改之前: [1, 2, 3] 2123053058816
修改之后: [1, 2, 3, 4] 2123053058816
外部原始数据: [1, 2, 3, 4] 2123053058816
3.3 避免副作用
通过复制对象来避免可变类型副作用:
- 列表:使用
lst.copy()
或切片lst[:]
来创建一个副本。 - 字典:使用
dict.copy()
或copy.deepcopy()
来进行深拷贝。 - 元组:推荐用
tuple
创建新的元组,虽然本身就是不可变。
示例:
def modify(lst):
lst_copy = lst.copy() # 创建副本
lst_copy.append(4) # 修改副本
a = [1, 2, 3]
modify(a)
print(a) # 输出: [1, 2, 3] (外部列表不受影响)
# 通过设计函数的时候可以看出来 传入一个可以访问下标的数据容器 函数运行的时候才不会报错
def fn2(arr):
arr[0]=100
x=[10,20,30]
fn2(x)
print(x)
# 解决方案:如果传入一个可变数据,函数内部想操作它,又不想改变外部传入的可变数据
def fn3(s):
s = s.copy()
s[0]= 1
print(s)
x = [10,20]
fn3(x)
print(x) # [10,20]
四、匿名函数
匿名函数是没有名字的函数,通常用于需要一个简短的、临时的函数场景,它可以有任意数量的参数,但只能包含一个表达式,并返回该表达式的结果。
4.1 基本语法
lambda arguments: expression
- arguments:一个或多个输入参数,可以是位置参数或关键词参数。
- expression:一个单一的表达式,它的值将作为返回值返回。
4.2 匿名函数定义
# 定义一个 lambda 函数,接收两个参数 a 和 b,返回它们的和
add = lambda a, b: a + b
print(add(3, 5)) # 输出: 8
fn = lambda x, y: x + y
print(fn(1, 2))
fn2 = lambda x:"奇数" if x % 2==1 else "偶数"
print(fn2(2))
五、常见的官方内置函数
-
all()
:如果可迭代对象中的所有元素都为True
,返回True
,否则返回False
。 -
sum()
:返回可迭代对象中所有元素的总和。 -
sorted()
:返回一个新列表,其中包含可迭代对象中的元素,按照升序排序。
示例
# 官方内置函数
x = [10,20,30,0]
re = all(x)
print(re)
re = any(x)
print(re)
print(sum(x))
print(sum(x)/len(x))
sorted(x) # 传入数据排序默认升序
print(x)
x = [-10,20,-30,0]
def fn(x):
print(x)
return x
re = sorted(x,key=fn)
print(re) #[-30, -10, 0, 20]
print(sorted(x,key=lambda x:x))
# abs() 函数,求绝对值
print(abs(-10))
-
reversed()
:返回一个反向迭代器。 -
callable()
:检查对象是否可以被调用(即是否是函数或方法)。 -
zip()
:将多个可迭代对象打包成一个元组,常用于并行遍历多个序列。 -
eval()
:将字符串作为有效的 Python 表达式来执行,并返回结果。 -
exec()
:执行存储在字符串中的 Python 代码。 -
globals()
和locals()``globals()
:返回当前全局符号表(一个字典);locals()
返回当前局部符号表(也是字典)。 -
filter()
:从可迭代对象中过滤出符合条件的元素。
示例
# reversed(x)反转x函数,返回一个可迭代对象
x = [1, 2, 3, 40, 5]
re = reversed(x)
re = list(re)
print(re) # [5, 40, 3, 2, 1]
# callable(x)判断x是否可调用,返回布尔值,检测x标识符
fn = lambda x: x + 1
print(callable(fn)) # True
fn = 100 # 不可调用
print(callable(fn)) # False
# zip(x, y)函数,把x,y两个可迭代对象组合成一个可迭代对象
x = [1, 2, 3]
y = [4, 5, 6]
z = zip(x, y) # 允许x,y两个可迭代对象长度不一致
print(z) # <zip object at 0x0000012FCE354080>
z = list(z)
print(z) # [(1, 4), (2, 5), (3, 6)]
# eval(x)函数,执行x字符串,返回执行结果
x = 'print(1+1)'
eval(x) # 2
# exec(x)函数,执行x字符串
x = 'print(1+3)'
exec(x) # 4
exec('for i in range(10):print(i, end=" ")') # 0 1 2 3 4 5 6 7 8 9
# globals()函数,返回全局变量名字
print(globals())
# locals()函数,返回局部变量名字
def fn():
x = 100
y = 200
print(locals())
fn() # {'x': 100, 'y': 200}
六. 高阶函数
高阶函数是指可以接受一个或多个函数作为参数,或者返回一个函数作为结果的函数。
6.1 常见高阶函数
比较常用,单独拎出来熟悉一下
6.1.1 map
map(function, iterable)
map函数:
- 接受一个函数和一个可迭代对象
- 将接受的函数应用到可迭代对象的每个元素上
- 返回一个包含结果的迭代器
6.1.2 filter
filter(function, iterable)
filter函数:
- 接受一个函数和一个可迭代对象
- 用接受的函数来筛选出可迭代对象中满足条件的元素
- 返回一个包含满足条件的元素的迭代器
6.1.3 reduce
reduce(function, iterable[, initializer])
reduce函数:
- reduce函数接受一个函数和一个可迭代对象
- 将接受的函数累积地应用到可迭代对象的元素上
- 可选的 i n i t i a l i z e r initializer initializer 参数可以作为累积的初始值
示例
# filter(函数,可迭代对象),传入两个参数:函数和可迭代对象,筛选
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
x2 = filter(lambda x: x % 2 == 0, x)
print(list(x2)) # [2, 4, 6, 8, 10]
# map(函数,可迭代对象),传入两个参数:函数和可迭代对象,每个数据都会被处理
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
x2 = map(lambda x: x * 2, x)
print(list(x2))
# reduce(函数,可迭代对象),传入两个参数:函数和可迭代对象
from functools import reduce
x = [1,2,3,4,5]
def fn(x,y):
print(x,y)
return x + y
reduce(fn,x,0) # 0是初始值,可以是任何数字
七、变量作用域
变量的作用域(Scope)是指在程序中某个变量的有效范围,也就是在代码的哪个部分可以访问或修改该变量。作用域定义了变量的可见性和生命周期。
Python中的变量作用域遵循LEGB规则(Local, Enclosing, Global, Built-in),依次搜索变量的定义位置。
7.1 Local→局部作用域
-
指函数或方法内部定义的变量。
-
仅在函数内部有效,函数外部无法访问。
-
在函数调用时被创建,在函数调用后自动销毁。
7.2 Enclosing→嵌套作用域
-
指外层函数中的变量,在内层函数中可访问,但不可修改。
-
当一个函数嵌套在另一个函数内部时,外层函数的变量属于Enclosing作用域。
-
nonlocal:将局部作用域中变量声明为外部嵌套函数作用域中的变量
7.3 Global→全局作用域
-
指模块级别定义的变量,整个模块都可以访问。
-
如果想在函数中修改全局变量,需要使用
global
关键字。 -
示例:如何用一个变量来记录一个函数调用的次数
7.4 Built-in→内建作用域
-
包含Python内建的函数、异常和常量,如
print()
,len()
,int
,Exception
等。 -
这些变量可以在任何地方使用。
-
示例:
7.5 变量查找顺序
作用域遵循LEGB规则(Local, Enclosing, Global, Built-in)
7.6 示例
示例一:
# 函数也是标识符 也是一种数据 也是跟变量一样的 关于作用域
a = 100
print(a) # 100
b = [10,202,30,lambda x:x+1]
print(b[0]) # 10
print(b[3](100)) # 101
示例二:
# # 作用域-函数的调用
# # 函数调用在A作用域中,函数定义在B作用域中
# 函数无论在哪个作用域中调用执行,一定是在它定义的作用域下执行代码
a = 100
def fm():
print(a)
def fn():
a = 200
fm()
fn() # 100
示例三:
def t1():
h = 100
def t2():
print(h)
return t2
h = 200
re = t1()
re() # 100
示例四:
a = 10
def fn():
global a # a是全局变量的a
print(a)
a = 20 # 修改全局变量a的值
fn()
print(a) # 20
示例五:
a = 1
def fn():
a = 2
def fn2():
print(a) # 2
fn2()
fn()
print(a) # 1
示例六:
a = 10
def fn():
a = 20
def fn2():
global a
print(a) # 10
a = 30
fn2()
print(a) # 20
fn()
print(a) # 30
示例七:
a = 10
def fn():
a = 10
def fn2():
nonlocal a # 非局部变量a
print(a) # 10
a = 20
fn2()
print(a) # 20
fn()
print(a) # 10
八. 函数内存分配
1、将函数的代码存储到代码区,函数体中的代码不执行。
2、调用函数时,在内存中开辟空间(栈帧),存储函数内部定义的变量。
3、函数调用后,栈帧立即被释放。
示例:
# 函数内存分配
def func(a,b):
a = 20
b[0] = 50
a = 10
b = [30,10]
func(a,b)
print(a,b)
九、递归函数
递归函数通常由两个部分组成:
- 终止条件:用于确保递归能够停止,避免无限递归。
- 递归调用:函数在其内部调用自身,传递简化或减少的参数,以解决更小的子问题。
步骤分成三部:
- 定义递归函数:在函数体内调用函数自身。
- 设定终止条件:确保递归能终止。
- 逐步简化问题:通过传递较小的参数递归求解,直至终止条件满足。
以下是一些递归函数的例子:
def fn(a):
if a >= 10:
return 100
else:
return a*fn(a+1)
re = fn(1)
print(re) #36288000
一个例子和详细解释:
def fn(a):
if a>=5:
return 5
else:
return fn(a+1)
a=1
re=fn(a)
print(re)
伪代码="""
伪代码 主要用于分析程序的执行过程
假设 大括号就是作用域
global->scoped{
fn函数
a=1
re=fn函数执行后的结果
fn(a)==>执行产生作用域
fn(1)->scoped{
a=1
if a>=5:
return 5
else:
return fn(a+1)->结果fn(2)
}
fn(2)->scoped{
a=2
if a>=5:
return 5
else:
return fn(a+1)->结果fn(3)
}
fn(3)->scoped{
a=3
if a>=5:
return 5
else:
return fn(a+1)->结果fn(4)
}
fn(4)->scoped{
a=4
if a>=5:
return 5
else:
return fn(a+1)->结果fn(5)
}
fn(5)->scoped{
a=5
if a>=5:
return 5-->结果5
else:
return fn(a+1)
}
}
"""
十、思考题
#今天的作业: 测试题: x2不要求是迭代器 是列表就可以了 实现myfilter 和 mymap myreduce
```# myfilter 函数
def myfilter(fn, list):
re = []
for el in list:
if fn(el):
re.append(el)
return re
# mymap 函数
def mymap(fn, list):
result = []
for el in list:
result.append(fn(el))
return result
# myreduce 函数
def myreduce(fn,list):
re=list[0]
for i in list[1:]:
re=fn(re,i)
return re
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(myfilter(lambda el: el % 2 == 1, x)) # 输出:[1, 3, 5, 7, 9]
print(mymap(lambda el: el ** 2, x)) # 输出:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
x=[1,2,3,3,4]
def fn(a,b):
print(a,b,)
return a+b
re=myreduce(fn,x)
print(re)
更多推荐
所有评论(0)