闭包:嵌套定义在非全局作用域里面的函数,能够记住它被定义时所处的封闭命名空间;
在一个函数内容定义一个函数,在内部的函数可以携带外部函数的信息;这样可以使得函数的组合运用更加的灵活;
我们看一个例子:
def line_conf(a, b):
# 在函数内部定义了一个函数
def line(x):
return a*x+b # 返回值是一个值
return line #返回值是一个函数
# 测试一下,我们发现这个line_conf可以包装出很多新的函数
line1 = line_conf(1,1) # 1*x+1
print(line1) #<function line_conf.<locals>.line at 0x000002A784E1F400>
print(line1(5)) # 6
line2 = line_conf(4,5) # 4*x+5
print(line2) # <function line_conf.<locals>.line at 0x000002A784E25400>
print(line2(5)) # 25
这便是Python函数的闭包特性,使得Python的函数功能非常强大!
我们再来看一个例子,进一步来说明Python 闭包的强大及其带来的Python另一个重要的特性:装饰器。
# 用面向对象来写一个简单的坐标处理类
class Coordinate(object):
def __init__(self, x, y):# 构造方法
self.x = x
self.y = y
def __repr__(self): # 打印方法,当print时,以一定的格式输出
return "x: " + str(self.x) + ", y: "+ str(self.y)
def add(a, b):
return Coordinate(a.x+b.x, a.y+b.y)
def sub(a, b):
return Coordinate(a.x-b.x, a.y-b.y)
# 测试一下代码
one = Coordinate(100,200)
two = Coordinate(300,200)
print(add(one, two)) # 400, 400
print(sub(one, two)) # -200, 0
这很好理解。
但是,随着新的需求产生,我们可能需要将原来的函数进一步的扩充和增强。
而在软件开发种,有一个很重要的思想:不改变或者尽可能少改变源码,而更多的通过扩充代码,来完成代码功能的增强。
比如上面的例子,来了新的需求:要求其坐标不能小于0,否则需要置0;
面对这样的问题怎么办?我们来看下解决方案:
# 用面向对象来写一个简单的坐标处理类
class Coordinate(object):
def __init__(self, x, y):# 构造方法
self.x = x
self.y = y
def __repr__(self): # 打印方法,当print时,以一定的格式输出
return "x: " + str(self.x) + ", y: "+ str(self.y)
# 新的需求:坐标不能小于0,否则需要置0
# !!!用一个闭包来扩充原函数的逻辑!!!
def wrapper(func):
def check(a, b):
# 在函数调用之前检查参数
if a.x < 0 or a.y < 0:
a = Coordinate(a.x if a.x > 0 else 0,
a.y if a.y > 0 else 0)
if b.x < 0 or b.y < 0:
b = Coordinate(b.x if b.x > 0 else 0,
b.y if b.y > 0 else 0)
ret = func(a,b)
# 在函数调用之后检查参数
if ret.x < 0 or ret.y < 0:
ret = Coordinate(ret.x if ret.x > 0 else 0,
ret.y if ret.y > 0 else 0)
return ret # 这样ret返回的结果一定不会出现负坐标
return check
@wrapper # 用装饰器来装饰一下,没有对原逻辑的修改
def add(a, b):
return Coordinate(a.x+b.x, a.y+b.y)
@wrapper # 用装饰器来装饰一下,没有对原逻辑的修改
def sub(a, b):
return Coordinate(a.x-b.x, a.y-b.y)
one = Coordinate(100,200)
two = Coordinate(300,200)
print(add(one, two)) # 400, 400
print(sub(one, two)) # 0, 0 # 这里的坐标置0
#写代码时,比较好的模式是:可以扩充,但是不要更改已经写好的逻辑!
注意:这个例子中原有的代码没有改变,而功能得以增强。这就是Python中的装饰器,
而也正是我们后面要提的面向对象设计模式中装饰器模式的一种Python实现。
在下一节中,我们可以看一下,在没有语言机制支持时,如何来实现这种设计模式。