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__() 方式。

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

  • 相关阅读:
    LeetCode OJ String to Integer (atoi) 字符串转数字
    HDU 1005 Number Sequence(AC代码)
    HDU 1004 Let the Balloon Rise(AC代码)
    HDU 1003 Max Sum(AC代码)
    012 Integer to Roman 整数转换成罗马数字
    011 Container With Most Water 盛最多水的容器
    010 Regular Expression Matching 正则表达式匹配
    007 Reverse Integer 旋转整数
    006 ZigZag Conversion
    005 Longest Palindromic Substring 最长回文子串
  • 原文地址:https://www.cnblogs.com/shengyang17/p/9160825.html
Copyright © 2011-2022 走看看