zoukankan      html  css  js  c++  java
  • 函数(函数基础、装饰器、递归、匿名函数)

     1. 函数知识体系-python代码缩进

    什么是函数?
    为什么要用函数?
    函数的分类:内置函数与自定义函数
    如何自定义函数
      语法
      定义有参数函数,及有参函数的应用场景
      定义无参数函数,及无参函数的应用场景
      定义空函数,及空函数的应用场景
    调用函数
        如何调用函数
        函数的返回值
        函数参数的应用:形参和实参,位置参数,关键字参数,默认参数,*args,**kwargs
    高阶函数(函数对象)
    函数嵌套
    作用域与名称空间
    装饰器
    迭代器与生成器及协程函数
    三元运算,列表解析、生成器表达式
    函数的递归调用
    内置函数
    面向过程编程与函数式编程

    Python中的代码缩进

    Python中,是通过代码的缩进,来决定代码的逻辑的。(同缩进的先执行完,再执行更多缩进的代码块)

    通俗的说,Python中的代码的缩进,不是为了好看,而是觉得代码的含义,上下行代码之间的关系。

    缩进弄错了,就会导致程序出错,执行结果变成不是你想要的了。

    参考:  https://www.crifan.com/tutorial_python_indent/

    如下:

    代码块 

     对应的,上述几个图解中,def indentDemo后面的代码,也就被因此成为代码块:

    说白了,就是一个逻辑上的概念,可以简单理解为其他语言中的,一个函数内的代码,一个if判断内的代码等等相应的概念;

    其他语言中的代码缩进:只是决定了是否好看,不影响代码逻辑和运行结果

    2. 函数基础

    2.1 引子

    为何要用函数之不用函数的问题

    #1、代码的组织结构不清晰,可读性差
    #2、遇到重复的功能只能重复编写实现代码,代码冗余
    #3、功能需要扩展时,需要找出所有实现该功能的地方修改之,无法统一管理且维护难度极大 

    函数是什么

    针对二中的问题,想象生活中的例子,修理工需要实现准备好工具箱里面放好锤子,扳手,钳子等工具,然后遇到锤钉子的场景,拿来锤子用就可以,而无需临时再制造一把锤子。
    
    修理工===>程序员
    具备某一功能的工具===>函数
    
    要想使用工具,需要事先准备好,然后拿来就用且可以重复使用
    要想用函数,需要先定义,再使用

     函数分类

    #1、内置函数
    为了方便我们的开发,针对一些简单的功能,python解释器已经为我们定义好了的函数即内置函数。对于内置函数,我们可以拿来就用而无需事先定义,如len(),sum(),max()
    ps:我们将会在最后详细介绍常用的内置函数。
    
    #2、自定义函数
    很明显内置函数所能提供的功能是有限的,这就需要我们自己根据需求,事先定制好我们自己的函数来实现某种功能,以后,在遇到应用场景时,调用自定义的函数即可。例如

    2.2. 函数定义

    如何自定义函数

    #语法
    def 函数名(参数1,参数2,参数3,...):
        '''注释'''
        函数体
        return 返回的值
    
    #函数名要能反映其意义
    
    
    def auth(user:str,password:str):
        '''
        auth function
        :param user: 用户名
        :param password: 密码
        :return: 认证结果
        '''
        if user == 'egon' and password == '123':
            return 1
    # print(auth.__annotations__) #{'user': <class 'str'>, 'password': <class 'str'>, 'return': <class 'int'>}
    
    user=input('用户名>>: ').strip()
    pwd=input('密码>>: ').strip()
    res=auth(user,pwd)
    print(res)

    函数使用的原则:先定义,再调用

    函数即“变量”,“变量”必须先定义后引用。未定义而直接引用函数,就相当于在引用一个不存在的变量名
    #测试一
    def foo():
        print('from foo')
        bar()
    foo() #报错
    
    #测试二
    def bar():
        print('from bar')
    def foo():
        print('from foo')
        bar()
    foo() #正常
    
    #测试三
    def foo():
        print('from foo')
        bar()
        
    def bar():
        print('from bar')
    foo() #会报错吗?
    
    
    #结论:函数的使用,必须遵循原则:先定义,后调用
    #我们在使用函数时,一定要明确地区分定义阶段和调用阶段
    
    #定义阶段
    def foo():
        print('from foo')
        bar()
    def bar():
        print('from bar')
    #调用阶段
    foo()
    View Code

    函数定义的3中形式

    #1、无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印
    #2、有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值
    #3、空函数:设计代码结构

    #定义阶段
    def tell_tag(tag,n): #有参数
        print(tag*n)
    
    def tell_msg(): #无参数
        print('hello world')
    
    #调用阶段
    tell_tag('*',12)
    tell_msg()
    tell_tag('*',12)
    
    '''
    ************
    hello world
    ************
    '''
    
    #结论:
    #1、定义时无参,意味着调用时也无需传入参数
    #2、定义时有参,意味着调用时则必须传入参数
    
    无参、有参
    def auth(user,password):                             
        '''                                                           
        auth function                                                 
        :param user: 用户名                                              
        :param password: 密码                                           
        :return: 认证结果                                                 
        '''                                                           
        pass                                                          
                                                                      
    def get(filename):                                                
        '''                                                           
        :param filename:                                              
        :return:                                                      
        '''                                                           
        pass                                                          
                                                                      
    def put(filename):                                                
        '''                                                           
        :param filename:                                              
        :return:                                                      
        '''                                                           
    def ls(dirname):                                                  
        '''                                                           
        :param dirname:                                               
        :return:                                                      
        '''                                                           
        pass                                                          
    
    #程序的体系结构立见           
    
    空函数
    空函数

    2.3 函数调用

    函数调用的三种形式

    1 语句形式:foo()
    2 表达式形式:3*len('hello')
    3 当中另外一个函数的参数:range(len('hello'))

    函数返回值

    无return->None
    return 1个值->返回1个值
    return 逗号分隔多个值->元组
    什么时候该有返回值?
        调用函数,经过一系列的操作,最后要拿到一个明确的结果,则必须要有返回值
        通常有参函数需要有返回值,输入参数,经过计算,得到一个最终的结果
    什么时候不需要有返回值?
        调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值
        通常无参函数不需要有返回值

    2.4 函数参数

    参考:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431752945034eb82ac80a3e64b9bb4929b16eeed1eb9000

    #1、位置参数:按照从左到右的顺序定义的参数
            位置形参:必选参数
            位置实参:按照位置给形参传值
    
    #2、默认参数:形参在定义时就已经为其赋值
            可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认
            参数(形参)
            注意的问题:
                    1. 只在定义时赋值一次
                    2. 默认参数的定义应该在位置形参右面
                    3. 默认参数通常应该定义成不可变类型
    
    #3.可变参数:可变长指的是实参值的个数不固定
            
    
    #4、关键字参数:按照key=value的形式定义的实参
            无需按照位置为形参传值
            注意的问题:
                    1. 关键字实参必须在位置实参右面
                    2. 对同一个形参不能重复传值
    
    #5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
    可以保证,传入的参数中一定包含某些关键字

    函数参数几点总结:

    1.必选参数: 有必选参数,调用时候就必须跟参数个数一致,也不能为空

    def test(a,b):
        print(a,b)
    
    test(1)
    
    结果:
    TypeError: test() takes exactly 2 arguments (1 given)
    
    test(1,2)  #结果 (1,2)

    2.当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。(选参数在前,默认参数在后)

    把年龄和城市设为默认参数,name和gender是必选参数
    
    def enroll(name, gender, age=6, city='Beijing'):
        print('name:', name)
        print('gender:', gender)
        print('age:', age)
        print('city:', city)

    3.牢记:默认参数必须指向不变对象!

    def add_end(L=[]):
        L.append("END")
        return  L
    
    #正常调用结果是正常的,函数的默认参数没有变化
    print add_end([1,2,3])
    print add_end(['a','b','c'])

    #默认参数调用,第一次正常,第二次调用的时候,默认参数就已经变成了['ENd'].
    print add_end() print add_end() 结果: [1, 2, 3, 'END'] ['a', 'b', 'c', 'END'] ['END'] ['END', 'END']

    分析:Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,
    默认参数的内容就变了,不再是函数定义时的[]了。

    4. 可变参数。可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。

        Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去。可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。

    def foo1(x,y,*args):
        print(args)
    
    foo1(1,2,3,4,5) #从左到右依次给传参,剩余的参数自动作为可变参数args
    
    foo1(1,2,*[3,4,5,6])  #一般用这种调用方式
    
    foo1(*[1,2,3,4])

    foo1(*[1,2]) 结果: (
    3, 4, 5) (3, 4, 5, 6) (3, 4)
    ()

    #一般常见可变参数使用
    >>> nums = [1, 2, 3]
    >>> calc(*nums)

    5.关键字参数:允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

    def foo(x, y, **kwargs):
        print(x, y)
        print(kwargs)
    
    
    foo(1, y=2, a=1, b=2, c=3)  #从左到右依次给传参,剩余的参数自动作为关键字参数kwargs
    结果:
    (1, 2)
    {'a': 1, 'c': 3, 'b': 2}
    
    foo(1,y=2,**{'a':1,'b':2})
    结果:
    (1, 2)
    {'a': 1, 'b': 2}
    
    foo(**{'x':1,'y':2,'other':3})
    结果:
    (1, 2)
    {'other': 3}


    #关键字参数一般使用
    >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 24, **extra)

    6.命名关键字参数:和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。(python 2中没有)

    对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。

    仍以person()函数为例,我们希望检查是否有cityjob参数:

    def person(name, age, **kw):
        if 'city' in kw:
            # 有city参数
            pass
        if 'job' in kw:
            # 有job参数
            pass
        print('name:', name, 'age:', age, 'other:', kw)
    
    
    但是调用者仍可以传入不受限制的关键字参数:
    >>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)

    如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收cityjob作为关键字参数。这种方式定义的函数如下:

    def person(name, age, *, city, job):
        print(name, age, city, job)

    如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

    def person(name, age, *args, city, job):
        print(name, age, args, city, job)

    参数组合:

    在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

    3.函数对象、函数嵌套、名称空间与作用域、装饰器

    3.1 函数对象

    函数是第一类对象,即函数可以当作数据传递

    #1 可以被引用
    #2 可以当作参数传递
    #3 返回值可以是函数
    #3 可以当作容器类型的元素

    3.2 函数嵌套

    #函数嵌套定义
    def f1():       ------------- 1  函数定义
        def f2():   --------------3 调用函数进入函数内部代码块,定义f2
            def f3(): ------------ 5 定义f3
                print('from f3') -----------------7 进入f3,执行print
            f3()  ----------------6 嗲用f3
        f2() --------------------4 调用f2 
    
    f1() ----------------2 调用f1
    

    注意: 根据代码缩进来执行,如上
    改写一下上面的代码: 装饰器的调用差不多可以这样理解.外函数返回内函数的引用.

    def father(name):
    print('from father %s' %name)
    def son():
    print('from son')
    def grandson():
    print('from grandson')
    return 1111111
    return grandson

    return son
    print father('林海峰')()()

    结果:

    from father 林海峰
    from son
    from grandson

    1111111



    #函数嵌套调用 def max(x,y): return x if x > y else y def max4(a,b,c,d): res1=max(a,b) res2=max(res1,c) res3=max(res2,d) return res3 print(max4(1,2,3,4))

    3.3 名称空间与作用域

    什么是名称空间?

    #名称空间:存放名字的地方,三种名称空间,(之前遗留的问题x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方)

    名称空间的加载顺序

    python test.py
    #1、python解释器先启动,因而首先加载的是:内置名称空间
    #2、执行test.py文件,然后以文件为基础,加载全局名称空间
    #3、在执行文件的过程中如果调用函数,则临时产生局部名称空间

     名字的查找顺序

    局部名称空间--->全局名称空间--->内置名称空间
    
    #需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例
    
    # max=1
    def f1():
        # max=2
        def f2():
            # max=3
            print(max)
        f2()
    f1()
    print(max)

    作用域

    #1、作用域即范围
            - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
          - 局部范围(局部名称空间属于该范围):临时存活,局部有效
    #2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
    x=1
    def f1():
        def f2():
            print(x)
        return f2
    x=100
    def f3(func):
        x=2
        func()
    x=10000
    f3(f1())
    
    #3、查看作用域:globals(),locals()
    
    
    LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
    locals 是函数内的名字空间,包括局部变量和形参
    enclosing 外部嵌套函数的名字空间(闭包中常见)
    globals 全局变量,函数定义所在模块的名字空间
    builtins 内置模块的名字空间

    3.4 闭包函数

    在函数内部定义的函数和外部定义的函数是一样的,只是他们无法被外部访问

    将 g 的定义移入函数 f 内部,防止其他代码调用 g:
    def f():
        print 'f()...'
        def g():
            print 'g()...'
        return g

    参考:  https://www.cnblogs.com/Lin-Yi/p/7305364.html

    闭包:

    闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对
    象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重
    复使用性。

    def line_conf(a, b):
        def line(x):
            return a*x + b
    
        return line
    
    
    line1 = line_conf(1, 1)
    line2 = line_conf(4, 5)
    print(line1(5), line2(5))

    这个例子中,函数line与环境变量a,b构 成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

    如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。

    在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
    
    一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的
    临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

    闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量
     

    # 希望一次返回3个函数,分别计算1x1,2x2,3x3:
    def count():
      fs = []
      for i in range(1, 4):
        def f():
          return i*i
        fs.append(f)
      return fs
    f1, f2, f3 = count()

    
    

    你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果全部都是 9。

    返回闭包不能引用循环变量,请改写count()函数,让它正确返回能计算1x1、2x2、3x3的函数。

    def count():
      fs = []
      for i in range(1, 4):
    #问题的产生是因为函数只在执行时才去获取外层参数i,若函数定义时可以获取到i,问题便可解决。
    #而默认参数正好可以完成定义时获取i值且运行函数时无需参数输入的功能,
    #所以在函数f()定义中改为f(m = i),函数f返回值改为m*m即可.
        def f(m = i):
          return m * m
        fs.append(f)
      return fs
    f1, f2, f3 = count()
    print f1(), f2(), f3()

    #闭包函数的实例
    # outer是外部函数 a和b都是外函数的临时变量
    def outer( a ):
        b = 10
        # inner是内函数
        def inner():
            #在内函数中 用到了外函数的临时变量
            print(a+b)
        # 外函数的返回值是内函数的引用
        return inner
    
    if __name__ == '__main__':
        # 在这里我们调用外函数传入参数5
        #此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo
        # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数
        demo = outer(5)
        # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
        # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
        demo() # 15
    
        demo2 = outer(7)
        demo2()#17
    闭包函数实例

    从上面例子是我写的一个最简单的很典型的闭包。我估计如果是初学的小伙伴,可能很多名词都不明白是什么意思,没关系,我把这些名词按照自己的理解去解释一下~

    1 外函数返回了内函数的引用:

    当我们进行a=1的时候,实际上在内存当中有一个地方存了值1,然后用a这个变量名存了1所在内存位置的引用。引用就好像c语言里的指针,大家可以把引用理解成地址。a只不过是一个变量名字,
    a里面存的是1这个数值所在的地址,就是a里面存了数值1的引用。   相同的道理,当我们在python中定义一个函数def demo(): 的时候,内存当中会开辟一些空间,存下这个函数的代码、内部的局部变量等等。这个demo只不过是一个变量名字,它里面存了
    这个函数所在位置的引用而已。我们还可以进行x
    = demo, y = demo, 这样的操作就相当于,把demo里存的东西赋值给x和y,这样x 和y 都指向了demo函数所在的引用,在这之后我们可以
    用x() 或者 y() 来调用我们自己创建的demo() ,调用的实际上根本就是一个函数,x、y和demo三个变量名存了同一个函数的引用。

    同时我们发现,一个函数,如果函数名后紧跟一对括号,相当于现在我就要调用这个函数,如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用。

     

    2 外函数把临时变量绑定给内函数:

    按照我们正常的认知,一个函数结束的时候,会把自己的临时变量都释放还给内存,之后变量都不存在了。一般情况下,确实是这样的。但是闭包是一个特别的情况。外部函数发现,自己
    的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量送给内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外
    函数的临时变量。

    3 闭包中内函数修改外函数局部变量:

    在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:1 global 声明全局变量 2 全局变量是可变类型数据的时候可以修改
    
    在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候:
    
        1 在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
    
        2 在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。
    
    
    #修改闭包变量的实例
    # outer是外部函数 a和b都是外函数的临时变量
    def outer( a ):
        b = 10  # a和b都是闭包变量
        c = [a] #这里对应修改闭包变量的方法2
        # inner是内函数
        def inner():
            #内函数中想修改闭包变量
            # 方法1 nonlocal关键字声明
            nonlocal  b
            b+=1
            # 方法二,把闭包变量修改成可变数据类型 比如列表
            c[0] += 1
            print(c[0])
            print(b)
        # 外函数的返回值是内函数的引用
        return inner
    
    if __name__ == '__main__':
    
        demo = outer(5)
        demo() # 6  11

    还有一点需要注意:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量

    #coding:utf8
    def outer(x):
        def inner(y):
            nonlocal x
            x+=y
            return x
        return inner
    
    
    a = outer(10)
    print(a(1)) //11
    print(a(3)) //14

    两次分别打印出11和14,由此可见,每次调用inner的时候,使用的闭包变量x实际上是同一个。

    闭包有啥用

    3.1装饰器!!!装饰器是做什么的??其中一个应用就是,我们工作中写了一个登录功能,我们想统计这个功能执行花了多长时间,我们可以用装饰器装饰这个登录模块,装饰器帮我
    们完成登录函数执行之前和之后取时间。   
    3.2面向对象!!!经历了上面的分析,我们发现外函数的临时变量送给了内函数。大家回想一下类对象的情况,对象有好多类似的属性和方法,所以我们创建类,用类创建出
    来的对象都具有相同的属性方法。闭包也是实现面向对象的方法之一。在python当中虽然我们不这样用,在其他编程语言入比如avaScript中,经常用闭包来实现面向对象编程   
    3.3实现单利模式!! 其实这也是装饰器的应用。单利模式毕竟比较高大,,需要有一定项目经验才能理解单利模式到底是干啥用的,我们就不探讨了。

     4. 装饰器

    参考: http://python.jobbole.com/85056/

      http://www.cnblogs.com/linhaifeng/articles/7532497.html#_label5

      http://python.jobbole.com/82344/

    4.1 什么是装饰器

    器即函数
    
    装饰即修饰,意指为其他函数添加新功能
    
    装饰器定义:本质就是函数,功能是为其他函数添加新功能

    4.2 装饰器遵循原则

    1.不修改被装饰函数的源代码(开放封闭原则)
    
    2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式

    4.3 实现装饰器知识储备

    装饰器=高阶函数+函数嵌套+闭包

    4.4 高阶函数

    高阶函数定义:
    1.函数接收的参数是一个函数名

    2.函数的返回值是一个函数名

    3.满足上述条件任意一个,都可称之为高阶函数

    def foo():
        print('我的函数名作为参数传给高阶函数')
    def gao_jie1(func):
        print('我就是高阶函数1,我接收的参数名是%s' %func)
        func()
    
    def gao_jie2(func):
        print('我就是高阶函数2,我的返回值是%s' %func)
        return func
    
    gao_jie1(foo)
    gao_jie2(foo)
    
    高阶函数示范
    高阶函数示范
    #高阶函数应用1:把函数当做参数传给高阶函数
    import time
    def foo():
        print('from the foo')
    
    def timmer(func):
        start_time=time.time()
        func()
        stop_time=time.time()
        print('函数%s 运行时间是%s' %(func,stop_time-start_time))
    timmer(foo)
    #总结:我们确实为函数foo增加了foo运行时间的功能,但是foo原来的执行方式是foo(),现在我们需要调用高阶函数timmer(foo),改变了函数的调用方式
    
    把函数当做参数传给高阶函数
    把函数作为参数传递给高阶函数
    #高阶函数应用2:把函数名当做参数传给高阶函数,高阶函数直接返回函数名
    import time
    def foo():
        print('from the foo')
    
    def timmer(func):
        start_time=time.time()
        return func
        stop_time=time.time()
        print('函数%s 运行时间是%s' %(func,stop_time-start_time))
    foo=timmer(foo)
    foo()
    #总结:我们确实没有改变foo的调用方式,但是我们也没有为foo增加任何新功能
    
    函数返回值是函数名
    函数返回值是函数名

    高阶函数总结
    1.函数接收的参数是一个函数名
      作用:在不修改函数源代码的前提下,为函数添加新功能,
      不足:会改变函数的调用方式
    2.函数的返回值是一个函数名
      作用:不修改函数的调用方式
      不足:不能添加新功能

    4.5 函数嵌套

    def father(name):
        print('from father %s' %name)
        def son():
            print('from son')
            def grandson():
                print('from grandson')
            grandson()
      print locals() ##打印当前层的局部变量,同层的函数也属于 son() father(
    '林海峰')


    from father 林海峰
    {'name': 'xe6x9ex97xe6xb5xb7xe5xb3xb0', 'son': <function son at 0x201e0c8>}   #print locals()结果


    from son
    from grandson



    4.6 无参装饰器

    无参装饰器=高级函数+函数嵌套

    基本框架

    #这就是一个实现一个装饰器最基本的架子
    def timer(func):
         def wrapper():  #该内函数引用了外函数的变量fun,所以说装饰器是闭包的一种应用.
             func()
         return wrapper #外函数返回内函数的引用,同理,内函数里面可以再返回另一个函数的引用或者返回值。所以装饰器实际最后执行的是最内层函数。最内层函数给原函数加上新功能.

    加上参数

    def timer(func):
         def wrapper(*args,**kwargs):
             func(*args,**kwargs)
         return wrapper

    加上功能

    import time
    def timer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            func(*args,**kwargs)
            stop_time=time.time()
            print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
        return wrapper

    加上返回值,如果要想得到被装饰函数的返回值

    import time
    def timer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs) # 被装饰函数返回值
            stop_time=time.time()
            print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
            return res  #返回被装饰函数的返回值
        return wrapper

    使用装饰器

    def cal(array):
        res=0
        for i in array:
            res+=i
        return res
    
    cal=timer(cal)
    cal(range(10))

    语法糖@

    @timer  #@timer就等同于cal=timer(cal)
    def cal(array):
        res=0
        for i in array:
            res+=i
        return res
    
    cal(range(10))

    4.7 装饰器应用示例

    装饰器为函数加上验证功能

    user_list=[
        {'name':'alex','passwd':'123'},
        {'name':'linhaifeng','passwd':'123'},
        {'name':'wupeiqi','passwd':'123'},
        {'name':'yuanhao','passwd':'123'},
    ]
    
    current_user={'username':None,'login':False}
    
    def auth_deco(func):
        def wrapper(*args,**kwargs):
            if current_user['username'] and current_user['login']:
                res=func(*args,**kwargs)
                return res
            username=input('用户名: ').strip()
            passwd=input('密码: ').strip()
    
            for index,user_dic in enumerate(user_list):
                if username == user_dic['name'] and passwd == user_dic['passwd']:
                    current_user['username']=username
    
                    current_user['login']=True
                    res=func(*args,**kwargs)
                    return res
                    break
            else:
                print('用户名或者密码错误,重新登录')
    
        return wrapper
    
    @auth_deco
    def index():
        print('欢迎来到主页面')
    
    @auth_deco
    def home():
        print('这里是你家')
    
    def shopping_car():
        print('查看购物车啊亲')
    
    def order():
        print('查看订单啊亲')
    
    print(user_list)
    # index()
    print(user_list)
    home()
    
    无参装饰器
    无参装饰器

    装饰器模拟session

    user_list=[
        {'name':'alex','passwd':'123'},
        {'name':'linhaifeng','passwd':'123'},
        {'name':'wupeiqi','passwd':'123'},
        {'name':'yuanhao','passwd':'123'},
    ]
    
    current_user={'username':None,'login':False} #保存用户是否登录
    def auth(auth_type='file'):
        def auth_deco(func):
            def wrapper(*args,**kwargs):
                if auth_type == 'file':
                    if current_user['username'] and current_user['login']: #登录了直接执行
                        res=func(*args,**kwargs)
                        return res
                    username=input('用户名: ').strip()
                    passwd=input('密码: ').strip()
    
                    for index,user_dic in enumerate(user_list):#没登录验证用户密码
                        if username == user_dic['name'] and passwd == user_dic['passwd']:
                            current_user['username']=username #更改登录状态
                            current_user['login']=True
                            res=func(*args,**kwargs)
                            return res
                    else:
                        print('用户名或者密码错误,重新登录')
                elif auth_type == 'ldap':
                    print('巴拉巴拉小魔仙')
                    res=func(*args,**kwargs)
                    return res
            return wrapper
        return auth_deco
    
    
    #auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file')
    #就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数
    @auth(auth_type='ldap')
    def index():
        print('欢迎来到主页面')
    
    @auth(auth_type='ldap')
    def home():
        print('这里是你家')
    
    def shopping_car():
        print('查看购物车啊亲')
    
    def order():
        print('查看订单啊亲')
    
    # print(user_list)
    index()
    # print(user_list)
    home()
    
    带参装饰器
    带参装饰器

    4.8 超时装饰器

    import sys,threading,time
    
    
    class KThread(threading.Thread):
    
        """A subclass of threading.Thread, with a kill()
    
        method.
    
    
    
        Come from:
    
        Kill a thread in Python:
    
        http://mail.python.org/pipermail/python-list/2004-May/260937.html
    
        """
    
        def __init__(self, *args, **kwargs):
    
            threading.Thread.__init__(self, *args, **kwargs)
    
            self.killed = False
    
    
    
        def start(self):
    
            """Start the thread."""
    
            self.__run_backup = self.run
    
            self.run = self.__run      # Force the Thread to install our trace.
    
            threading.Thread.start(self)
    
    
    
        def __run(self):
    
            """Hacked run function, which installs the
    
            trace."""
    
            sys.settrace(self.globaltrace)
    
            self.__run_backup()
    
            self.run = self.__run_backup
    
    
    
        def globaltrace(self, frame, why, arg):
    
            if why == 'call':
    
              return self.localtrace
    
            else:
    
              return None
    
    
    
        def localtrace(self, frame, why, arg):
    
            if self.killed:
    
              if why == 'line':
    
                raise SystemExit()
    
            return self.localtrace
    
    
    
        def kill(self):
    
            self.killed = True
    
    
    
    class Timeout(Exception):
    
        """function run timeout"""
    
    
    
    def timeout(seconds):
    
        """超时装饰器,指定超时时间
    
        若被装饰的方法在指定的时间内未返回,则抛出Timeout异常"""
    
        def timeout_decorator(func):
    
            """真正的装饰器"""
    
    
    
            def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):
    
                result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))
    
    
    
            def _(*args, **kwargs):
    
                result = []
    
                new_kwargs = { # create new args for _new_func, because we want to get the func return val to result list
    
                    'oldfunc': func,
    
                    'result': result,
    
                    'oldfunc_args': args,
    
                    'oldfunc_kwargs': kwargs
    
                }
    
                thd = KThread(target=_new_func, args=(), kwargs=new_kwargs)
    
                thd.start()
    
                thd.join(seconds)
    
                alive = thd.isAlive()
    
                thd.kill() # kill the child thread
    
                if alive:
    
                    raise Timeout(u'function run too long, timeout %d seconds.' % seconds)
    
                else:
    
                    return result[0]
    
            _.__name__ = func.__name__
    
            _.__doc__ = func.__doc__
    
            return _
    
        return timeout_decorator
    
    
    @timeout(5)
    
    def method_timeout(seconds, text):
    
        print('start', seconds, text)
    
        time.sleep(seconds)
    
        print('finish', seconds, text)
    
        return seconds
    
    
    method_timeout(6,'asdfasdfasdfas')
    View Code

    4.9 类装饰器

    参考: https://blog.csdn.net/hesi9555/article/details/70224911

    def decorate(cls):
        print('类的装饰器开始运行啦------>')
        return cls
    
    @decorate #无参:People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People('egon',18,3333.3)
    
    类的装饰器:无参
    View Code
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            return cls
        return decorate
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People('egon',18,3333.3)
    
    类的装饰器:有参
    View Code
    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            for name,expected_type in kwargs.items():
                setattr(cls,name,Typed(name,expected_type))
            return cls
        return decorate
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    print(People.__dict__)
    p1=People('egon',18,3333.3)
    
    刀光剑影
    View Code
    def typed(**kwargs):
        def deco(cls):
            for key,value in kwargs.items():
                setattr(cls,key,value)
    
            return cls
        return deco
    
    
    @typed(name="che")
    class Foo(object):
        pass
    
    print Foo.name
    print Foo.__dict__
    给类加上类属性
    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            for name,expected_type in kwargs.items():
                setattr(cls,name,Typed(name,expected_type))
            return cls
        return decorate
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    print(People.__dict__)
    p1=People('egon',18,3333.3)
    给类加上属性,并且再加之前检查类型(描述符号+装饰器应用)

    ====================================================================================================

    装饰器就是闭包函数的一种应用场景

    为何使用装饰器:   开放封闭原则:对修改封闭,对扩展开放

    什么是装饰器:

    装饰其他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
    强调装饰器的原则:
    1 不修改被装饰对象的源代码
     2 不修改被装饰对象的调用方式
    装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

    装饰器语法:

    被装饰函数的正上方,单独一行
            @deco1
            @deco2
            @deco3
            def foo():
                pass
    
            foo=deco1(deco2(deco3(foo)))

    装饰器例子:

    被装饰的函数带参数(未使用语法糖调用):
    
    import time
    
    def deco(fun):  #fun接收函数名
        def warp(a,b):  #a,b啊接收被装饰函数的参数
            start = time.time()
            fun(a,b)
            end = time.time()
            alltime = end - start
            print 'run myfun() cost %0.2f s' %alltime
        return warp
    
    
    def myfun(a,b):
        print 'myfun() called and a + b = %d!' %(a+b)
        time.sleep(1.5)
    
    
    print "myfun is :", myfun.__name__
    myfun = deco(myfun)   #将myfun重新赋值,”@deco”的本质就是”myfunc = deco(myfunc)”
    print "myfun is:",myfun.__name__
    print
    myfun(1,2)
    
    输出如下:
    
    myfun is : myfun
    myfun is: warp
    
    myfun() called and a + b = 3!
    run myfun() cost 1.50 s
    
    使用语法糖:
    @deco  #@deco”的本质就是”myfunc = deco(myfunc)
    def myfun(a,b):
        print 'myfun() called and a + b = %d!' %(a+b)
        time.sleep(1.5)
    myfun(1,2)  #这里返回的是deco中的warp函数,相当于myfun的代码没变,现在在原有基础上添加了
    时间测试的功能。
    
    注意:对于被装饰函数需要支持参数的情况,我们只要使装饰器的内嵌函数支持同样的签名即可。
    带参数的装饰器
    
    import time
    
    def deco(arg=True): #arg 为装饰器参数
        if arg:
            def _deco(fun):# fun  接收被装饰的函数名
                def warp(*args,**kwargs):  #*args,**kwargs 接收被装饰函数的参数
            start = time.time() 
            fun(
    *args,**kwargs)
            end
    = time.time()
            alltime
    = end - start
            
    print 'run myfun() cost %0.2f s' %alltime
          
    return warp
      
    else:
          
    def _deco(fun):
            
    return fun
      
    return _deco

    @deco(True)
    #相当于 myfun = deco(True)(myfun)

    def myfun(a,b):
      
    print 'myfun() called and a + b = %d!' %(a+b)
      time.sleep(
    1.5)


    myfun(
    1,2) 注意:如果装饰器本身需要支持参数,那么装饰器就需要多一层的内嵌函数。这时候, myfun = deco(True)(myfun)


    装饰器是可以叠加使用的,那么这是就涉及到装饰器调用顺序了。对于Python中的”@”语法糖, 装饰器的调用顺序与使用 @ 语法糖声明的顺序相反。

    5. 递归

    递归调用的定义

    #递归调用是函数嵌套调用的一种特殊形式,函数在调用时,直接或间接调用了自身,就是递归调用

    递归分为两个阶段:递推,回溯

    #图解。。。
    # salary(5)=salary(4)+300
    # salary(4)=salary(3)+300
    # salary(3)=salary(2)+300
    # salary(2)=salary(1)+300
    # salary(1)=100
    #
    # salary(n)=salary(n-1)+300     n>1
    # salary(1) =100                n=1
    
    def salary(n):
        if n == 1:
            return 100
        return salary(n-1)+300
    
    print(salary(5))

    python中的递归效率低且没有尾递归优化

    #python中的递归
    python中的递归效率低,需要在进入下一次递归时保留当前的状态,在其他语言中可以有解决方法:尾递归优化,即在函数的最后一步(而非最后一行)调用自己,
    尾递归优化:http://egon09.blog.51cto.com/9161406/1842475 但是python又没有尾递归,且对递归层级做了限制 #总结递归的使用: 1. 必须有一个明确的结束条件 2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少 3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,
    栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

    6. 匿名函数 

    什么是匿名函数?

    匿名就是没有名字
    def func(x,y,z=1):
        return x+y+z
    
    匿名
    lambda x,y,z=1:x+y+z #与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,除非让其有名字
    func=lambda x,y,z=1:x+y+z 
    func(1,2,3)
    #让其有名字就没有意义

    匿名函数lambda. 关键字lambda表示匿名函数,冒号前面的表示函数参数。不用写return,返回值就是该表达式的结果。

    有名字的函数与匿名函数的对比

    #有名函数与匿名函数的对比
    有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
    
    匿名函数:一次性使用,随时随时定义
    
    应用:max,min,sorted,map,reduce,filter

    7. 内置函数

    #enumerate()
    >>> for i in enumerate([0,1,2,3]):
    ...     print i
    ...
    ...
    (0, 0)
    (1, 1)
    (2, 2)
    (3, 3)
    
    #eval
    1. 提取出字符串中的数据结构
    >>> dict_str = "{'a':1,'b':2}"
    >>> eval(dict_str)
    {'a': 1, 'b': 2}
    
    2.把字符串表达式进行运算
    >>> eval("1+2/3*4")
    1
    
    
    #zip,像拉链一样将2个序列一一对应组成元组
    >>> list(zip(('a','b','c'),(1,2,3)))
    [('a', 1), ('b', 2), ('c', 3)]
    
    >>> p = {'name':'che','age':18,'gender':'male'}
    >>> list(zip(p.keys(),p.values()))
    [('gender', 'male'), ('age', 18), ('name', 'che')]
    
    >>> list(zip("hellp",(1,2,3)))
    [('h', 1), ('e', 2), ('l', 3)]
    
    
    #max()  min(),同类型比较,是对序列进行for循环来比较
    >>> max((5,5),(4,5))
    (5, 5)
    
    例子: 找出字典中年龄最大的人,zip结合使用
    >>> d
    {'age4': 100, 'age3': 100, 'age2': 20, 'age1': 18}
    >>> max(zip(d.values(),d.keys()))
    (100, 'age4')
    
    例子:找出一个一个班级年龄最大的学生信息
    d = [
    {'name':'che','age':12},
    {'name':'che1','age':20},
    {'name':'che2','age':100},
    ]
    
    print(max(d,key=lambda dic:dic['age']))
    
    
    #sorted 排序,同类型排序,也是for循环比较
    d = [
    {'name':'che','age':12},
    {'name':'che1','age':20},
    {'name':'che2','age':100},
    ]
    
    print sorted(d,key=lambda dic:dic['age'])
  • 相关阅读:
    poj 2528 Mayor's posters (线段树+离散化)
    poj 1201 Intervals (差分约束)
    hdu 4109 Instrction Arrangement (差分约束)
    poj 1195 Mobile phones (二维 树状数组)
    poj 2983 Is the Information Reliable? (差分约束)
    树状数组 讲解
    poj 2828 Buy Tickets (线段树)
    hdu 1166 敌兵布阵 (树状数组)
    Ubuntu网络配置
    Button控制窗体变量(开关控制灯的状态)
  • 原文地址:https://www.cnblogs.com/yitianyouyitian/p/8624160.html
Copyright © 2011-2022 走看看