zoukankan      html  css  js  c++  java
  • 函数进阶二(函数名运用,闭包,迭代器)

    一. 函数内容回顾
    万能参数:
    *args **kwargs
    *魔性用法
    函数的定义时:* ** 代表聚合
    函数的调用时:* ** 代表打散

    形参顺序:
    位置参数, *args, 默认参数, **kwargs

    名称空间
    临时名称空间
    def func():
    name = "alex"
    func()
    print(name) # 找不到
    func() # 再次调用又可以
    内置名称空间

    作用域:
    全局作用域:内置名称空间,全局名称空间
    局部作用域:临时名称空间,也称局部名称空间

    加载顺序:
    内置名称空间 > 全局名称空间 > 临时名称空间
    取值顺序:
    就近原则, LEGB
    临时名称空间 > 全局名称空间 > 临时名称空间

    内置函数:
    globals(): 返回一个字典, 全局作用域的内容。
    locals(): 返回一个字典, 当前作用域的内容。

    关键字:
    global:
    1. 在局部作用域可以声明一个全局变量
    2. 在局部作用域可以对全局变量进行修改

    nonlocal:
    1. 不能对全局变量进行修改
    2. 子级对父级函数的变量的修改

    . 函数名的运用

    1 # 1. 函数名可以当作一个特殊的变量
    2 
    3 def func():
    4     print(666)
    5 print(func)
    6 <function func at 0x00000203DE321E18>
    7 
    8 # 这里是函数的内存地址
    9 # 因此,函数名是一个特殊的变量
     
     1 # 2. 函数名可以当做变量赋值
     2 f1 = 2
     3 f2 = f1
     4 f3 = f2
     5 print(f3) # 2
     6 
     7 def func():
     8     print(666)
     9 func1 = func
    10 f2 = func1
    11 f3 = f2
    12 print(f3)
    13 f3()
    14 # 这个示例跟上面那个普通的变量的赋值是一样的意思
    
    
    
     1 # 3. 函数名可以当作容器类数据类型的元素
     2 def func1():
     3     print('in func1')
     4 
     5 def func2():
     6     print('in func2')
     7 
     8 def func3():
     9     print('in func3')
    10 
    11 # 一次执行三个函数的办法
    12 # 一开始只能这样
    13 # func1()
    14 # func2()
    15 # func3()
    16 
    17 # 现在可以这样:
    18 l1 = [func1, func2, func3]
    19 for i in l1:
    20     i()
    21 
    22 dic = {1: func1, 2: func2, 3: func3}
     1 # 4. 函数名可以当作函数的参数
     2 def func(x):
     3     print(x)
     4     print("in func")
     5 
     6 def func1():
     7     print("in func1")
     8 
     9 func(func1)
    10 # <function func1 at 0x000002000651AC80>
    11 # in func
    12 
    13 def func(x):
    14     x()
    15     print("in func")
    16 def func1():
    17     print("in func1")
    18 
    19 func(func1)
    20 # in func1
    21 # in func
    # 5. 函数名可以当作函数的返回值
    
    def func(x):
        print("in func")
        return x
    def func1():
        print("in func1")
    
    ret = func(func1)  # 把 func1 当成一个变量x传入到func(x)中,打印 in func
    ret()                   # func(func1)返回的是一个指向 in func1 的函数名
    
    # in func
    # in func1
    # func(func1)()   # 这个跟上面的一样
    
    # func(func1())    # 这个是先调用func1,然后再调用 func(),把 func1 当作一个变量
    
    # 函数名可以当作容器类数据类型的元素,函数名可以当作函数的参数,函数名可以当作函数的返回值,这三点得知函数是第一类对象
    . 闭包
    1. 内层函数对外层函数(非全局)变量的引用和改变
    2. 闭包只存在于内层函数中
    3. 函数都要逐层返回,最终返回给最外层函数
     1 # 这不是闭包
     2 name = "alex"
     3 def func():
     4     def inner():
     5         print(name)
     6     return inner
     7 func()
     8 
     9 # 这就是标准闭包
    10 def func():
    11     name = "alex"
    12     def inner():
    13         print(name)
    14     return inner
    15 func()
    16 
    17 # 这是闭包
    18 def func(n):
    19     def inner():
    20         print(n)
    21     return inner
    22 n = "太白"
    23 func(n)()
    24 
    25 def func(n):
    26     n = name
    27     def inner():
    28         print(n)
    29     return inner
    30 name = "太白"
    31 func(name)()
    32 # 这里和上面的一个意思
    33 
    34 # 判断是不是闭包的方法
    35 def func():
    36     name = "alex"
    36 n = 12
    37 def inner(): 38 print(name, n) 39 return inner 40 f = func() 41 # 获取闭包引用的外层变量 42 print(f.__closure__[0].cell_contents) # 注意要想判断是不是闭包,必须判断内层函数名的 __closure__ 方法,而不是外层函数名,这里 f=func()即是 inner 这个内层函数名 43 print(f.__closure__[1].cell_contents)
    44 # 这里发现,就算先去掉第二个 print 的内容,还是先打印出 12,再打印出 alex
    44 print(f.__closure__) 其实原因在 __closure__ 这个方法里,比如再加个 n1 = 0.02, 再 print(f.__closure__)
    44 # (<cell at 0x00000264283E6618: int object at 0x0000000056526C60>,
    44 # <cell at 0x00000264283E6648: float object at 0x00000264283011C8>, <cell at 0x00000264283E6678: str object at 0x000002642A018030>)
    44 # 这里发现它内部就已经有打印数据类型的顺序了
    44 45 name = "alex" 46 def func(): 47 def inner(): 48 print(name) 49 return inner 50 f = func() 51 print(f.__closure__[0].cell_contents) # 报错,说明不是闭包
     1 # 闭包的作用:
     2 def func(step):
     3     num = 1
     4     num += step
     5     print(num)
     6 func(3)  # 4
     7 
     8 # 临时名称空间
     9 j = 0
    10 while j < 5:
    11     func(3)
    12     j += 1
    13 # 4 # 这一步函数执行完后,函数内变量与值的临时空间消失
    14 # 4
    15 # 4
    16 # 4
    17 # 4
    18 
    19 # 闭包:解释器在执行程序时,如果遇到函数,随着函数的结束而关闭临时名称空间
    20 # 但是遇到闭包,有一个机制:闭包的空间不会随着函数的结束而关闭
    21 def wrapper(step):
    22     num = 1
    23     def inner():
    24         # num += step
    25         # 单是上面这步是内层函数对外层函数的引用和修改,会出错
    26         nonlocal num
    27         num += step
    28         print(num)
    29     return inner
    30 f = wrapper(3)
    31 j = 0
    32 while j < 5:
    33     f() # inner()
    34     j += 1
    35 
    36 # 4
    37 # 7
    38 # 10
    39 # 13
    40 # 16
    41 # 因为闭包的机制,num这个变量在函数第一次执行结束后不会马上消失,会在内层中保存一段时间,因为在下一个循环时,它会保存之前num对应的值
    42 
    43 def wrapper(step):
    44     num = 1 # 这个外层函数也不会关闭,因为只要内层函数有闭包不会关闭
    45     def inner():
    46         nonlocal num
    47         num += step
    48         print(num)
    49     return inner # 返回函数的变量名
    50     # return inner() 一般不这样写
    51 f = wrapper(3)
    52 j = 0
    53 while j < 5:
    54     f() # inner()
    55     # 如果这里改成wrapper(3)()
    56     # 那就相当于在内层中开辟了5个空间,每个空间调用 wrapper(3)
    57     # 因此打印结果是 4 4 4 4 4
    58     j += 1
     1 # 闭包的应用
     2 # 1. 装饰器(它的本质是利用了闭包的原理)
     3 # 2. 爬虫
     4 
     5 from urllib.request import urlopen
     6 
     7 content = urlopen("http://www.cnblogs.com/jin-xin/articles/8259929.html")
     8 print(id(content))  # 2630731144832
     9 # print(content.decode("utf-8"))
    10 content = urlopen("http://www.cnblogs.com/jin-xin/articles/8259929.html")
    11 print(id(content))  # 2630731219576
    12 # 如果不用闭包,爬一次就得创建一个空间,创建多次就得在内层中创建多个空间
    13 # 因此要使用闭包形式,爬一次在内层中创建一个空间后,再爬一次还是在同一个内层地址
    14 # 如下所示
    15 
    16 from urllib.request import urlopen
    17 
    18 def index():
    19     url = "http://www.cnblogs.com/jin-xin/articles/8259929.html"
    20     def get():
    21         return urlopen(url).read()
    22     return get
    23 
    24 taibai = index()
    25 content = taibai()
    26 # content = index()() 和上面两步一样
    27 print(id(content))  # 2550328055264
    28 print(id(content))  # 2550328055264
    29 print(content.decode("utf-8"))
    30 
    31 # 闭包就是在内层中开一个空间,常贮存一些内容,以便后续程序调用
     1 # 迭代器
     2 
     3 # iterable: 可迭代对象——只要内部含有__iter__方法的就是可迭代对象, 它遵循可迭代协议
     4 # str list tuple dict range() 文件句柄
     5 
     6 s1 = "abc"
     7 print(dic(s1))  # 打印结果中含有__iter__方法
     8 
     9 for i in "abc":
    10     print(i)  # 可执行
    11 for i in 123:
    12     print(i)  # 报错
    13 
    14 # 判断一个对象是否是可迭代对象的方法
    15 # 第一种
    16 s1 = "abc"
    17 print("__iter__" in dir(s1))         # True
    18 print("__iter__" in dir(range(10)))  # True
    19 
    20 # 第二种
    21 from collections import Iterable
    22 from collections import Iterator
    23 
    24 s1 = "abcd"
    25 obj = iter(s1)
    26 print(obj)
    27 
    28 print(isinstance(obj, Iterator))  # True
    29 print(isinstance(obj, Iterable))  # True
    30 
    31 print(isinstance(s1.Iterator))  # False
    32 print(isinstance(s1.Iterable))  # False
     1 # 可迭代对象不能直接取值,除了索引和切片
     2 # 比如for...in...是将可迭代对象先转换成迭代器,再从迭代器里面通过next()方法取值的
     3 # 因此迭代器的含义:内部含有"__iter__"并且含有"__next__"方法的就是迭代器,遵循迭代器协议
     4 
     5 # 可迭代对象转换成迭代器的两种方法
     6 # 可迭代对象.__iter__() 或者iter(可迭代对象)
     7 s1 = "abcd"
     8 obj = s1.__iter__()
     9 print(obj)  # <str_iterator object at 0x000001828C618400>
    10 print(obj.__next__())  # a
    11 print(obj.__next__())  # b
    12 print(obj.__next__())  # c
    13 print(obj.__next__())  # d
    14 print(obj.__next__())  # 报错——StopIteration,因为一个next对应一个值,多一个就会报错
    15 
    16 # 可迭代对象转换成迭代器
    17 # 可迭代对象.__iter__() 或者iter(可迭代对象)
    18 s1 = "abcd"
    19 obj = iter(s1)
    20 print(obj)  # <str_iterator object at 0x0000010F49D58048>
    21 print("__iter__" in dir(obj) and "__next__" in dir(obj))  # True
    22 
    23 
    24 # 判断文件句柄是否是可迭代对象,是否是迭代器
    25 from collections import Iterable
    26 from collections import Iterator
    27 
    28 f = open("s", encoding="utf-8", mode="w")
    29 print(isinstance(f,Iterator))
    30 
    31 # s2 = [1, 2, 3]
    32 # 将s2转换成迭代器进行取值
    33 
    34 # 第一种
    35 s2 = [1, 2, 3]
    36 obj = s2.__iter__()
    37 obj2 = iter(s2)
    38 # print(obj2.__next__())
    39 print(next(obj2))
    40 
    41 # 第二种
    42 s2 = [1, 2, 3]
    43 obj = iter(s2)
    44 # print(obj2.__next__())
    45 print(next(obj2))
     1 # isinstance() 和 type() 的区别
     2 
     3 s1 = "abcd"
     4 print(isinstance(s1, str))
     5 print(type(s1))
     6 
     7 # True
     8 # <class 'str'>
     9 
    10 # type() 只是判断该对象的数据类型
    11 # 而 isinstance() 不仅可以判断该对象的数据类型,还可以判断它的其他类型,比如它是否是迭代器之类的
     1 # 迭代器的作用:
     2 
     3 # 1. 节省内存
     4 # 比如有1000个元素的列表,在内存中开辟1000个内存地址,而迭代器中的元素不管有多少,在内存中只占一个位置
     5 
     6 # 2. 惰性机制
     7 # next()就取一个值,不next()就不取值
     8 
     9 # 3. 一条路走到黑,不走回头路
    10 s2 = [1, 2, 3, 4, 5]
    11 obj2 = iter(s2)  # 迭代器
    12 print(next(obj2)) # 前面的不能再取,只能运行完又从头开始运行
    13 
    14 # 里面元素少的时候使用列表
    15 # 元素很多的时候使用迭代器
    16 # 比如上面提到的文件句柄,就是迭代器,文件里面有很多内容都可以很快打开读取
     1 # while循环模拟for循环机制(很重要!)
     2 l1 = [i for i in range(20)]
     3 obj = iter(l1)
     4 while 1:
     5     print(next(obj)) # 会取出但是会报错,因为next()方法,不走回头路
     6 
     7 # 因此可以使用异常处理
     8 l1 = [i for i in range(20)]
     9 obj = iter(l1)
    10 while 1:
    11     try:
    12         print(next(obj))
    13     except StopIteration:
    14         break
    15 # 这样程序就能正常运行
  • 相关阅读:
    【POJ
    【POJ
    【POJ
    【POJ
    【POJ
    【POJ
    【POJ
    【POJ
    NAT
    OSPF与ACL综合实验
  • 原文地址:https://www.cnblogs.com/shawnhuang/p/10208956.html
Copyright © 2011-2022 走看看