zoukankan      html  css  js  c++  java
  • python全栈开发中级班全程笔记(第二模块、第三章)第4节 :函数进阶(重点:装饰器、迭代器、生成器)

     python全栈开发笔记第二模块

    第三章4节:函数进阶

    一、函数进阶

    1、函数进阶--命名空间

      命名空间,又称名称空间,或名字空间,就是存放变量名的地方比如 n = 1 , 1是存在内存的,但n 这个名字和绑定关系就存储在命名空间

      *命名空间和作用域是有直接关系的,

       主要分三种:

      • locals : 是函数内的名称空间,包括局部变量和形参
      • globals : 全局变量,函数定义所在模块的名字空间
      • builtins:内置模块的名字空间
      • 不同变量的作用域不同,就是由这个变量或函数所在的命名空间决定的

      **作用域的范围

      • 全局变量:全局存活,全局有效
      • 局部变量:临时存活,局部有效
          • 查看作用域一般用 locals() 和 globls()方法

     

    代码实现功能:
    >>> n = 6
    >>> locals()                #打印当前变量
    {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 

    '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'n': 6} >>> globals() #打印全局变量 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>,

    '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'n': 6} >>> dir(__builtins__) #打印内置模块的所有变量(注意使用语法,要先dir下再2个下划线) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError',
    'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError',
    'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError',
    'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', #发现报错的语法,还有内置函数等
    'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError',
    'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning',
    'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
    'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True',
    'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning',
    'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__',
    '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr',
    'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',
    'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
    'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord',
    'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str',
    'sum', 'super', 'tuple', 'type', 'vars', 'zip']

     

    2、函数进阶--作用域的查找顺序(空间

     

    例:
    n = 10
    def func():                   # 定义一个嵌套函数
        n = 20
        print("func:",n)          # 每层以函数名做记号
    
        def func1():
            n = 30
            print("func1",n)
    
            def func2():
                print("func2:",n)
    
            func2()                #发现内两层的结果一样,如果没有n = 30,会向上一层找到 n = 20 以此类推
    
        func1()                    #这就是查找变量名由内而外的顺序
    
    func()

      **由此就存在一个查找小规则

    • LEGB: 是查找顺序的英文首字母缩写
    • L: locals(函数内的名字空间)
    • E: enclosing(相邻外部前套函数的名字空间)
    • G: globals(全局变量:函数所在模块的名字空间)
    • B: builtins(内置模块的名字空间)

    3、函数进阶--闭包

      闭包。是一个使用概念,或者说是一个规则,没有专门的语和定义,   

    举例说明:

     

    def func():
        n = 15
        def func2():
            print("func2:",n)
        return func2             # 返回内存地址
    s = func()                   # 执行后也会返回内存地址,而不是执行效果
    print(s)                     # 此时,正常来说,函数已经执行完,内部变量已释放
    s()                          # 现在我再看下s()*加括号表示会执行函数的运行程序,不加括号会打印内存地址
    # 此时发现func2 的代码被执行 n 被打印,为什么函数执行完已经释放内存了,还能执行函数内的代码呢?
    这种效果就叫闭包,返回内部函数的名称,而在外部也拿到了顶层函数,
    一旦顶部函数(func())里有返回值,这个函数的内存就不会被释放、
    在外部可以执行函数内的值,并且可以用内部函数作用域内所有的值,这个现象就叫闭包,
    函数内套子函数,子函数被返回了,反回了内存地址,
    在外边执行子函数的时候,子函数又引用了外层函数的变量,子函数与外部函数存在了互相引用的关系,这种引用关系就叫闭包

     4、***函数进阶---装饰器(重点)

    • 装饰器,就是不修改源代码的基础上,给代码增加新功能
    • 公司的源代码要遵守开放-封闭原则,( 开放:是外端不修改源代码的基础上,可扩展。封闭:是已实现功能的源代码不可更改)
    • 公司的源代码是不能更改调用规则的,如需使用,就要学会用装饰器:
    • 装饰器,是一个概念,没有固定语法,只有一定规则
    • 装饰器:就是在不修改源代码和调用原则的情况下,增加和拓展新的功能
    • 需求:
    • 1、不修改源代码
    • 2、不影响其他代码的执行
    • 3、写的程序谁都可以调用
    _user = False                # 标志位;用户登录后,就改成True
    def login(func):             #usa 的函数
        def inner():
            _name = "alex"
            _pass = "abc123"     # 假设用户名和密码是数据库信息
            global _user         # 函数内修改变量
            if _user == False:   # 判断登陆状态
                username = input("username:")
                password = input("password:")
    
                if username == _name and password == _pass:
                    print("welcome login....")
                    _user = True # 更改登陆状态
                else:
                    print("输入错误!")
            else:
                print("已登录,验证通过....")
            if _user:
                func()        #usa 的函数
        return inner
    
    def home():
        print("山东专区")
    def msaa():
        print("欧美专区")
    @login                    # 等价于 usa = login(usa)可把调用登录函数简写成这样(注意,只能加在函数上,才能对下面的函数起作用)
    def usa():
        print("美国专区")
    def omee():
        print("日韩专区")
    
    usa()                     # 调用函数
    4.1****装饰器-带参数的函数:
    1)细分和过滤条件(不同需求)
    2) 有的需要参数,有的不需要参数(不同需求)
    _user = False
    def login(func):                     #inner
        def inner(*args,**kwargs):       #usa 非固定参数,传多少进去都可以
            _name = "alex"
            _pass = "abc123"
            global _user
            if _user == False:
                username = input("username:")
                password = input("password:")
    
                if username == _name and password == _pass:
                    print("welcome login....")
                    _user = True
                else:
                    print("输入错误!")
            else:
                print("已登录,验证通过....")
            if _user:
                func(*args,**kwargs)     #usa    非固定参数,传多少进去都可以
        return inner
    
    @login
    def home():
        print("山东专区")
    def msaa():
        print("欧美专区",)
    @login
    def usa(w,a,e):
        print("美国专区",w,a,e)
    @login
    def omee(a,w,s):
        print("日韩专区",a,w,s)
    
    usa("3p","4p","5p")     #inner
    omee("11","22","33")
    home()
    4.2装饰器---带参数2
    • 让用户可以通过 QQ、微博、微信也可以认证
    • 简单点说,就是另一种带参数的装饰器

     

    _user = False
    def login(auth_type):        # 认证方式
       def auth(func):          #因为又增加了一种认证方式,所以要再套一层函数,现在认为是 usa
            def inner(*args,**kwargs):
                _name = "alex"
                _pass = "abc123"
                global _user
                if _user == False:
                    username = input("username:")
                    password = input("password:")
    
                    if username == _name and password == _pass:
                        print("welcome login....")
                        _user = True
                    else:
                        print("输入错误!")
                else:
                    print("已登录,验证通过....")
                if _user:
                    func(*args,**kwargs)
            return inner
        return auth
    def home():
        print("山东专区")
    def msaa():
        print("欧美专区",)
    def usa(w,a,e):
        print("美国专区",w,a,e)
    def omee():
        print("日韩专区")
      (*****下面三步很重要*****)
    xx = login("qq")          #把新的认证方式填进在首层函数赋值给 xx
    usa = xx(usa)             # 再重新赋值 usa 等同于执行 xx 把老的 usa 填进去当参数
    usa("3p","4p","5p")       #再次执行 usa 参数
    # 简单解析:如果要再加一种认证方式,就要再套一层函数在第二层,再套一层函数就意味着再赋一次值
    5、函数进阶 -- 列表生成式:
    • 重点是掌握好列表生成式、生成器、迭代器
    • 现在有一个需求是吧列表 [0,1,2,3,4,5,6,7,8,9] 内的每一个值都加 1
    • 按照学过的方法,用for 循环
    a = [0,1,2,3,4,5,6,7,8,9]
    v = []
    for i in a:
        v.append(i+1)
    print(v)                  # 这样的方法也能实现,但是,需要占2倍的内存实现(不建议用)
    
    for index,i in enumerate(a):
        a[index]+=1
    print(a)                  #这种方法也行,但是有更省事的
    
    a = map(lambda a:a+1 ,a)  #用匿名函数(map 和 lambda 组合使用)
    for i in a:
        print(i)              #但还是不够简洁
    **下面学一种新的方法实现功能  
    a = [i+1 for i in range(10)]          # 新语法,把计算方式写进列表内(三元运算极为相似)
    print(a)
    a = [i if i<5 else i*i for i in a]    # 还可以把三元运算加进来判断运算
    print(a)
    b = "alexLi"
    b = [i for i in b]                    # 还可把字符串循环每个字符变成列表(列表内每个字符独立)
    c = {i for i in b}                    # 变成集合
    d = {i:i*i for i in a}                # 变成字典
    print(b,c,d)
    以上代码的执行结果(pycharm)
    [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [1, 2, 3, 4, 25, 36, 49, 64, 81, 100] ['a', 'l', 'e', 'x', 'L', 'i']
    {'e', 'x', 'i', 'a', 'l', 'L'}
    {1: 1, 2: 4, 3: 9, 4: 16, 25: 625, 36: 1296, 49: 2401, 64: 4096, 81: 6561, 100: 10000}
    **小结:
    列表生成器作用主要有:
    1)写 for 循环更简洁(写一行就搞定了)
    2)此方法常用于列表,其他不常用(字典,集合)

     6函数进阶 -- 生成器

    • 用生成式,固然很省事,但是也是比较占内存的,
    • 如果我有一千万个数据,平时用几十个或者几百个就够了,剩下的九百多万还是在内存里白白的浪费空间(占内存)
    • 在 python 中,有一种边循环,边计算的机制就叫生成器(generator)
    python:
    >>> a = (i for i in range(8))
    >>> a
    <generator object <genexpr> at 0x0000018B096D59E8>    #a变成了生成式
    >>> next(a)
    0
    >>> next(a)
    1
    >>> next(a)
    2
    >>> next(a)
    3
    >>> next(a)
    4       
    >>> next(a)                                          #每循环一次,增加一个数值
    5 
    >>> next(a)
    6
    >>> next(a)
    7
    >>> next(a)
    Traceback (most recent call last):                   #循环完了就会报错
      File "<stdin>", line 1, in <module>
    StopIteration            
    **小结:
    特点:不能回退,如果没执行完,可以再次执行,数字执行完毕再次执行会报错
    
    

     7、函数进阶 -- 生成器的调用方式

    上一点讲的只能人肉执行(每执行一次,只往前走一步),如果是十万次,或者更多,敲一万次代码就不友好了

    讲的生成器,还可以用 while 循环或者 for 循环来替换手动执行

    while循环:   (会报错不推荐)
    a = (i for i in range(8))
    while True:           # 直接死循环
        print(next(a))    # 打印执行命令 next()(麻烦)
    pycharm执行结果:
    0
    1
    2
    3
    4
    5
    6
    7               #执行没问题
    
    Traceback (most recent call last):
      File "C:/Users/57098/PycharmProjects/.py", line 239, in <module>
        print(next(a))
    StopIteration      #但是会报错,报错会导致程序退出
    下面用 for 循环看下
    for 循环: (推荐)
    a = (i for i in range(20))     # 制作生成器
    for i in a:                    # 进行 for 循环
        print(i)                    # 打印取值,不会报错(推荐)
    pycharm 执行结果:
    0
    1
    2
    3
    4
    5
    6
    7             #很完美的执行了且没有报错
        ********以上是调用生成器的2种方法********
    下面分析下 range(),它在 py2 里是一个自动生成列表(range(3)和 [0,1,2]是同等关系,
    但是在 py3 中,range(3)只是一个生成器
    print(range(20))      # 用 py3 执行这个之后会得出一个生成器,不会立即生成
    pycharm执行效果:
    range(0, 20)
    小结:
    在 py2 内:
    range = list(range 的值会立即生成一个列表并返回),
    有一个叫 xrange 的,是生成器(等同于 py3 内的range)
    在 py3 内:
    range 就是一个生成器,没有 xrange
    8、斐波那契数列:斐波那契数列(Fibonacci sequence),又称黄金分割数列、也可看做是一个数学计算公式,
    因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,
    指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
    在数学上,斐波纳契数列以如下被以递推的方法定义:
    F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)除前面2个数( 0 , 1 )之外,任意一个数都是有前两个数相加
    斐波那契数列用列表生成器是没法实现的,但是用函数可以轻松实现
    def fid(max):
        n,a,b = 0,0,1            # 一次赋 三个值
        while n < max :
            yield b              # 把函数内的执行过程冻结在这一步,并把 b 得出的值返回给函数外 next()
            a,b = b,a+b          # 计算公式
            n = n+1              #计数
        return "done"            
    print(next(fid(20)))         # 可以一次取一个值重复使用此语句
    ——————————分割线————————————
    a = fid(20)                  # 也可以for 循环一次取值
    for i in a:
        print(i)
    小结:
    这种方式可以函数执行中的每一个状态传给函数外边,
    返回函数内的每一个值,并且可以加括号不执行,想调用的时候再执行
    9、函数进阶 -- 函数写生成器
    1)列表生成式(列表最复杂的生成式:只能处理三元运算)
    2)函数生成器,(用来计算更复杂的运算,比如读取文件,调取文件中的多个值)
    def usa(n):
    
        count = 0
        while count < n:
            print("loop",count)
            count += 1
            yield count       #和 return 效果相似,但它会冻结在这里,等待下次 next 调用
    
    f = usa(5)                #一旦调用,证明生成器已生效
    next(f)                   # 执行 yield 的第一种方法
    f.__next__()              # 执行 yield 第二种方法
    next(f)
    yield 和 return 以及 next 的作用与区别:
    yield 是返回本次执行结果,并冻结当前的执行过程等待 next 的下次调用
    return 的作用是返回并终止函数,
    next 唤醒冻结的 yield 所在函数,并继续执行,直到遇到下一个 yield
    这样的方法可以用于边读取文件,边处理文件,提高效率,通过这样的方式,可以把函数运行过程的每一个执行步骤返回并处理
    小结:
    1、只要函数内有 yield ,就代表这个函数是一个生成器,
    2、一个函数内有了yield 之后,执行函数(函数名加())就会变成生成器,而 return 在生成器内,就意味着会终止生成器,直接报错
    10、函数进阶 -- 生成器的 send 方法及单线程并发编程
    def usa(n):
        count = 0
        while count < n:
            print("count",count)
            count += 1
            er = yield count   # 接收外部指令并返回指令
            if er == "stop":
                break
            print("---er",er)
        return 88
    f = usa(8)
    next(f)
    f.send("stop")            # 唤醒并继续执行,发送一个信息到生成器内部
    小结:next():唤醒生成器并继续执行, send:唤醒并继续执行,且会发送一个信息到生成器内部 
      1、生成器不但能往外返回值,还可以从外部传参进去
    def calc():
        while True:
            y = yield
            print("外部传参进来", y)
    c = calc()
    c.__next__()
    
    
    for i in range(10):
        c.send(i)
        2、单线程高并发练习题(吃包子游戏)
    def steamed(name):
        print("用户%s开始吃包子了" % name)
        while True:
            n = yield
            print("%s吃了第%s个包子" % (name, n))
    
    
    user_one = steamed("user_one")
    user_tow = steamed("user_tow")
    user_three = steamed("user_three")
    user_one.__next__()
    user_tow.__next__()
    user_three.__next__()
    
    
    for i in range(10):
        print("————生产的第%s批包子——————" % i)
        user_one.send(i)
        user_tow.send(i)
        user_three.send(i)
    # 此方法适用于边分析数据,边产生数据的场景,边生产,边分析,边判断
        # 此方法适用于边分析数据,边产生数据的场景,边生产,边分析,边判断
    11、函数进阶 -- 迭代器
    所谓的迭代,也可以理解为只要能用 for 循环的对象,迭代 = 循环,可以这么理解,
    可直接用于 for 循环的有这几种:
    1、数据集合【列表(list)、字典(dict)、元祖(tuple)、集合(set)、字符串(str)】.

    2、生成式 generator 类型(包括生成器和带 yield 的函数生成器)

    这些可以直接用于 for 循环的对象统称为可迭代对象:iterable(可迭代对象,也可理解为可 for 循环对象)
    判断是否为可迭代对象可以使用 isinstance() 这个方法,它是一个工具包(需要导入)
    导入工具包语法:from collections import Iterable


    可迭代对象有数据集合,生成器类型2种,
    而成器类型不但可 for 循环,还可以被 next()函数不断调用并返回下一个值,直到最后抛出 Stoplteration (报错)异常错误

    ***(记住)可以被next()函数调用并不断返回下一个值的对象被称为迭代器:Iterator(生成器只是迭代器中的一种)
    但是列表(list),字典(dict),字符串(str)这些都只是可迭代的对象,虽然不是迭代器,
    但是可以用 iter() (函数)把它们变成迭代器
    n = ["list","dict","str","int","set"]  # 这是一个列表
    n1 = iter(n)                    # 只需要 iter() 就会变成迭代器
    print(next(n1))
    print(next(n1))                # 每执行一次,往下走一次
    print(n1.__next__())
    迭代器 Iterator 是一个数据流(计算公式)迭代器对象可以被next()函数调用,并不断返回下一个数据,直到报错,
    可以把他看做是个有序序列,不知道长度,是有惰性的,只有需要了调用了,才会执行

    小结:1、凡是可作用于 for 循环的对象,都是可迭代对象
    2、凡是可以用 next() 函数的,都是迭代器 Iterator 类型,它们表示一个惰性的计算的序列
    3、集合【list,dict,set,str】这些都是可迭代对象,但不是迭代器,可以通过 iter() 函数转成一个迭代器
    4、python3 内的 for 循环,本质上就是通过不断调用 next() 函数实现的
    作业题:员工增删改查
    本章作业题:员工增删改查表
    用代码实现一个简单的员工信息增删改查表
    需求:

    1、支持模糊查询,
    (1、find name ,age form staff_table where age > 22
    (查找 staff_fable (文件)内所有 age > 22 的name 和 age全部打印)
    (2、find * from staff_table where dept = "IT"
    (查找所有部门是 IT的所有列打印出来*代表所有)
    (3、find * from staff_table where enroll_date like "2013"
    (查找所有 enroll_date (登记日期)为 2013的序列并打印)

    2、可创建员工信息记录,以 phone 做唯一键(既不允许表内有手机号重复的情况),staff_id 需自增
    语法格式为:add staff Alex Li,25,134435344,IT,2015-10-29

    3、可删除指定员工信息记录,输入员工id 即可删除
    语法格式:del staff 3

    4、可修改员工信息 语法如下:
    UPDATE staff_table SET dept = "Market" WHERE dept = "IT"(把部门是 IT 的全部更改成 Market)
    UPDATE staff_table SET age = 25 WHERE name = "Alex Li" (把 name = Alex Li 的年龄改成 25 )
    以上每条语句执行完毕后,要显示影响了多少条记录,(比如查询,要显示查询出多少条、修改要显示修改多少条
     
  • 相关阅读:
    Match function in R
    Excel lastindex of a substring
    Print a file's last modified date in Bash
    IQR(Inter-Quartile Range)
    nohup 让进程在后台可靠运行的几种方法
    extension(类扩展)和 category(类别)
    Dart学习-操作符
    为什么我觉得Python烂的要死?
    自定义 Cordova插件(基础篇)
    Cordova入门系列(四)自定义Cordova插件--showToast
  • 原文地址:https://www.cnblogs.com/guoyilong/p/10180326.html
Copyright © 2011-2022 走看看