Python 装饰器 & 闭包

Python 装饰器 & 闭包


参考文章

理解装饰器和闭包

参考视频

明确python变量作用域

按下边的函数嵌套示例:

  1. 读变量和C语言是一样的,首先搜本层楼,搜不到就下楼
  2. 改【下层楼】变量,要加关键字:
  • global 用于函数或其他局部作用域中,声明全局变量
  • nonlocal 用于内部嵌套函数中,声明外部嵌套函数变量
    参考文章

建议,不论读或改,最好都声明变量的作用域。

msg = '我是一楼'
def secondFloor():
    # global msg 
    # msg += 'hello'  # 修改一楼msg
    msg = '我是二楼'
    def thirdFloor():
        # global msg # 读出一楼msg
        # nonlocal msg # 读出二楼msg
        print(msg)

闭包

在函数嵌套的前提下:

  1. 内部函数使用了外部函数的变量
  2. 并且外部函数返回了内部函数的引用(即内部函数的内存地址)

其中的内部函数称为闭包。

def secondFloor():
    msg = '我是二楼'
    def thirdFloor():
        print(msg)
    return thirdFloor  # 注意这里不加()

# 调用闭包
sf = secondFloor()
sf()

# ------------------------------------------------------------------
# 如果给secondFloor一个形参,也可满足闭包的第一个条件
def secondFloor(arg):
    def thirdFloor():
        print(arg)
    return thirdFloor  # 注意这里不加()

# 调用闭包
info = 'hello'
sf = secondFloor(info)
sf()

装饰器

装饰器的实现基于闭包。
闭包是指嵌套函数的内部函数。装饰器指嵌套函数的外部函数。

def secondFloor(func):
    # func就是内部函数所使用的外部函数变量
    def thirdFloor(arg):
        print('原函数开始')
        func(arg)
        print('原函数结束')
    return thirdFloor

@secondFloor
def origin(info):
    #print('原函数')
    print(info)

# 装饰器基本使用
# sf = secondFloor(origin)
# sf()

# 通过python语法糖,执行origin()会自动调用装饰器
info = 'hello'
origin(info)  

装饰器调用起来更灵活和更方便,我们不需要修改原函数代码,甚至不用移动原代码块的位置,就可对函数增加一些附加功能。

闭包和装饰器的区别

功能上:两种都是用来实现函数的扩展的。
结构上:闭包的前提是函数嵌套,装饰器的实现则基于闭包的。但是,闭包是指嵌套函数的内部函数。装饰器指嵌套函数的外部函数。

实际应用

1.用闭包实现一个指数函数

# 闭包函数,其中 exponent 称为自由变量
def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of 
    
square = nth_power(2) # 平方函数
cube = nth_power(3) # 立方函数

print(square(2))  # 计算 2 的平方
print(cube(2)) # 计算 2 的立方

当然指数函数也可用简单的方式实现:

def nth_power_rewrite(base, exponent):
    return base ** exponent

# 不使用闭包
res1 = nth_power_rewrite(base1, 2)
res2 = nth_power_rewrite(base2, 2)
res3 = nth_power_rewrite(base3, 2)

对比可发现,使用闭包,可以让程序变得更简洁易读

闭包将那些额外工作的代码放在外部函数,就可以减少多次调用导致的不必要开销,提高程序的运行效率。

进阶

闭包的__closure__属性

闭包比普通的函数多了一个 closure 属性,该属性记录着自由变量的地址。当闭包被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用。

def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of
square = nth_power(2)
#查看 __closure__ 的值
print(square.__closure__)
# 输出结果为:
# (<cell at 0x0000014454DFA948: int object at 0x00000000513CC6D0>,)

自定义装饰器

函数装饰器主要用于修饰一个函数

用于修改对象的装饰器

给被装饰的对象,添加一个name属性并且设置值为python

def warp(obj):
    obj.name = 'python'
    return obj
 
@warp        # => Bar = warp(Bar)
class Bar(object):
    def __init__(self):
        pass
 
print(Bar.name)     # => python
用于模拟对象的装饰器
函数装饰器

上面例子中的装饰器,是直接修改了传入对象;而装饰器最常用的方式却是模拟一个传入对象。即返回一个和原对象相似的对象(即调用接口完全一样的另一个对象),并且该模拟对象是包装了原对象在内的。

def outer(func):         # 函数装饰器
    def inner():
        print('hello inner')
        func()
    return inner
 
@outer      # foo = outer(foo)
def foo():
    print('hello foo')

print(foo.__name__)
foo()  # 其实质上是执行的inner()
类方法装饰器
def outer(obj):         # 类方法装饰器
    def inner(self):
        print('hello inner')
        obj(self)  
    return inner
 
class Zoo(object):
    def __init__(self):
        pass
 
    @outer        # => zoo = outer(zoo)
    def zoo(self):
        print('hello zoo')
 
zoo = Zoo()
print(zoo.zoo.__name__)
zoo.zoo()   

唯一的区别就是多了一个默认的self参数

类装饰器

待补充

Python内置装饰器

三种类方法装饰器
@classmethod

类方法,不需要实例化,也不需要self参数,但需要一个cls参数,可以用类名调用,也可以用对象来调用。

@staticmethod

静态方法,不需要实例化,不需要self和cls等参数,就跟使用普通的函数一样,只是封装在类中

在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数

class Foo(object):
    @classmethod
    def cls_fun(cls):
        return "hello world"
    @staticmethod
    def static_fun():
        return "hello python"

foo = Foo()
print(Foo.cls_fun())
# print(foo.cls_fun())
print(Foo.static_fun())
@property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

@property 是经典类中的一种装饰器,新式类中具有三种:
1.@property获取属性
2.@方法名.setter 修改属性
3.@方法名.deleter 删除属性@property的使用:

class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价*折扣
        new_price = self.original_price*self.discount
        return new_price

    @price.setter
    def price(self,value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price


obj = Goods()
# print(obj.price)
obj.price = 200
print(obj.price)
del obj.price  # 删除了类中的price属性若再次调用就会报错

参考文章
参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值