函数式编程更多的需要思维方式的改变,这个需要在平时的编程中去逐渐的体会。python中函数式编程其实和迭代器(iterator),生成器(generator)列表生成式(list comprehension)以及装饰器(decorator)等交织在一起的。 对于函数式编程,这里参考这几个文档来学习。

  1. 函数式编程 | | 酷 壳 - CoolShell
  2. Functional Programming HOWTO — Python 3.6.1 documentation
  3. Functional Programming in Python
  4. 从python 函数式编程到装饰器一气呵成

1. 几种创建可调用的函数(拟函数)的方法

  1. 使用def定义具名函数(named function) && 使用lambda定义的匿名函数(anonymous function)
    • 两者原则上的差异(in-principle difference):是不是具有.qualname属性
  2. 定义了call()方法的类(class)的对象(instance) && 函数工厂(function factories)返回的闭包(closure)
    • 类(class)是“data with operations attached”;闭包(closure)是“operations with data attached”
    • 类(class)强调的是可变性(mutable)或者可重绑定状态(rebindable state);而闭包(closure)强调的则是不变性(immutability)和纯函数(pure function)
    • 当闭包中引用了变量时,结果可能会出乎意料,需要特别注意。
  3. 类中的方法
    • 通过@staticmethod装饰器定义的静态方法
    • 通过@classmethod装饰器定义的类方法
    • 抽象方法(没有具体实现,只有raise NotImplementedError)
    • 通过类的的dict定义的静态方法
    • 类中的accessors和operators
      • 通过@property装饰器可以将函数变成属性
      • python中每一个操作符都有对应的操作符函数(operator function),可以重新定义操作符函数重新定义对应的操作符
  4. 生成器函数(statefulness)
    • 带有yield语句的函数,这种函数返回的时一个生成器(generator)
    • 通过next()函数或者是循环(loop)方法去获得相应的值
    • 具有惰性求值(lazy evaluation)的特点

2. 函数式编程的几个技术

  1. map&reduce&filter
    • map
    • reduce的原型:functools.reduce(function, iterable[, _initializer_])
      • 如果有initializer,那么initializer作为function的第一个参数,iterable的第一个元素作为function的第二个参数。
      • 如果没有initializer,那么iterable的第一个和第二个参数分别作为function的第一个和第二个参数。
      • 对于理解参数的顺序有关的function有帮助
  2. pipeline
  3. recursing
  4. currying
    • currying是把接受多个参数的函数变成只接受部分参数的函数:f(x, y) = f(x)(y)返回fp = f(x)接受y参数,fp(y) == f(x, y)
    • python不支持currying,但是可以通过functools.partial间接的实现类似的功能
  5. HOF(higher order function)

In [3]:
## def具名函数和lambda匿名函数
def hello1(name):
    print("Hello,", name)
    
hello2 = lambda name: print("Hello,", name)

hello1("Longshan")
hello2("Longshan")

print(hello1.__qualname__)
print(hello2.__qualname__)


Hello, Longshan
Hello, Longshan
hello1
<lambda>

In [5]:
## callable class
class Adder(object):
    def __init__(self, n):
        self.n = n
    def __call__(self, m):
        return self.n + m
    
add5_i = Adder(5)
print(add5_i)
print(add5_i(10))

## closure

def make_adder(n):
    def adder(m):
        return m + n
    return adder

adder5_f = make_adder(5)
print(adder5_f)
print(adder5_f(11))
print(make_adder(5)(12))


<__main__.Adder object at 0x7f56903ad128>
15
<function make_adder.<locals>.adder at 0x7f56903a7840>
16
17

In [13]:
## variables in closure
adders = []
for n in range(5):
    adders.append(lambda m: m + n)
print(list([adder(10) for adder in adders]))
n = 10
print(list([adder(10) for adder in adders]))
## 因为这里是惰性求值,在创建adders的时候并没有计算n的值。n的值是在使用adders的时候计算的,在list comprehension中使用的时候n的值已经是4了,所以adders中加的都是4.

adders = []
for n in range(5):
    adders.append(lambda m, k=n: m + k)
print(list([adder(10) for adder in adders]))
n = 10
print(list([adder(10) for adder in adders]))
## 这里n变成了局部变量,每一次执行k=n的时候,k的值是确定的,所以可以得到期望的结果。同时试图去重写n的值也不起作用了。
## TODO 这里的解释还是有些牵强的,需要进一步深化理解


[14, 14, 14, 14, 14]
[20, 20, 20, 20, 20]
[10, 11, 12, 13, 14]
[10, 11, 12, 13, 14]

In [34]:
# staticmethod/classmethod/abstractmethod

import math

class Pizza(object):
    radius = 42
    
    def __init__(self, cheese, vegetables):
        self.cheese = cheese
        self.vegetables = vegetables
    
    @staticmethod
    def mix_ingredients(x, y):
        return x + y
    
    @classmethod
    def get_radius(cls):
        return cls.radius
    
    @staticmethod
    def get_area():
        raise NotImplementedError

    def cook(self):
        return self.mix_ingredients(sef.cheese, self.vegetables)

if __name__ == '__main__':
    print(Pizza.mix_ingredients(2, 3))
    print(Pizza(10, 12).get_radius)
    print(Pizza(10, 12).get_radius())


5
<bound method Pizza.get_radius of <class '__main__.Pizza'>>
42
# accessor:getter & setter class Car(object): def __init__(self): self._speed = 100 @property def speed(self): print("Speed is", self._speed) return self._speed @speed.setter def speed(self, value): print("Setting to", value) self._speed = value car = Car() x = car.speed car.speed = 80 x = car.speed

In [36]:
# operator
## TODO 为什么self是8?
class TalkativeInt(int):
    def __lshift__(self, other):
        print("Shift", self, "by", other)
        return int.__lshift__(self, other)

if __name__ == '__main__':
    t = TalkativeInt(8)
    print(t << 3)


Shift 8 by 3
64

In [1]:
from itertools import chain, count
thrice_to_inf = chain(count(), count(), count())
print(next(thrice_to_inf))


0

In [4]:
# The parameter order of reduce function
from functools import reduce
def even_filter(nums):
    return filter(lambda x: x % 2 == 0, nums)
def multiply_by_three(nums):
    return map(lambda x: x * 3, nums)
def convert_to_string(nums):
    return map(lambda x: 'The number: %s' %x, nums)

def pipeline_func(data, fns):
    return reduce(lambda a, x: x(a), fns, data)

list(pipeline_func(list(range(10)), [even_filter, multiply_by_three, convert_to_string]))


Out[4]:
['The number: 0',
 'The number: 6',
 'The number: 12',
 'The number: 18',
 'The number: 24']

In [6]:
import functools
def add(x, y):
    return x + y

add2 = functools.partial(add, 2)
assert add(2, 3) == add2(3)
print(add2(3))


5

In [ ]: