函数进阶
- 1、函数的本质:函数的本质是一个变量,函数名就是一个变量名,定义函数相当于定义变量。一个函数可以作为另一个函数的参数或返回值使用,只需要传递或返回函数名即可。
num = 10
print(num,type(num)) # 10 <class 'int'>
def func1():
pass
print(func1,type(func1)) # <function func1 at 0x102818f70> <class 'function'> 注意:这里打印的是函数名,可以喊出函数名指向一个函数。
#2.1、区别函数调用和函数名
print(abs(-30)) # 函数的调用,30
print(abs) # 函数本身,<built-in function abs> 注意:这里abs是一个内部(built-in)函数。
#2.2、进一步说明函数名的意义
r1 = abs(-66) #<built-in function abs>
print(r1) # 66
f1 = abs
print(f1) # <built-in function abs>
print(f1(-100)) #100 相当于调用abs
'''
c.可以修改函数名的指向,同时注意:自定义变量的时候,一定不要和系统的内置函数重名,
如:list(),dict(),tuple(),set(),int(),float(),str()等,否则会导致系统的内置函数失效
abs = True
print(abs(-35)) #TypeError: 'bool' object is not callable
'''
#2.3、函数作为函数参数
def show1(num1,num2,f):
print(f)
print(f(num1) + f(num2)) #这里决定了参数f的意义为一个function(python中先用后定义)
# show1(345,6,7) # TypeError: 'int' object is not callable
show1(34,-10,abs) #44
def a(n):
return n - 10
show1(34,45,a) #59
#2.4、函数名作为函数返回值
def show2():
return abs
r2 = show2() #注意这里show2()执行完之后,r2是一个abs函数名,但并没有执行
print(r2) #<built-in function abs> 显示函数
print(r2(-34)) #34
#2.5、函数调用作为函数返回值
def show2():
return abs(-134)
r2 = show2() #注意这里show2()执行完之后,r2是一个已经被执行的函数返回值
print(r2) #134 显示返回函数的函数调用结果
# print(r2()) #TypeError: 'int' object is not callable
- 2、匿名函数
- 说明:就是通过一句代码实现的简单逻辑的函数(类似列表当中的“列表推导式”、条件语句中的“三目运算符”、字典当中的“dict(zip([key1,key2…],[value1,value2…])) ”),通俗而言就是简单一些的函数,可以通过匿名函数快速生成。
- 格式:lambda 形参:返回值
- 匿名函数经常用于其他函数的参数中,比如:高阶函数和装饰器
#3、匿名函数
f = lambda n:n**2
print(f) #<function <lambda> at 0x000001D9A784A700>
print(f(3)) #9
#3.1、匿名函数常常用于函数参数
#3.1.1、用于sort
l0 = [{'name:':'a','id':1,'score':76},{'name:':'b','id':2,'score':56},{'name:':'c','id':3,'score':98}]
# l0.sort() # TypeError: '<' not supported between instances of 'dict' and 'dict'
l0.sort(key=lambda k:k['score'])
print(l0) #[{'name:': 'b', 'id': 2, 'score': 56}, {'name:': 'a', 'id': 1, 'score': 76}, {'name:': 'c', 'id': 3, 'score': 98}]
l0.sort(reverse=True,key=lambda k:k['score'])
print(l0) #[{'name:': 'c', 'id': 3, 'score': 98}, {'name:': 'a', 'id': 1, 'score': 76}, {'name:': 'b', 'id': 2, 'score': 56}]
#3.1.2、用于sorted
l0 = [{'name:':'a','id':1,'score':76},{'name:':'b','id':2,'score':56},{'name:':'c','id':3,'score':98}]
l1 = sorted(l0,key=lambda k:k['score'])
print(l1) #[{'name:': 'b', 'id': 2, 'score': 56}, {'name:': 'a', 'id': 1, 'score': 76}, {'name:': 'c', 'id': 3, 'score': 98}]
l1 = sorted(l0,reverse=True,key=lambda k:k['score'])
print(l1) #[{'name:': 'c', 'id': 3, 'score': 98}, {'name:': 'a', 'id': 1, 'score': 76}, {'name:': 'b', 'id': 2, 'score': 56}]
#3.2、【面试题】思考题:代码阅读题(分清函数的定义与调用,程序从上往下运行遇到函数定义,先读入函数,不进入函数体,只有在调用的时候进入函数体。)
def check():
l = []
for i in range(5):
l.append(lambda x:i * x)
return l
r = check()
print(r) # 返回列表l
print(r[0]) #<function check.<locals>.<lambda> at 0x000002CD5AA2A820> 返回列表l中装着的第一个匿名函数
'''
注意:i为4,因为在执行定义函数的时候没有执行匿名函数,只是把匿名函数添加到列表中,
所以i在每次循环时并没有进入到匿名函数体中,最后调用匿名函数的时候,函数已经执行结束,
i的最终取值为4,所以最终调用每次用到的i都是4。
(关键点要理解函数的定义与调用)
'''
print(r[0](3)) #12 返回列表l中装着的第一个匿名函数在输入参数为3的时候的返回值‘i * x’
print(r[1](3)) #12
print(r[2](3)) #12
print(r[3](3)) #12
print(r[4](3)) #12
- 3、闭包
- 概念介绍:函数只是一段可执行代码,编译后就“固化”了,每个函数在内存中只有一份实例,得到函数的入口点便可以执行函数了。函数还可以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题
- 定义:两个函数嵌套定义,如果在内部函数中访问了外部函数中的变量,则会形成一个闭包
- 说明:
a.闭包本质还是一个函数,只要遵循闭包的概念,可以设置默认参数,关键字参数,不定长参数和返回值。
b.闭包的使用场景:变量的作用域和装饰器
# 4、闭包
# 4.1、需求:在func2中访问func1中的变量num1
# 方式一:设置返回值,在func2中调用func1
def func1():
num1 = 234
return num1
def func2():
print(func1() + 10) #函数在另一个函数中作为函数体中的一个参与运算的变量。
func2() #244
# 方式二:进行函数的嵌套定义
# def func1():
# num1 = 234
# def func2():
# print(num1 + 10)
# func1() #这种情况无法调用func2
#4.2、调用嵌套定义函数的两种方式
#a、外层函数调用
def func1():
num1 = 234
def func2():
print(num1 + 10)
func2()
func1() #244
#b、将内层函数的调用作为外层函数的返回值
def func1():
num1 = 234
def func2():
print(num1 + 10)
return func2()
func1() #244
#c、将内层函数名作为外层函数的返回值
def func1():
num1 = 234
def func2():
print(num1 + 10)
return func2
func1()() #244
#4.3、注意
# 1、不同作用域内的变量重名问题:全局变量可以在函数内部访问,但是无法在函数内部进行修改。
# 这里对变量的记忆方法就是‘就近原则’(即允许访问范围内距离这个变量最近的定义值,就是这个变量的值)
num = 10
a = 10
b = 11
def outter():
print('b:',b) #b: 11 这里的b是全局变量b
num = 20
def inner():
print('b:',b) #b: 11 这里的b是全局变量b
# a += 1 #UnboundLocalError: local variable 'a' referenced before assignment (函数内全局变量只能使用,无法修改)
# print('a:',a) #UnboundLocalError: local variable 'a' referenced before assignment
num = 30
a = 5
print('inner:',num) #inner: 30 这里的num是内部函数中重新定义的num
print('a:',a) #a: 5 这里的a是内部函数中重新定义的a
print('outter:',num) #outter: 20 这里的num是内部函数中重新定义的num
print('a',a) #a: 10 这里的num是内部函数中重新定义的num
return inner
f = outter() #这里调用outter,产生结果20
f() #这里调用inner产生结果30
print('global:',num) #global: 10
print('a:',a) #global: 10
变量作用域
-
定义:变量的作用域指的是变量可以使用的范围
-
说明:程序的变量并不是在任意位置都可以访问,访问权限取决于这个变量是在哪里赋值的
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。 -
【面试题】Python的作用域一共有4种,分别是
L:Local,局部作用域,特指内部函数
E:Enclosing,函数作用域【内部函数外的函数中】
G:Global,全局作用域
B:Built-in,内建作用域【内置作用域】 num = int(“244”)
查找方式:以L—>E—>G—>B,在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找
- 变量访问原则:就近原则(L—>E—>G—>B);注意变量如果在一个域内未定义,则不能修改。
- 涉及变量作用域的情况:
- a.在Python中,只有函数,类,模块会引入新的作用域
- b.其他的代码块,如:if,while,for,try-except,with等都不会引入新的作用域
#5、变量作用域
# 5.1.变量定义在不同的位置,访问的权限和位置也不同
num1 = 10 # G:Global,全局作用域:可以在当前py文件的任意位置访问
def outter():
num2 = 20 # E:Enclosing,函数作用域:只能在outter中访问
def inner():
num3 = 30 # L:Local,局部作用域:只能在inner中访问
print('inner:',num1,num2,num3)
print("outter:",num1,num2)
return inner
f = outter() #outter: 10 20
f() #inner: 10 20 30
print("global:",num1) #global: 10
# 5.2、【面试题】不同作用域内的变量重名,变量被访问的原则:就近原则
num = 10
def outter():
num = 20
def inner():
num = 30
print('inner:',num) # 10 20 30-----》 30
print("outter:",num) # 10 20 ----》20
return inner
f = outter()
f()
print("global:",num) # 10
#5.3、全局变量和局部变量是一个相对概念
"""
注意:全局变量和局部变量是一个相对的概念
全局变量:可以在当前文件的任意位置被访问的变量
局部变量:只能在指定的范围内被访问的变量
"""
# 1
num = 34 # 全局变量
def test():
num1 = 19 # 局部变量
# 2.
num1 = 34 # 全局变量
def func1():
num2 = 35 # 局部变量【函数作用域】
def func2():
num3= 24 # 局部变量【局部作用域】
#5.4、global函数:使得在局部作用域可以修改全局变量。
#a.第一种
a = 10
def show():
# 声明此处的变量来自于全局
global a
a += 1
print(a) # 11
show()
print(a) # 11
#b.第二种
print('第二种')
a = 10
def show():
def sh():
global a
a += 1 #全局变量a的修改
print(a) # 11
print(a) #10 全局变量a的访问
return sh()
show()
print(a) # 11
#5.5、nonlocal函数:使得在局部作用域可以修改函数作用域的变量。
#a.第一种
print('#'*10)
a = 10
def show():
b = 5
def sh():
nonlocal b
b += 1 #函数作用域的变量b的修改
print(b) # 6
print(b) #5 函数作用域的变量b的访问
return sh()
show()
# #b.第二种用nonlocal访问全局变量,不可行
# print('#'*10)
# a = 10
# def show():
# def sh():
# nonlocal a #SyntaxError: no binding for nonlocal 'a' found
# a += 1
# print(a) # 6
# print(a) #5 函数作用域的变量b的访问
# return sh()
# show()
生成器(一边生成一边使用)
- 说明:
- 列表:一次性将所有的元素全部定义出来,如果只需要访问其中的前几个元素,大量的内存空间会被浪费。
- 解决方案:
使用第n个元素,则只需要生成前n元素,在Python中,将这种一边使用,一般计算的机制被称为生成器(generator)
- 生成器的定义方式有两种:
- a.将列表推导式中的[]改为()
- b.函数结合yield,定义函数生成器
#6、生成器
#6.1、生成器的定义:列表推导式
list1 = [i for i in range(10)]
print(type(list1)) #<class 'list'>
print(list1) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list2 = (j for j in range(10))
print(list2) #<generator object <genexpr> at 0x0000019A5A21FF20>
#访问生成器:法一
print(next(list2)) #0
print(next(list2)) #1
#访问生成器:法二
print(list(list2)) #[2, 3, 4, 5, 6, 7, 8, 9] 注意:这里是因为list2生成器之前已经被访问了两个元素了。
#访问生成器:法三
print('dd')
for m in list2:
print(m) #没执行,可能是因为上面已经访问结束了
list2 = (j for j in range(10))
print('fa3')
for m in list2:
print(m) #挨个遍历生成器
#6.2、生成器定义:yield(v. 产生(收益、效益等))
print('yield')
def test1():
for c in range(1,11):
yield c
r1 = test1()
print(r1,type(r1)) # <generator object test1 at 0x0000026C0D21FF20> <class 'generator'>
#访问生成器:法一
print(next(test1())) #1 #第一次调用test1
print(next(test1())) #1 #第一次调用test1
print('for')
te = test1()
for i in range(1,11):
print(next(te)) #打印结果是从1到10挨个遍历生成器
# #访问生成器:法二
print(list(test1())) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 注意:这里是因为list2生成器之前已经被访问了两个元素了。
# #访问生成器:法三
print('fa3')
for m in test1():
print(m) #打印结果是从1到10挨个遍历生成器
可迭代对象&迭代器
- 概念:可迭代对象:iterable;迭代器:iterator
- 面试题】简述可迭代对象和迭代器之间的区别和联系
- 区别:
可迭代对象:Iterable,可以直接作用于for循环的对象【可以使用for循环遍历其中元素的对象】,
如:list,tuple,dict,set,str,range(),生成器等
迭代器:Iterator,可以直接作用于for循环,又可以通过next()获取下一个元素的对象,
如:生成器 - 联系:
迭代器一定是可迭代对象,可迭代对象不一定是迭代器
但是,可以通过系统功能iter()将不是迭代器的可迭代对象转换为迭代器
- 区别:
# isintance(变量,类型):判断一个变量的类型是否是指定的类型
from collections import Iterable,Iterator #这么写会有警告
# 1.
print(isinstance([34],Iterable)) # True
print(isinstance((34,),Iterable)) # True
print(isinstance("agag",Iterable)) # True
print(isinstance({"a":10},Iterable)) # True
print(isinstance({457},Iterable)) # True
print(isinstance(range(5),Iterable)) # True
print(isinstance((i ** 2 for i in range(5)),Iterable)) # True
print(isinstance(34,Iterable)) # False
print(isinstance(True,Iterable)) # False
print('*'*10)
# 2.
print(isinstance([34],Iterator)) # False
print(isinstance((34,),Iterator)) # False
print(isinstance("agag",Iterator)) # False
print(isinstance({"a":10},Iterator)) # False
print(isinstance({457},Iterator)) # False
print(isinstance(range(5),Iterator)) # False
print(isinstance((i ** 2 for i in range(5)),Iterator)) #True
print("*"*10)
print(isinstance(iter([34]),Iterator)) #True
print(isinstance(iter((34,)),Iterator)) #True
print(isinstance(iter("agag"),Iterator)) #True
print(isinstance(iter({"a":10}),Iterator)) #True
print(isinstance((i ** 2 for i in range(5)),Iterator)) #True
高阶函数
-
1、map():映射
- map(func,iterable),返回值是一个iterator【容器,迭代器】
func:函数
iterable:可迭代对象【容器】,可以是多个,常用列表,如果是多个容器,则对应的func函数就要有多个输入形参与之对应。 - 功能:将iterable容器中的每一个元素传递给func,func返回一个结果,结果会成为iterator中的元素
- 容器----》func----》新的容器
- map(func,iterable),返回值是一个iterator【容器,迭代器】
-
2、reduce():减少
- reduce(func,seq)
func:函数
seq:序列【容器】 - 功能:减少
- 注意:func中要设置两个参数
首先将seq中的第0个元素和第1个元素传递给func,进行运算,返回结果1
接着,将 结果1 和第2个元素传递给func,进行运算,返回结果2
接着,将 结果2 和第3个元素传递给func,进行运算,返回结果3
…
直到所有的元素全部参与运算,表示运算结束
- reduce(func,seq)
-
3、filter():过滤
- filter(func,iterable):过滤
func:函数
iterable:可迭代对象 - 功能:将iterable中的元素依次传递给func,根据func的返回值决定是否保留该元素,如果func的返回值为True,则表示当前元素需要保留,如果为False,则表示过滤。
- filter(func,iterable):过滤
-
4、sort()和sorted():排序
- 【面试题】列表中的sort函数和高阶函数sorted的区别和联系
1.调用语法:
列表.sort(reverse,key=func),
sorted(iterable,reverse,key=func)
2.结果:sort是在原列表内部排序的,sorted是生成了一个新的列表
3.二者默认情况下都是升序排序,如果要降序,则都是设置reverse=True - sort和sorted见上面“匿名函数部分”
- 【面试题】列表中的sort函数和高阶函数sorted的区别和联系
#8、高级函数
#map
#a.
l1 = [1,2,3,4,5]
l2 = map(lambda i:pow(i,2),l1)
print(l2) #<map object at 0x000001D3ABBBC910>
print(list(l2)) #[1, 4, 9, 16, 25]
#b.
l1 = [1,1,1,2,0,2,0,1]
l2 = [2,2,2,1,1]
l3 = map(lambda x,y:x+y,l1,l2)
print(list(l3)) #[3, 3, 3, 3, 1]
#reduce
from functools import reduce
#a.
l1 = list(range(1,4))
l2 = reduce(lambda x,y:x+y,l1)
print(l2) #6
#b.常见错误
# l1 = list(range(1,4))
# l2 = reduce(lambda x:x + 1,l1) #TypeError: <lambda>() takes 1 positional argument but 2 were given
# print(l2)
#filter
l1 = list(range(1,11))
l2 = filter(lambda x:x % 2 == 0,l1)
print(l2) #<filter object at 0x000001C2B32FCEE0>
print(list(l2)) #[2, 4, 6, 8, 10]
#sorted见详解