函数进阶&变量作用域&生成器&可迭代对象&迭代器

函数进阶

  • 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----》新的容器
  • 2、reduce():减少

    • reduce(func,seq)
      func:函数
      seq:序列【容器】
    • 功能:减少
    • 注意:func中要设置两个参数
      首先将seq中的第0个元素和第1个元素传递给func,进行运算,返回结果1
      接着,将 结果1 和第2个元素传递给func,进行运算,返回结果2
      接着,将 结果2 和第3个元素传递给func,进行运算,返回结果3

      直到所有的元素全部参与运算,表示运算结束
  • 3、filter():过滤

    • filter(func,iterable):过滤
      func:函数
      iterable:可迭代对象
    • 功能:将iterable中的元素依次传递给func,根据func的返回值决定是否保留该元素,如果func的返回值为True,则表示当前元素需要保留,如果为False,则表示过滤。
  • 4、sort()和sorted():排序

    • 【面试题】列表中的sort函数和高阶函数sorted的区别和联系
      1.调用语法:
      列表.sort(reverse,key=func),
      sorted(iterable,reverse,key=func)
      2.结果:sort是在原列表内部排序的,sorted是生成了一个新的列表
      3.二者默认情况下都是升序排序,如果要降序,则都是设置reverse=True
    • 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见详解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值