python进阶-- 01 函数式编程

1.概念

函数:function,是编程里面的方法

函数式:functional,是一种编程范式

2.特点

把计算视为函数,而非指令

纯函数式编程:不需要变量,没有副作用,测试简单

支持高阶函数,代码简洁

3.python支持的函数式编程

不是纯函数式编程:允许有变量

支持高阶函数:函数可以作为变量传入

支持闭包:有了闭包就能返回函数

有限度的支持匿名函数

4.高阶函数

4.1概念

能接收函数作为变量的函数,称为高阶函数

4.2原理

python中函数名其实是一个变量

>>> abs(-10)

10

>>> abs=len

>>> abs(-10)

 

Traceback (most recent call last):

  File "<pyshell#2>", line 1, in <module>

    abs(-10)

TypeError: object of type 'int' has no len()

>>> abs([1,2,3])

3

4.3举例

>>> def add_ab(x,y,f):

         return f(x)+f(y)

 

>>> add_ab(-5,9,abs)

14

5.返回函数

>>> def f():

    print 'call f()...'

    # 定义函数g:

    def g():

        print 'call g()...'

    # 返回函数g:

    return g

 

>>> x=f()

call f()...

>>> x

<function g at 0x0000000002F05A58>

>>> x()

call g()...

6.闭包

6.1概念

内层函数引用了外层函数的变量(外层函数的参数也算变量),然后返回内层函数的情况,称为闭包(Closure)

6.2特点

返回的函数还引用了外层函数的局部变量;

另外,闭包在定义时,里面的代码未运行,只会在真正被调用时去运行代码所以运行时取的变量的值为运行时刻(非定义时刻)该变量的值!!!

所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。

重要:默认参数值的获取是在定义时刻!!!

# 希望一次返回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()

9

7.匿名函数

7.1举例

lambda x: x * x

等效于

def f(x):
    return x * x

7.2特点:

关键字lambda表示匿名函数,冒号前面的一个(多个)变量表示函数参数

只能有一个表达式,不写return返回值就是该表达式的结果

使用匿名函数,可以不必定义函数名

返回函数的时候,也可以返回匿名函数

8.装饰器decorator

8.1背景

已经定义了一个函数,想在运行时动态增加函数功能,又不必去改变函数本身的代码

8.1.1改变源代码

>>> def f1(x):

...    print 'call f1()'

...    return x*2

...

>>> def f2(x):

...    print 'call f2()'

...    return x*x

...

>>> def f3(x):

...    print 'call f3()'

...    return x*x*x

8.1.2使用高阶函数返回新函数

>>> def new_fn(f):

...    def fn(*args,**kw):

...             print 'call %s()' % f.__name__

...             return f(*args,**kw)

...    return fn

调用方法有两种:

 

8.1.3通过内置@自动生成新函数

 

打印日志:@log

检测性能:@performance

数据库事务:@transaction

URL路由:@post('/register')

8.1.4装饰器概念

Python的 decorator 本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数。

8.2无参数装饰器

8.2.1格式

# 定义无参装饰器

def deco_performance(f):

    # 定义装饰器中临时函数

    def tmp(*args,**kw):

           

        # 执行被装饰函数,可使用有参装饰器所带参数

        # xxx

           

        # 返回被装饰函数执行结果

        return result

        #return f(*args,**kw)

    # 返回装饰器中临时函数名

    return tmp

8.2.2举例

# 请编写一个@performance,它可以打印出函数调用的时间

def performance(f):

    # 要让 @performance 自适应任何参数定义的函数,可以利用Python的

         # *args 和 **kw,保证任意个数的参数总是能正常调用

    def tmp(*args,**kw):

        print "before call--> %s" % (time.time())

        # 此刻调用被装饰的函数

        result = f(*args,**kw)

        print "after call--> %s" % (time.time())

        # 只需要返回函数结果,不一定必须要return f(*args,**kw)

        return result

         # 只是返回函数名

    return tmp

   

# @performance使用

@performance

def factorial(n):

    return reduce(lambda x,y: x*y, range(1, n+1))

 

# 使用@performance装饰过的函数的调用

print factorial(10)

8.3有参数装饰器

8.3.1格式

# 定义有参装饰器

def performance(unit):

    # 定义无参装饰器

    def deco_performance(f):

        # 定义装饰器中临时函数

        def tmp(*args,**kw):

           

            # 执行被装饰函数,可使用有参装饰器所带参数

            # xxx

           

            # 返回被装饰函数执行结果

            return result

        # 返回装饰器中临时函数名

        return tmp

    # 返回无参装饰器名

    return deco_performance

8.3.2举例

# 定义有参装饰器

def performance(unit):

    # 定义无参装饰器

    def deco_performance(f):

        # 定义装饰器中临时函数

        def tmp(*args,**kw):

           

            # 执行被装饰函数,可使用有参装饰器所带参数

            print "before call time --> %f" % (time.time() if unit=='s' else time.time()*1000)

            result=f(*args,**kw)

            print "after call time --> %f" % (time.time() if unit=='s' else time.time()*1000)

            print "call %s()" % (f.__name__)

           

            # 返回被装饰函数执行结果

            return result

        # 返回装饰器中临时函数名

        return tmp

    # 返回无参装饰器名

    return deco_performance

 

# 使用有(无)参装饰器

@performance('s')

def factorial(n):

    return reduce(lambda x,y: x*y, range(1, n+1))

 

# 调用使用有(无)参装饰器的函数

print factorial(10)

8.4装饰器完善

8.4.1问题

经过@decorator“改造”后的函数,返回的新函数函数名已经被改变,不是'f2',而是@log内部定义的'wrapper'。这对于那些依赖函数名的代码就会失效。decorator还改变了函数的__doc__等其它属性。如果要让调用者看不出一个函数经过了@decorator的“改造”,就需要把原函数的一些属性复制到新函数中。

8.4.2完善方法

1.手动在装饰器的临时函数中手动添加属性

def log(f):

    def wrapper(*args, **kw):

        print 'call...'

        return f(*args, **kw)

    wrapper.__name__ = f.__name__

    wrapper.__doc__ = f.__doc__

    return wrapper

2.使用python内置的functools装饰器自动完成复制动作

import functools

def log(f):

    @functools.wraps(f)

    def wrapper(*args, **kw):

        print 'call...'

        return f(*args, **kw)

    return wrapper

8.4.3无法避免的问题

最后需要指出,由于我们把原函数签名改成了(*args, **kw),因此,无法获得原函数的原始参数信息

9偏函数

9.1概念

已经定义了一个函数,函数的参数个数太多,想在运行时动态减少函数必须输入的参数,从而在调用时更简单,实现与默认参数相同的功能,但不改变原本函数

9.2举例

>>> int('12345')

12345

>>> int('12345',8)

5349

>>> int2 = functools.partial(int,base=2)

>>> int2('1111')

15

9.3注意

创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数,如果传入xx=xx类型的参数,代表传入kw;如果传入xx类型的参数,会作为args自动加入到参数元组中,需注意是否会影响原本函数应有结果。

>>> max(3,4,5)

5

>>> max2 = functools.partial(max,10)

>>> max2(3,4,5)

10

原文地址:https://www.cnblogs.com/yc913344706/p/7749347.html