概念
函数是可以重复执行的语句块,可以重复调用
作用
用于封装语句块,提高代码的重用性。
定位
函数是面向过程编程的最小单位
使用def 语句定义一个函数a
#函数
#定义一个函数
def kn1():
print("kn1")
kn1()#运行函数内部的代码
kn1
def kn2(a,b):
print(a+b)
kn2(1,2)
3
函数的返回值,return
#函数的返回值,return
def kn3(a,b):
return a+b
re=kn3(1,2)
print(re)
3
最常见的传参方式,参数按定义的顺序依次传入函数。
#函数位置传参
#传入的实参的顺序与形参的顺序一一对应
def fn1(x1,x2):
print("x1",x1)
print("x2",x2)
fn1(1,2)
fn1(10,20)
#fn1(1,2,3)参数一致,多了一个参数,报错,
#fn1(1)少了也报错
x1 1
x2 2
x1 10
x2 20
def fn2(x1,x2,x3):
print(x1)
re=x1+x2
print(re)
fn2(1,2,3)#参数一致,但未使用,不影响函数执行
# fn2(1,2)#就算只使用了2个参数,的调用时也必须输入3个参数,否则报错、
1
3
2 1
通过指定参数的名称来传值,无顺序限制,代码可读性较高。
#关键字参数
def fn3(x1,x2):
print(x1,x2)
fn3(x2=1,x1=2)#指定了名字可以不按顺序传入参数
fn3("hello",x2="world")
# fn3(x1="hello","world")#位置参数不能出现在关键字参数后面
2 1
hello world
定义函数时可为某些参数指定默认值,如果不传参则使用默认值。默认值参数必须位于无默认值参数的后面。
#参数默认值
def fn(a,b=1):
print(a,b)
fn(1)
fn(1,2)
1 1
1 2
def fn(a,b=1,c=10):
print(a,b,c)
fn(1,c=2)
fn(10,20,30)
1 1 2
10 20 30
使用 ∗ a r g s *args ∗args 可让函数接受任意数量的位置参数。 ∗ a r g s *args ∗args 会将多余的位置参数收集成一个元组。
def fn(a,b,*args):
print(a,b,args)
for i in args:
print(i)
fn(10,20,30,41,25,52)
10 20 (30, 41, 25, 52)
30
41
25
52
使用 ∗ ∗ k w a r g s **kwargs ∗∗kwargs 可以让函数接受任意数量的关键词参数。 ∗ ∗ k w a r g s **kwargs ∗∗kwargs 会将多余的关键词参数收集成一个字典。
def fn(a,b,**arges):
print(a,b,arges)
for i in arges:
print(i)
fn(1,2,name='张三',age=18)
1 2 {'name': '张三', 'age': 18}
name
age
#*参数
#*本身不是一个参数,而是一个特殊用法,是一个占位符,
#c必须是关键字参数
def fn(a,b,*,c):
print(a,b,c)
fn(1,2,c=30)
1 2 30
def fn(a,b,c):
print(a,b,c)
fn(1,2,3)
fn(*[1,2,3])
1 2 3
1 2 3
def fn(a,b,c):
print(a,b,c)
fn(1,2,3)
fn(**{'a':1,'b':2,'c':3})
1 2 3
1 2 3
在 Python 中,实参可以是可变类型或不可变类型。它们的区别主要体现在值传递和引用传递的行为上。
不可变类型包括: 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 fn(a):
a=20
x=10
a=fn(x)
print(x)
10
可变类型包括: l i s t 、 d i c t 、 s e t list、dict、set list、dict、set 等。这些类型的对象可以在原地修改。
传递方式是引用传递 :传递给函数的是对象的引用(即内存地址),在函数内部修改该参数的内容会直接影响外部变量。
def fn2(arr):#传入数据是可变数据,
arr[0]=20
x=[1,2,3,4,5]
fn2(x)
print(x)
[20, 2, 3, 4, 5]
通过复制对象来避免可变类型副作用:
lst.copy()
或切片 lst[:]
来创建一个副本。dict.copy()
或 copy.deepcopy()
来进行深拷贝。tuple
创建新的元组,虽然本身就是不可变。示例:
# 解决方案:如果传入一个可变数据 函数内部又想操作它 又不想改变外部传入的那个可变数据
def fn3(arr):
arr=arr.copy()
arr[0]=100
x=[10,20,30]
fn3(x)
[20, 20, 30]
匿名函数是没有名字的函数,通常用于需要一个简短的、临时的函数场景,它可以有任意数量的参数,但只能包含一个表达式,并返回该表达式的结果。
lambda arguments(参数): expression(表达式)
lambda
函数通常用在需要一个短小函数的地方,而普通函数则更适合复杂的逻辑。lambda
函数是没有名字的,它们通常只在一个地方使用,并且不需要被重复调用。lambda
函数只能包含一个表达式,不可以包含多行语句,而普通函数可以包含多行代码、条件判断、循环等复杂逻辑。#匿名函数
fn = lambda x,y:x+y
print(fn(10,20))
30
fn2 = lambda x:"奇数"if x%2==1 else "偶数"
print(fn2(10))
偶数
如果可迭代对象中的所有元素都为 True
,返回 True
,否则返回 False
。
x=[10,20,30,0]
re=all(x)#x可迭代对象中的所有元素都判定True
print(re)
False
传入的数据容器中的数据 只要有一个数据判定为true就返回true,否则返回false
x=[10,20,30,0]
any(x)#传入的数据容器中的数据 只要有一个数据判定为true就返回true,否则返回false
print(any(x))
True
返回可迭代对象中所有元素的总和。
x=[10,20,30,0]
sum(x)#传入的数据容器中的数据 求和
print(sum(x))
print(sum(x)/len(x))
60
15.0
返回一个新列表,其中包含可迭代对象中的元素,按照升序排序。
x=[10,20,30,0]
sorted(x)#对传入的数据容器中的数据进行排序
print(sorted(x))
[0, 10, 20, 30]
按照中位数,或者平均数距离最近排序
#距离20最近的数字排前面
x=[-10,20,-30,0]
def fn(x):
print(x)
return (x-20)**2
re=sorted(x,key=fn)
print(re)
-10
20
-30
0
[20, 0, -10, -30]
返回一个反向迭代器。
x=[10,22,65,54]
re=reversed(x)
re=list(re)
print(re)
[54,65,22,10]
检查对象是否可以被调用(即是否是函数或方法)。
#callable(x)
fn=100
fn=lambda x:x+1
re = callable(fn)
print(re)
fn(11)
True
将多个可迭代对象打包成一个元组,常用于并行遍历多个序列。
#zip(x,y) 把x和y合并成一个迭代器
x=[1,2,3,4,5]
y=[10,20,30,40,50]
re=zip(x,y)#允许x和y长度不一样,多出的就不输出
print(re)
print(list(re))
[(1, 10), (2, 20), (3, 30), (4, 40), (5, 50)]
将字符串作为有效的 Python 表达式来执行,并返回结果。
eval("字符串中的代码")把传入的字符串中的代码执行,并返回结果‘’
执行存储在字符串中的 Python 代码。
# exec("字符串中的代码")把传入的字符串中的代码执行
exec("print('hello')")
exec("for i in range(10):print(i)")
globals()
返回当前全局符号表(字典);
locals()
返回当前局部符号表(字典)。
#gelobals()和locals()
#gelobals()返回全局变量,locals()返回局部变量
x=100
y=200
def fn():
x=300
y=400
print(globals())
print(locals())
fn()
{'x': 300, 'y': 400}
从可迭代对象中过滤出符合条件的元素。
#filter(x,y)传入函数和迭代器,返回满足条件的迭代器
x1=[1,2,3,4,5,6,7,8,9,10]
x2=filter(lambda x:x%2==0,x1)
print(list(x2))
[2, 4, 6, 8, 10]
高阶函数是指可以接受一个或多个函数作为参数,或者返回一个函数作为结果的函数。
map(function, iterable)
#map(x,y)传入函数和迭代器,返回迭代器
x1=[1,2,3,4,5,6,7,8,9,10]
x2=map(lambda x:x**2,x1)
print(list(x2))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
def mymap(fn,list):
re=[]
for i in list:
re.append(fn(i))#
return re
print(mymap(lambda x:x**2,[1,2,3,4,5]))
[1, 4, 9, 16, 25]
filter(function, iterable)
x1=[1,2,3,4,5,6,7,8,9,10]
x2=filter(lambda x:x%2==0,x1)
print(list(x2))
[2, 4, 6, 8, 10]
def myfilter(fn,list):
re=[]
for i in list:
if fn(i):
re.append(i)
return re
print(myfilter(lambda x: x>2, [1,3,5,0]))
[3, 5]
reduce(function, iterable[, initializer])
from functools import reduce
x=[1,2,3,3,4]
def fn(a,b):
print(a,b,)
return a+b
re=reduce(fn,x)#a是上一次的返回值 b是当前值 re是最后一次fn调用的返回值
print(re)
1 2
3 3
6 3
9 4
13
#自定义
def myreduce(fn,list):
re=list[0]
for i in list[1:]:
re=fn(re,i)
return re
#测试
x=[1,2,3,3,4]
def fn(a,b):
print(a,b,)
return a+b
re=myreduce(fn,x)
print(re)
1 2
3 3
6 3
9 4
13
变量的作用域(Scope)是指在程序中某个变量的有效范围,也就是在代码的哪个部分可以访问或修改该变量。作用域定义了变量的可见性和生命周期。
Python中的变量作用域遵循LEGB规则(Local, Enclosing, Global, Built-in),依次搜索变量的定义位置。
精解分析
# 变量的作用域(难点)
# 作用域 只针对函数 没有函数 就不谈作用域
# 函数内部可以访问函数内部和外部的变量 但是函数外部不能访问函数内部的变量
#变量在fn函数外面也能使用 fn函数里面也能使用 在别的函数里面也能使用
# 一个变量它在哪些地方能够使用哪些地方 哪些地方就是它起作用的代码区域=>作用域
#放在代码的最外层的变量 就是全局变量 哪些都能访问
#在函数内部访问一个变量时
# 如果是取值那么就在自己的局部作用域中访问
# 没有就去外层作用域访问 外层没有就去外层的外层 直到全局没有 就报错
# 如果是存值:在自己的作用域中找 没找到就在当前作用域内部创建
指函数或方法内部定义的变量。
仅在函数内部有效,函数外部无法访问。
在函数调用时被创建,在函数调用后自动销毁。
示例:
def fn():
x=10
print(x)
指外层函数中的变量,在内层函数中可访问,但不可修改。
当一个函数嵌套在另一个函数内部时,外层函数的变量属于Enclosing作用域。
示例:
def fg():
a=100
def fg2():
a1=300
a2=200
a1=666#a1是存值 在自己的作用域中找 没找到就在当前作用域内部创建
print(a,a1,a2)
# re=b1 在函数内部访问一个变量时,如果是取值的话,会从当前作用域中找,如果找不到,
# 就会从外部作用域中找,找不到会报错
print(re)
fg2()
fg()
100 666 200
13
指模块级别定义的变量,整个模块都可以访问。
如果想在函数中修改全局变量,需要使用global
关键字。
示例:如何用一个变量来记录一个函数调用的次数
a=20
def fn():
x=10
print(a)
包含Python内建的函数、异常和常量,如print()
, len()
, int
, Exception
等。
这些变量可以在任何地方使用。
示例:
print(len([1, 2, 3])) # 使用内建函数len
b=[10,20,30,40,lambda x:x+1]
print(b[0])
b[4]()
# xxx[1][2](3)(1)[4]
a=100
def fm():
print(a)
def fn():
a=200
fm()
fn()
def t1():
a=100
def t2():
print(a)
return t2
a=200
re=t1()#re是一个数据(函数)
re()#执行函数
100
100
a=1
def fn():
# print(a)#报错 因为在函数内部有a就不会去访问外部的a 但是 取值时如果这个变量还没有创建 就会报错
a=2#赋值 这个变量在当前作用域中没有 就会创建
fn()
print(a)
a=10
def fn():
global a#明确指定 a是全局变量的a
print(a)
a=20#会修改全局变量a的值
fn()
print(a)
a=10
def fn():
a=20
def fm():
print(a)
fm()
fn()
a=10
def fn():
a=20
def fm():
global a
print(a)
a=30
fm()
fn()
print(a)
a=10
def fn():
a=20
def fm():
nonlocal a
print(a)
a=30
fm()
print(a)
fn()
print(a)
1
10
20
20
10
30
20
30
10
将函数的代码存储到代码区,函数体中的代码不执行。
调用函数时,在内存中开辟空间(栈帧),存储函数内部定义的变量。
函数调用后,栈帧立即被释放。
def func(a, b):
a = 20
b[0] = 20
a = 10
b = [10 ,10 ,20 ]
func(a, b)
print(a) # 10
print(b) # [20]
10
[20, 10, 20]
递归是指函数直接或间接调用自身的过程。递归通常用于解决分解成相似子问题的复杂问题
def fm():
print("fm被调用了")
def fn():
#在这里是fn函数内部 那fn函数外部的全局作用域 有哪些标识符?
# 这里的代码可以访问a fm fn
# print(a)
# print(fm)
# print(fn)
# fm()
print("fn被调用了")
fn()
fn()
def fn(a):
if a>=5:
return 5
else:
return fn(a+1)
a=1
re=fn(a)
print(re)
5
# 求12的阶乘
def fm2(a):
if a<=1:
return 1
else:
return a*fm2(a-1)
re=fm2(5)
print(re)#
120
伪代码="""
伪代码 主要用于分析程序的执行过程
假设 大括号就是作用域
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)
}
}
"""