Python菜鸟晋级07----闭包与修饰器

本文介绍了Python中的闭包和修饰器概念,作为函数式编程的重要组成部分,它们增强了代码的可重复使用性。闭包基于函数对象的作用域,而@修饰符(装饰器)自Python 2.4开始引入,用于修改函数行为。文中通过实例展示了装饰器的应用,包括无参数和带参数的装饰器,以及在类上的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

       闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
       不同的语言实现闭包的方式不同。Python以函数对象为基础,为闭包这一语法结构提供支持的 (我们在特殊方法与多范式中,已经多次看到Python使用对象来实现一些特殊的语法)。Python一切皆对象,函数这一语法结构也是一个对象。在函数对象中,我们像使用一个普通对象一样使用函数对象,比如更改函数对象的名字,或者将函数对象作为参数进行传递。

函数对象的作用域

          和其他对象一样,函数对象也有其存活的范围,也就是函数对象的作用域。函数对象是使用def语句定义的,函数对象的作用域与def所在的层级相同。比如下面代码,我们在line_conf函数的隶属范围内定义的函数line,就只能在line_conf的隶属范围内调用。

def line_conf():
    def line(x):
        return 2*x+1
    print(line(5))   # 顺利执行

line_conf()
print(line(5))       # 报错!  

          同样,如果使用lambda定义函数,那么函数对象的作用域与lambda所在的层级相同。

闭包

           函数是一个对象,所以可以作为某个函数的返回结果。在Python中,所谓的闭包是一个包含有环境变量取值的函数对象。

def line_conf(a, b):
    def line(x):
        return ax + b
    return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))
          这个例子中,函数line与环境变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
          如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了 泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。

@修饰符(装饰器)

       '@'符号用作函数修饰符是python2.4新增加的功能,修饰符必须出现在函数定义前一行,不允许和函数定义在同一行。也就是说@A def f(): 是非法的。可以在模块或类定义层内对函数进行修饰,在Python2.6版本以后也可用于修饰一个类。一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。

@dec2  
@dec1  
def func(arg1, arg2, ...):  
    pass  
等价于

def func(arg1, arg2, ...):  
    pass  
func = dec2(dec1(func))  

实用小例子

def makebold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"                     #注意其中的fn要带括号
    return wrapped

def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

print (hello()) 
输出结果
<b><i>hello world</i></b>

带参数的装饰器

def makebold(pre=" "):
    def printMakeBold(fn):
        def wrapped():
            print(pre)
            return "<b>"+fn()+"</b>"
        return wrapped
    return printMakeBold
    
@makebold("测试")
def printfn():
    return "HelloWorld"

print(printfn())
输出结果
测试
<b>HelloWorld</b>

装饰器用于类

def decorator(aClass):
    class newClass:
        def __init__(self, age):
            self.total_display   = 0
            self.wrapped         = aClass(age)
        def display(self):
            self.total_display += 1
            print("total display", self.total_display)
    return newClass

@decorator
class Bird:
    def __init__(self, age):
        self.age = age
    def display(self):
        print("My age is",self.age)

eagleLord = Bird(5)
for i in range(3):
    eagleLord.display()
输出结果:
total display 1
total display 2
total display 3


在decorator中,我们返回了一个新类newClass。在新类中,我们记录了原来类生成的对象(self.wrapped),并附加了新的属性total_display,用于记录调用display的次数。我们也同时更改了display方法。


通过修改,我们的Bird类可以显示调用display的次数了。




上一讲: Python菜鸟晋级06----特殊属性整理

下一讲:Python菜鸟晋级08----str.format()方法





欢迎收听我的微信公众号


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值