一、函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

1、定义函数

def function(): # 无参数,无返回值,也不执行任何动作。
    pass        # pass一般用作占用符,保持语义完整。

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x
print(my_abs(6))

2、返回多个值

一是必选参数在前,默认参数在后,否则Python的解释器会报错
  二是当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
  有多个默认参数时,调用的时候,既可以按顺序提供默认参数。也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。需要注意的是:默认参数必须指向不变对象!

import math
# x, y, step都是位置参数,angle设置了默认值,默认值的表达式只能被执行一次。
def move(x, y, step, angle=0):  
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny
x, y = move(100, 100, 60, math.pi / 6)
print(x, y)

def calc(*numbers): # 可变参数,参数numbers接收到的是一个tuple
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum
print(calc(1, 2, 3, 4))

3、函数参数

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

# *是tuple,**是dict
# 关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
# kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。
def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra)

# 如果要限制关键字参数的名字,就可以用命名关键字参数
def person(name, age, *, city, job): 
    print(name, age, city, job)
person('Jack', 24, city='Beijing', job='Engineer')

4、本节demo的运行结果:

函数及函数式编程_Python

二、函数式编程

函数式编程(Functional Programming)其思想更接近数学计算。函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

import math
f = abs # 函数名也是变量
print(f(-10))

1、传入函数

函数的参数如果接收一个指向函数的变量,就变成了一个函数接收另一个函数作为参数,这种函数称为高阶函数。函数式编程就是指这种高度抽象的编程范式。

def f(x):
    return x*x
# Python内建了map()和reduce()函数。map()函数接收两个参数,一个是函数,
# 一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
r1 = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r1))

import operator
from functools import reduce
# 把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,
# reduce把结果继续和序列的下一个元素做累积计算,其效果就是reduce(f, [x1, x2, x3, x4]) = 
# f(f(f(x1, x2), x3), x4)。递归函数
r2 = reduce(operator.mul, range(1, 6))
print(r2)

2、filter

Python内建的函数filter()用于过滤序列,其接收一个函数和一个序列,并把传入的函数作用于每个元素,然后根据返回值是True还是False决定是保留还是丢弃该元素。

def is_odd(n):
    return n % 2 == 1
print(list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])))

3、sorted 排序算法

sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序。key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True。

print(sorted([36, 5, -12, 9, -21], key=abs, reverse=True))

4、函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。如下示例,调用lazy_sum时,返回的并不是求和结果,而是求和函数。这种结构称为闭包 Closure。
【Note】:
(1)一个函数可以返回一个计算结果,也可以返回一个函数。
(2)返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

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)
print(f())
# 在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum
# 的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,
# 这种称为“闭包(Closure)”的程序结构拥有极大的威力。

5、匿名函数

在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x^2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数。关键字lambda表示匿名函数,冒号前面的x表示函数参数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。

6、装饰器

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。装饰器最大的作用就是对于我们已经写好的程序,我们可以抽离出一些雷同的代码组建多个特定功能的装饰器,这样我们就可以针对不同的需求去使用特定的装饰器,这时因为源码去除了大量泛化的内容而使得源码具有更加清晰的逻辑。

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
@log('execute')
def now():
    print('2015-3-25') 
now()

7、偏函数

# functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),
# 返回一个新的函数,调用这个新函数会更简单。
int2 = functools.partial(int, base=2)
print(int2('1010101'))

# 实际上会把10作为*args的一部分自动加到左边。
max2 = functools.partial(max, 10)
print(max2(5, 6, 7))

8、本节demo的运算结果:

函数及函数式编程_默认参数_02