14.闭包&变量的作用域&函数的本质

一、匿名函数应用

# 匿名函数常用于函数的参数中
​
# 1.将列表按学生成绩从大到小排序
students = [
{'name': '小花', 'age': 19, 'score': 93, 'gender': '女', 'tel':
'15300022839'},
{'name': '明明', 'age': 20, 'score': 40, 'gender': '男', 'tel':
'15300022838'},
{'name': '华仔', 'age': 18, 'score': 90, 'gender': '女', 'tel':
'15300022839'},
{'name': '静静', 'age': 16, 'score': 100, 'gender': '不明', 'tel':
'15300022428'},
{'name': 'Tom', 'age': 17, 'score': 59, 'gender': '不明', 'tel':
'15300022839'},
{'name': 'Bob', 'age': 18, 'score': 77, 'gender': '男', 'tel':
'15300022839'}
]
"""
列表.sort():将列表中的元素进行升序
列表.sort(reverse=True):将列表中的元素进行降序
​
工作原理:默认情况下,sort()进行排序的时候,是将列表中的元素两两进行大小比较
​
列表.sort(reverse,key=func)
    key:表示自定义排序规则,具体则规则由func函数定义
    工作原理:将列表中元素依次取出,传参给func函数,该函数的返回值就是排序的规则,所以要求该函数的返回值必须支持大小比较
"""
# students.sort()  # TypeError: '<' not supported between instances of 'dict' and 'dict'
# a.如果列表中的元素支持大小比较,可以直接排序
# list1 = [34,65,7,8,9,9,10]
# list1.sort()
# print(list1)
​
# b.如果列表中的元素不支持大小比较,则需要自定义排序规则,设置  key=函数   即可
# students.sort(reverse=True,key=10)  # TypeError: 'int' object is not callable调用
# def f1(x):
#     return x['score']
# students.sort(reverse=True,key=f1)
# print(students)
​
# 推荐:匿名函数
# 匿名函数经常用于其他函数的参数中,比如:高阶函数和装饰器
students.sort(reverse=True,key=lambda studict:studict['score'])
print(students)
​
​
​
# 【面试题】思考题:代码阅读题
def check():
    l = []
    for i in range(5):
        l.append(lambda x:i * x)  
    return l
r = check()
print(r)   
print(r[0])  
print(r[0](3))  
print(r[1](3))
print(r[2](3))
print(r[3](3))
print(r[4](3))

二、函数的本质【重点掌握】

函数本质是一个变量,函数名其实就是一个变量名

一个函数可以作为另一个函数的参数或返回值使用,只需要传递或返回函数名即可

# 1.函数本质是一个变量,函数名其实就是一个变量名,定义函数的过程相当于是定义变量的过程
# a.
num = 10   # 10 <class 'int'>
print(num,type(num))
def func1():
    pass
print(func1,type(func1))  # <function func1 at 0x102818f70> <class 'function'>
​
# b.
print(abs(-30))  # 函数的调用,30
print(abs)       # 函数本身,<built-in function abs>
​
r1 = abs(-66)
print(r1)   # 66
f1 = abs
print(f1)  # <built-in function abs>
​
print(f1(-100))  # 相当于调用abs
​
# c.可以修改函数名的指向,同时注意:自定义变量的时候,一定不要和系统的内置函数重名,
# 如:list(),dict(),tuple(),set(),int(),float(),str()等,否则会导致系统的内置函数失效
# abs = True
# print(abs(-35))
​
def func2():
    print('func~~~2222')
func2()
​
func2 = 45
# func2()
​
# 2.一个函数可以作为另一个函数的参数或返回值使用,只需要传递或返回函数名即可
# 注意:xx():表示函数的调用
# a.参数
def show1(num1,num2,f):
    print(f)
    print(f(num1) + f(num2))
# show1(345,6,7)  # TypeError: 'int' object is not callable
show1(34,-10,abs)
​
def a(n):
    return n - 10
