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'])
  • 相关阅读:
    hdu 5253 最小生成树
    hdu5248 序列变换
    bjfu1299 stl使用
    bjfu1277 简单递归
    bjfu1262 优先队列
    bjfu1287字符串输出的大水题
    bjfu1281
    bjfu1253 最大上升子序列和
    [转][Unity3D]引擎崩溃、异常、警告、BUG与提示总结及解决方法
    Unity3d 中 将远程 MySQL 数据库转换为本地 Sqlite
  • 原文地址:https://www.cnblogs.com/yitianyouyitian/p/8624160.html
Copyright © 2011-2022 走看看