函数式编程
在 Python 中一切皆对象,一切函数都是一等对象(first-class object)。什么是一等对象?它应该满足下述条件:
- 在运行时创建
- 能赋值给变量或数据结构中的元素
- 能作为参数传给函数
- 能作为函数的返回结果
举个例子:
# 我在控制台上定义这个函数,可以独立执行
>>> def factorial(n):
... '''returns n!'''
... return 1 if n < 2 else n * factorial(n-1)
...
>>> factorial(5)
120
# __doc__是一等对象factorial众多属性中的一个
>>> factorial.__doc__
'returns n!'
# factorial 是函数(function)类的一个实例,也就是一个对象
>>> type(factorial)
<class 'function'>
函数本身就是对象!
Python 函数是真正的对象的同时,任何 Python 对象都可以表现的像函数。
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用,让我们分别用面向对象和函数式编程的方式实现一个经典的模式(策略模式),来体会个中优劣。
《设计模式:可复用面向对象软件的基础》这样描述策略模式:
定义一系列算法,把它们逐个封装起来,并且使它们可以相互替换,本模式使得算法可以独立于使用它的客户而变化。
举例来说,比如我们要去办公室上班,开车去或者是坐公交车去都是可选的出行方式,不同的人可以选择不同的出行方式,比如张三开车,李四坐公交。
如果用面向对象的写法其 UML 类图大概如下:
代码实现的话,大概如下:
from abc import ABC, abstractmethod
# 要去办公室的人
class Person:
def __init__(self,name, transportation = None):
self.name = name
self.transportation = transportation
def go_to_office(self):
print(self.transportation.go_to_office(self))
# 抽象基类
class Transportation(ABC):
@abstractmethod
def go_to_office(self):
""" return how to go to office """
# 策略1 开车
class ByCar(Transportation):
def go_to_office(self):
return (self.name + " 开车")
# 策略2 坐公交
class ByBus(Transportation):
def go_to_office(self):
return (self.name + " 坐公交")
# 测试输出
zhang_3 = Person('张三',ByCar)
zhang_3.go_to_office()
li_4 = Person('李四',ByBus)
li_4.go_to_office()
输出:
张三 开车
李四 坐公交
那么如果用函数式实现,该怎么写呢?
import types
# 要去办公室的人
class Person:
def __init__(self,name, transportation = None):
self.name = name
# 让模块方法在调用的时候自动传入被调用对象作为self参数
self.go_to_office = types.MethodType(transportation, self)
def go_to_office(self):
pass
# 策略1 开车
def by_car(person):
print(person.name + " 开车")
# 策略2 坐公交
def by_bus(person):
print(person.name + " 坐公交")
# 测试输出
zhang_3 = Person('张三',by_car)
zhang_3.go_to_office()
li_4 = Person('李四',by_bus)
li_4.go_to_office()
输出:
张三 开车
李四 坐公交
相比之下,函数式的方法用了相对较少的代就实现了策略模式并且有以下特点:
- 只需要调用 self.go_to_office 来判断用何种方式出行
- 没有抽象类
- 各个策略都是函数
面向对象也好,面向过程也罢,都是编程思想里的瑰宝,它们联起手来让写代码这件事变的有哲学意味。它们之间的恩怨纠葛,孰是孰非,放在每个程序员的故事里都有自己的版本。我的观点,两手抓,两手都要硬。
新手误区
1.、函数只有再被调用后才会执行函数体里面对的内容
为什么提到这一点,因为在函数还未被调用时,把它当做空气对看到问题的核心是很有帮助的。看下面的例子,
def fun_out(a):
c = 1
def fun_in(b):
return a + b + c
c = 3
return fun_in
在外部函数内部,先忽略内部函数fun_in,只有当外部函数被调用并 return 内部函数fun_in时,变量c会在最近的局部变量c=3
2、执行函数需要在函数名后面加上括号()
不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不须等该函数执行完成
带括号(参数或者无参),调用的是函数的执行结果,须等该函数执行完成的结果
3、闭包不仅仅是函数+自由变量,还必须满足外部函数return内部函数
参考:
2. Python之美
3. python中的lambda函数用法