show1(34,45,a)
​
# b.返回值
def show2():
    return abs
r2 = show2()
print(r2)
print(r2(-34))

三、函数进阶【重点掌握】

1.闭包

​ 函数只是一段可执行代码,编译后就“固化”了,每个函数在内存中只有一份实例,得到函数的入口点便可以执行函数了。函数还可以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题

# 1.需求:在func2中访问func1中的变量num1
# 方式一:设置返回值,在func2中调用func1
# def func1():
#     num1 = 234
#     return num1
# def func2():
#     print(func1() + 10)
# func2()
​
# 方式二:进行函数的嵌套定义
# def func1():
#     num1 = 234
#     def func2():
#         print(num1 + 10)
# func1()
​
# 2.嵌套定义的函数的调用方式    ******
# 注意:一个函数,无论以什么样的形式,书写在哪个位置,定义之后都需要手动调用才能执行
# 方式一:在func1中调用func2
# def func1():
#     print('111111')
#     num1 = 234
#     def func2():
#         print('22222')
#         print(num1 + 10)
#     func2()
# func1()
​
# 方式二:将func2作为func1的返回值,常用于装饰器中
def func1():
    print('111111')
    num1 = 234
    def func2():
        print('22222')
        print(num1 + 10)
    return func2
f1 = func1()  # 调用外部函数
print(f1)   # <function func1.<locals>.func2 at 0x106440790>
f1()   # 相当于调用func2()
​
# 3.闭包
"""
概念:两个函数嵌套定义,如果在内部函数中访问了外部函数中的变量,则会形成一个闭包
"""
# a.
def outter1():
    num = 10
    def inner1():
        print(num)
    return inner1
f1 = outter1()
f1()
​
# b.
def outter2(a):
    num = 10
    def inner2():
        print(a,num)
    return inner2
f2 = outter2(34)
f2()
​
# c.
def outter3(a):
    num = 10
    def inner3(b):
        print(a,num,b)
    return inner3
f3 = outter3(34)
f3(18)
​
# d.
def outter4(a):
    num = 10
    def inner4(b):
        print(a,num,b)
        return 'abc'
    return inner4
f4 = outter4(34)
r4 = f4(18)  # r4 = 'abc'
​
"""
说明:
    a.闭包本质还是一个函数,只要遵循闭包的概念,可以设置默认参数,关键字参数,不定长参数和返回值
    b.闭包的使用场景:变量的作用域和装饰器
"""

2.变量的作用域

2.1作用域的分类

变量的作用域指的是变量可以使用的范围

程序的变量并不是在任意位置都可以访问,访问权限取决于这个变量是在哪里赋值的

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。

【面试题】Python的作用域一共有4种,分别是

​ L:Local,局部作用域,特指内部函数

​ E:Enclosing,函数作用域【内部函数外的函数中】

​ G:Global,全局作用域

​ B:Built-in,内建作用域【内置作用域】 num = int("244")

查找方式:以L—>E—>G—>B,在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找

# 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()
f()
print("global:",num1)
​
print("*" * 50)
​
# 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
​
# 3.什么样的情况下回涉及到变量的作用域问题
"""
a.在Python中,只有函数,类,模块会引入新的作用域
b.其他的代码块,如:if,while,for,try-except,with等都不会引入新的作用域
"""
if 1:
    a = 10
print(a)

2.2局部变量和全局变量

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中

"""
注意:全局变量和局部变量是一个相对的概念
全局变量:可以在当前文件的任意位置被访问的变量
局部变量:只能在指定的范围内被访问的变量
"""
​
# 1
num = 34     # 全局变量
def test():
    num1 = 19   # 局部变量
​
​
# 2.
num1 = 34    # 全局变量
def func1():
    num2  = 35   # 局部变量【函数作用域】
    def func2():
        num3= 24   # 局部变量【局部作用域】

2.3global

# 【面试题】
# a = 10
# def show():
#     # UnboundLocalError: local variable 'a' referenced引用 before assignment赋值
#     a += 1   # a = a + 1
# show()
# print(a)
​
# 方案一:在函数内部定义局部变量a,进行运算,涉及到的a是两个变量
a = 10
def show():
    a = 5
    a += 1   # a = a + 1
    print(a)  # 6
show()
print(a)  # 10
​
# 方案二:涉及到的a是一个变量            *******
a = 10
def show():
    # 声明此处的变量来自于全局
    global a
    a += 1
    print(a)  # 11
show()
print(a)    # 11

四、生成器

# 1,概念
"""
问题:
    列表:一次性将所有的元素全部定义出来,如果只需要访问其中的前几个元素,大量的内存空间会被浪费
解决方案:
    使用第n个元素,则只需要生成前n元素,在Python中,将这种一边使用,一般计算的机制被称为生成器(generator)
​
生成器的定义方式有两种:
    a.将列表推导式中的[]改为()
    b.函数结合yield,定义函数生成器
"""
# 1.方式一
# 列表推导式
list1 = [i for i in range(10000)]
print(type(list1))  #<class 'list'>
print(list1)
# 生成器
ge1 = (i for i in range(10000))
print(type(ge1))
print(ge1)
​
# 2.访问生成器中的元素
# a.next(生成器)获取生成器中的下一个元素
ge1 = (i for i in range(5))
# print(next(ge1))
# print(next(ge1))
# print(next(ge1))
# print(next(ge1))
# print(next(ge1))
# 注意:定义一个生成器,通过next()获取生成器中的下一个元素,当所有元素全部生成获取完毕,再次next(),则报错StopIteration
# print(next(ge1))
​
# b.for循环
# for n in ge1:
#     print(n)
​
​
# 3.方式二
# a
def test1():
    return 10
r1 = test1()
print(r1,type(r1))   # 10 <class 'int'>
​
# b
# 只要在函数内部出现yield关键字,则该函数就是一个函数生成器,yield关键字后面的数据将是生成器中的元素
def test1():
    yield 10
r1 = test1()
print(r1,type(r1))  # <generator object test1 at 0x0000021523610570> <class 'generator'>
# print(next(r1))
for n in r1:
    print(n)
​
# c.
def test2():
    yield 10
    yield 20
    yield 30
r2 = test2()
for n in r2:
    print(n)
​
# d.
# 获取一个生成器中的3个元素
def test3(n):
    for i in range(n):
        yield  i ** 2
r3 = test3(5)
print(next(r3))  # 0
print(next(r3))  # 1
print(next(r3))  # 4
​
# 分别获取三个生成器中的第0个元素
def test3(n):
    for i in range(n):
        yield  i ** 2
print(next(test3(5)))
print(next(test3(5)))
print(next(test3(5)))
​
# 注意:在函数生成器中,只要函数调用一次,则表示生成一个新的生成器

五、可迭代对象和迭代器

【面试题】简述可迭代对象和迭代器之间的区别和联系
区别:
    可迭代对象: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))
print(isinstance("agag",Iterable))
print(isinstance({"a":10},Iterable))
print(isinstance({457},Iterable))
print(isinstance(range(5),Iterable))
print(isinstance((i ** 2 for i in range(5)),Iterable))
print(isinstance(34,Iterable))   # False
print(isinstance(True,Iterable))  # False
​
​
# 2.
print(isinstance([34],Iterator))   # False
print(isinstance((34,),Iterator))
print(isinstance("agag",Iterator))
print(isinstance({"a":10},Iterator))
print(isinstance({457},Iterator))
print(isinstance(range(5),Iterator))
print(isinstance((i ** 2 for i in range(5)),Iterator))
​
​
print(isinstance(iter([34]),Iterator))
print(isinstance(iter((34,)),Iterator))
print(isinstance(iter("agag"),Iterator))
print(isinstance(iter({"a":10}),Iterator))
print(isinstance((i ** 2 for i in range(5)),Iterator))
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiayi_init_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值