zoukankan      html  css  js  c++  java
  • 第三章| 3.3 函数进阶

     4、高阶函数

    命名空间

    又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方

    名称空间共3种,分别如下

    • locals: 是函数内的名称空间,包括局部变量和形参
    • globals: 全局变量,函数定义所在模块的名字空间
    • builtins: 内置模块的名字空间

    不同变量的作用域不同就是由这个变量所在的命名空间决定的。

    作用域即范围

    • 全局范围:全局存活,全局有效
    • 局部范围:临时存活,局部有效

    查看作用域方法 globals(),locals()

    作用域查找顺序

     LEGB:

    L:locals  ; E:enclosing相邻的 ; G:globls  ; B:builtins

    n = 10
    def func():
        n = 20
        print('func:' ,n)  #func:20
        def func2():
            n = 30
            print(' func2' ,n) #func2:30
    
            def func3():
                print('func3:' , n) #func3:30
            func3()
        func2()
    func() 

    LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__

    • locals 是函数内的名字空间,包括局部变量和形参
    • enclosing 外部嵌套函数的名字空间
    • globals 全局变量,函数定义所在模块的名字空间
    • builtins 内置模块的名字空间

    闭包

      在函数里边又套了一层子函数,在子函数被返回了,就是当外层函数执行的时候子函数被返回了返回了内存地址;然后在外边执行这个子函数的时候它又引用了外边函数的这个变量,相当于这个子函数跟外层函数有某种扯不清的关系,这种关系就叫闭包。

      关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。也就是说,内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。

     装饰器( 闭包+函数的重新赋值)

    软件开发中的一个原则“开放-封闭”原则,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

    • 封闭:已实现的功能代码块不应该被修改
    • 开放:对现有功能的扩展开放

    实现装饰器知识储备:
    1. 函数即“变量”
    2. 高阶函数;  a、把一个函数名当做实参传入另一个函数 ;b、返回值中包含函数名(不修改函数的调用方式) 
    3. 嵌套函数 
    高阶函数+嵌套函数=》装饰器

    在不改变原代码前提下给它加一个装饰的功能:搞一个高阶函数,把要修饰的当做参数传进去,然后在里边返回。

    定义:本质是函数(装饰其他函数)就是为其他函数添加附加功能
    原则:1.不能修改被装饰的函数的源代码;2.不能修改被装饰的函数的调用方式;
    装饰器对它被装饰的函数是完全透明的,就是别人不知道你改了它的,没影响。

    如:

    编写装饰器,为每个函数加上统计运行时间的功能

    提示:在函数开始执行时加上start=time.time()就可纪录当前执行的时间戳,函数执行结束后在time.time() - start就可以拿到执行所用时间

    import time
    def deco(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s'%(stop_time - start_time))
    def test1():
        time.sleep(2)
        print('in the test1')
    
    deco(test1)     #test1()加括号就是把它的运行结果传进去了; 这种调用方式改变了函数的调用方式。改变了test1的调用方式。test1=deco(test1) ---> test1();前提是把它修改成第二种形式的高阶函数加个return返回它的内存地址即它的返回值。
      
    
    
    
    import time
    def timer(func):  #timer(test1)  func=test1
        def deco():
            start_time = time.time()
            func()  #运行test1()
            stop_time = time.time()
            print('the func run time is %s'%(stop_time - start_time))
        return deco  #第二种形式的高阶函数
    def test1():
        time.sleep(2)
        print('in the test1')
    #print(timer(test1))  #返回的是deco的一个内存地址
    test1 = timer(test1) #执行timer(test1)这个函数,把test1当做参数传进去;拿到一个返回结果deco; 代码走到这一步的时候会执行timer(test1),不会往下走了
    test1()  ## 实际上运行执行的是deco    #既没有改变调用方式也没有改变原代码
    


    #######初级装饰器 import time def timer(func): #timer(test1) func=test1 def deco(): start_time = time.time() func() #运行test1() stop_time = time.time() print('the func run time is %s'%(stop_time - start_time)) return deco @timer #--->> test1 = timer(test1) def test1(): time.sleep(2) print('in the test1') @timer #--->> test2 = timer(test2) def test2(): time.sleep(4) print('in the test2') test1() test2()
    #####中级带参数装饰器
    import time
    def timer(func):  #timer(test1)  func=test1
        def deco(*args,**kwargs):
            start_time = time.time()
            func(*args,**kwargs)  #运行test1()
            stop_time = time.time()
            print('the func run time is %s'%(stop_time - start_time))
            return func  #把函数的结果要给返回了
        return deco
    @timer #--->>  test1 = timer(test1)
    def test1():
        time.sleep(2)
        print('in the test1')
        return 'from test1'
    @timer #--->> test2 = timer(test2)  = deco  test2(name,age)=deco(name,age) ==deco(arg1,arg2)-->func(arg1,arg2)
    def test2(name,age):
        time.sleep(4)
        print('test2',name,age)
    
    test1()
    test2('alex',22)
    打印:
    in the test1
    the func run time is 2.0001144409179688
    test2 alex 22
    the func run time is 4.0002288818359375
    #####高级版
    import time
    user,passwd = 'kris' , 'abc123'
    def auth(auth_type):  #auth_type : 'local'
        print("auth func:" , auth_type)
        def outer_wrapper(func):
            def wrapper(*args, **kwargs):
                print("wrapper func args:" , *args,**kwargs)
    
                if auth_type == "local":
                    username = input("Username:").strip()
                    password = input("Password:").strip()
                    if user == username and passwd == password:
                        print("33[33;1m User has passed authentication 33[0m" )
                        res = func(*args,**kwargs) #from home
                        print("-------after authentication")
                        return res  #这样就把home的执行结果返回了
                    else:
                        exit("33[31;lmInvalid username or password33[0m")
                elif auth_type == "ldap":
                    print("ladp呀呀")
            return wrapper
        return outer_wrapper
    def index():
        print("welcome to index page")
    @auth (auth_type="local")   #本地的认证 #home = wrapper();最终返回的是wrapper;你现在加上括号了,执行了;
    def home():
        print("welcome to home page")
        return "from home"   #home执行完之后返回的数据,不能改变执行结果;怎么获取home的返回结果呢
    @auth(auth_type="ldap")   #远程的认证
    def bbs():
        print("welcome to bbs page")
    index()
    print(home()) #相当于调用的是wrapper()
    bbs()
    ##打印:
    auth func: local
    auth func: ldap
    welcome to index page
    wrapper func args:
    Username:kris
    Password:abc123
     User has passed authentication 
    welcome to home page
    -------after authentication
    from home
    wrapper func args:
    ladp呀呀
    #_*_coding:utf-8_*_
    
    user_status = False #用户登录了就把这个改成True
    
    def login(func): #把要执行的模块从这里传进来
    
        def inner(*args,**kwargs):#再定义一层函数
            _username = "alex" #假装这是DB里存的用户信息
            _password = "abc!23" #假装这是DB里存的用户信息
            global user_status
    
            if user_status == False:
                username = input("user:")
                password = input("pasword:")
    
                if username == _username and password == _password:
                    print("welcome login....")
                    user_status = True
                else:
                    print("wrong username or password!")
    
            if user_status == True:
                func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能
    
        return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
    
    
    def home():
        print("---首页----")
    
    @login
    def america():
        #login() #执行前加上验证
        print("----欧美专区----")
    
    def japan():
        print("----日韩专区----")
    
    # @login
    def henan(style):
        '''
        :param style: 喜欢看什么类型的,就传进来
        :return:
        '''
        #login() #执行前加上验证
        print("----河南专区----")
    
    home()
    # america = login(america) #你在这里相当于把america这个函数替换了
    henan = login(henan)
    
    # #那用户调用时依然写
    america()
    
    henan("3p")

     带参数的装饰器

    #_*_coding:utf-8_*_
    user_status = False #用户登录了就把这个改成True
    
    def login(auth_type): #把要执行的模块从这里传进来
        def auth(func):
            def inner(*args,**kwargs):#再定义一层函数
                if auth_type == "qq":
                    _username = "alex" #假装这是DB里存的用户信息
                    _password = "abc!23" #假装这是DB里存的用户信息
                    global user_status
    
                    if user_status == False:
                        username = input("user:")
                        password = input("pasword:")
    
                        if username == _username and password == _password:
                            print("welcome login....")
                            user_status = True
                        else:
                            print("wrong username or password!")
    
                    if user_status == True:
                        return func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能
                else:
                    print("only support qq ")
            return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
    
        return auth
    
    def home():
        print("---首页----")
    
    @login('qq')
    def america():
        #login() #执行前加上验证
        print("----欧美专区----")
    
    def japan():
        print("----日韩专区----")
    
    @login('weibo')
    def henan(style):
        '''
        :param style: 喜欢看什么类型的,就传进来
        :return:
        '''
        #login() #执行前加上验证
        print("----河南专区----")
    
    home()
    # america = login(america) #你在这里相当于把america这个函数替换了
    #henan = login(henan)
    
    # #那用户调用时依然写
    america()
    
    # henan("3p")

    5、生成器&迭代器

    列表生成器

    直接对列表里边的值修改

    >>>a = [ i*i for i in range(10)]
    >>>a
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    >>>a = list(range(10))
    >>>a
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,]
    >>>a = [ i if i < 5 else i*i for i in a]
    >>>a
    [0, 1, 2, 3, 4, 25, 36, 49, 64, 81 ]

    生成器

    range(0,10,2)代表指向了0,2,4,6,8这几个值,最后的2代表步长;

    >>> m=range(10)
    >>> m[2]
    2
    >>> m[-1]
    9
    >>> m[5]
    5

    yield 语句可以让普通函数变成一个生成器,并且相应的 __next__() 方法返回的是 yield 后面的值。

    一种更直观的解释是:程序执行到 yield 会返回值并暂停,再次调用 next() 时会从上次暂停的地方继续开始执行。

    生成器的创建方式:1.列表生成式()(通过list函数生成列表); 2.函数

    >>>a2 = (i for i in range(1000))
    >>>a2
    <generator  object  <genexpr>  at 0x1014a73b8>  ##生成了一个生成器
    >>>next(a2)
    0
    >>>next(a2)
    1
    >>>next(a2)
    2

     上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

    >>> g = (x * x for x in range(10))
    >>> for n in g:
    ...     print(n)
    ...
    0
    1
    4
    9
    16
    25
    36
    49
    64
    81

     

    def fib(max):
        n, a, b = 0, 0 ,1
        while n < max:
            print('before yield')
            yield b  #把函数的执行过程返回冻结在这一步了,并且把b的值返回给了外面的next()
            print(b)
            a, b = b, a+b
            n = n + 1
        return 'done'
    f = fib(15) #turn function into a generator
    next(f) #first time call next()
    next(f) #first time call next()
    next(f) #first time call next()
    next(f)
    
    打印出:
    before yield
    1
    before yield
    1
    before yield
    2
    before yield

    斐波那契

    generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

    比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

    1, 1, 2, 3, 5, 8, 13, 21, 34, ...

     主要就是yield后边那两句;里边有yield函数一加括号,代码根本不执行,只生成一个生成器。

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            print(b)
            a, b = b, a + b
            n = n + 1
        return 'done’
    
    >>> fib(10)
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    done

    fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

    也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

    ##用函数写生成器
    def fib(max):
        n,a,b = 0,0,1
    
        while n < max:
            #print(b)
            yield  b
            a,b = b,a+b
    
            n += 1
    
        return 'done'
    
    
    >>> f = fib(6)
    >>> f
    <generator object fib at 0x104feaaa0>

    用next调用它结果是一样的:

    1
    1
    2
    3
    5
    8
    13
    21
    34
    55

    这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

     函数写生成器

    def range2(n):
        count = 0
        while count < n:
            #print(count) #
            count +=1
            yield count  #return
    print(range2(10))    #打印: <generator object range2 at 0x10159ac50>  #一个生成器
    
    打印:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def range2(n):
        count = 0
        while count < n:
            print('count',count)
            count +=1
            yield count  #return
    
    new_range = range2(10)
    r1 = next(new_range)
    print(r1)
    for i in range2(10):
        print(i)
    
    打印:
    count 0
    1
    count 0
    1
    count 1
    2
    count 2
    3
    count 3
    4
    count 4
    5
    count 5
    6
    count 6
    7
    count 7
    8
    count 8
    9
    count 9
    10
    def range2(n):
        count = 0
        while count < n:
            print(‘count’ ,count)  
            count +=1
            yield count  #return
    
    new_range = range2(10)
    r1 = next(new_range)
    print(r1) 
    r2 = next(new_range)
    print'干别的事'print(r2)
    next(new_range)  # == new_range.__next__() #这两个是一样的
    new_range.__next__()
    
    打印:
    count 0
    1
    count 1
    干别的事
    2
    count 2
    count 3

    生成器调用方法

    python2:  range = list           Python3: range = 生成器

                   xrange = 生成器                     xrange | 没有

    生成器send方法

    只要函数里边有yield就是生成器了;

    迭代器(循环)

    范围比生成器大

    可以直接作用于for循环的数据类型有以下几种:

    一类是集合数据类型,如listtupledictsetstr等;

    一类是generator,包括生成器和带yield的generator function。

    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

    凡是可作用于for循环的对象都是Iterable(可迭代)类型;凡是可作用于next()函数的对象都是Iterator(迭代器)类型,它们表示一个惰性计算的序列;集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

    可以使用isinstance()判断一个对象是否是Iterable对象:

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance((x for x in range(10)), Iterable)
    True
    >>> isinstance(100, Iterable)
    False

    而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

    *可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

    可以使用isinstance()判断一个对象是否是Iterator对象:

    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    >>> isinstance([], Iterator)
    False
    >>> isinstance({}, Iterator)
    False
    >>> isinstance('abc', Iterator)
    False

    Python3的for循环本质上就是通过不断调用next()函数实现的,例如:

    for x in [1, 2, 3, 4, 5]:
        pass

    实际上完全等价于:

    # 首先获得Iterator对象:
    it = iter([1, 2, 3, 4, 5])
    next(it)
    1
    next(it)
    2
    # 循环:
    while True:
        try:
            # 获得下一个值:
            x = next(it)
        except StopIteration:
            # 遇到StopIteration就退出循环
            break

    创建一个迭代器有3种方法,其中前两种分别是:

    1. 为容器对象添加 __iter__() 和 __next__() 方法(Python 2.7 中是 next());__iter__() 返回迭代器对象本身 self__next__() 则返回每次调用 next() 或迭代时的元素;
    2. 内置函数 iter() 将可迭代对象转化为迭代器
    3. 生成器,生成器通过 yield语句快速生成迭代器,省略了复杂的 __iter__() & __next__() 方式。

    创建迭代器对象的好处是当序列长度很大时,可以减少内存消耗,因为每次只需要记录一个值即可。

  • 相关阅读:
    HDU 5119 Happy Matt Friends(DP || 高斯消元)
    URAL 1698. Square Country 5(记忆化搜索)
    POJ 2546 Circular Area(两个圆相交的面积)
    URAL 1430. Crime and Punishment(数论)
    HDU 1111 Secret Code (DFS)
    HDU 1104 Remainder (BFS求最小步数 打印路径)
    URAL 1091. Tmutarakan Exams(容斥原理)
    PDO连接mysql8.0报PDO::__construct(): Server sent charset (255) unknown to the client. Please, report to the developers错误
    swoole简易实时聊天
    centos安装netcat
  • 原文地址:https://www.cnblogs.com/shengyang17/p/9160825.html
Copyright © 2011-2022 走看看