Python 学习总结(二):理解函数式编程,丰富开发

        回顾上一节,我们已经将python的基础知识了解掌握,现在可以开始进一步的学习了。函数,无论在Java、C还是其他语言,都不可避免,这也从侧面反映了函数的重要性。所以,也就产生了函数式编程这一概念,到底什么是函数式编程?下面让我们一步一步来揭开它:

     (1)函数是什么?

        说起函数,相信大家一定不会陌生,其实从很早就已经接触了,像数学课堂上计算数列的和:1+2+3+4+5+...+10,函数就是最基本的一种代码抽象的方式,以便更加快速的计算和处理。

     (2)调用函数和定义函数

        python内置了很多函数,确定参数就可以直接调用,方便使用,可以直接从Python的官方网站查看文档:http://docs.python.org/3/library/functions.html。在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号,然后,在缩进块中编写函数体,函数的返回值用return语句返回,加入参数检查,能够完善函数定义,数据类型检查可以用内置函数isinstance()实现。定义函数时,注意函数书写的格式,避免出错,尽快熟悉这种书写风格:

# 定义求整数和浮点数类型的绝对值的函数
# 加入了参数检查,传入错误参数时会抛出对应错误

def my_abs(x):
    if not isinstance(x, (int, float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

     (3)函数的参数

        定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。

# 初始函数-计算x2的值
def power(x):
    return x * x

# 位置参数-计算x3,x4,x5...的值(初始函数不可用)
def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

# 默认参数-计算x3,x4,x5...的值(初始函数可用)
def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

        定义默认参数要牢记一点:默认参数必须指向不变对象。在Python函数中,还可以定义可变参数,顾名思义,可变参数就是传入的参数个数是可变的。在参数前加上*号,即把list或tuple的元素变成可变参数传进去:

# 给定一组数字a,b,c...,请计算a2 + b2 + c2 +...


def calc_old(numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

def calc_new(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

nums = [1, 2, 3]
calc_old(nums[0],nums[1],nums[2])
calc_old([1,2,3])
calc_new(*nums)

        下面说下可变参数和关键字参数的区别:可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple;关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict,它的作用就是扩展函数,换种说法,就是在接收必须的参数外,可以接收其他的非必须的参数,当然,既然是非必须的,那就可以不填,如关键字参数kw(习惯性写法,也可以用其他参数名),注意书写格式:

# 关键字参数

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

>>> person('Ada',30)
>>> name:Ada age:30 other:{}

>>> person('Bob',25,sex='M', job='Engineer')
>>> name:Bob age:25 kw:{'sex':'M','job':'Engineer'}

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
>>> name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

        而命名关键字参数就是为了限制关键字参数的名字,书写格式为特殊分隔符 *,*号后面的参数被视为命名关键字参数:

# 只接收city和job作为关键字参数

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

>>> person('Jack', 24, city='Beijing', job='Engineer')
>>> Jack 24 Beijing Engineer

        在Python函数定义时,各种参数都可以组合使用,注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。如非必要,不要使用过多的参数组合,这大大降低了参数接口的理解性。

     (4)函数式编程

        函数式编程就是一种抽象程度很高的编程范式,函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!下面来通过几个高阶函数(把函数作为参数传入)来理解函数式编程,以及函数式编程中常见的名词。

# Python内建的函数map()
# map()函数接收两个参数,一个是函数,一个是Iterable
# map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回

def f(x):
   return x*x

>>> r = map(f,[1,2,3,4,5,6,7,8,9])
>>> list(r)
>>> [1,4,9,16,25,36,49,64,81]
 

        其他高阶函数的比如:reduce()、filter()、sorted(),不再一一举例了,自行查看。

        返回函数,一个函数可以返回一个计算结果,也可以返回一个函数,注意:返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

# 函数作为返回值
# 闭包

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum


>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
>>> <function lazy_sum.<locals>.sum at 0x101c6ed90>
>>> f()
>>> 25

        匿名函数,在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便,python对匿名函数支持有限,好处是不必担心函数名冲突,如:lambda x : x*x,即关键字lambda表示匿名函数,冒号前面的x表示函数参数,不用写return,返回值就是表达式的结果:

# 匿名函数
# 关键字lambda

>>> f = lambda x: x*x
>>> f(5)
>>> 25

# 匿名函数作为返回值
def fa(x,y):
    return lambda: x*x+y*y

        装饰器,在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便:

# 装饰器decorator:在代码运行期间动态增加功能的方式
# decorator就是一个返回函数的高阶函数

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

@log
def now():
    print('2018-01-22')


>>> now()
call now():
2018-01-22

        解释下实现过程:由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。wrapper()函数的参数定义是(*args,**kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

        偏函数,当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单:

# 偏函数
# functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2()
# int()函数把字符串转换为整数

import functools

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

>>> int2('1000000')
>>> 64
>>> int2('1000000', base=10)
>>> 1000000

        以上介绍的就是一些函数式编程中需要掌握的,不多,但需要反复练习和理解,当然理解是第一前提,懵懂状态下,练习再多也是徒劳。

文章来源:

Author:海岸线的曙光
link:https://my.oschina.net/u/3747963/blog/1611279