python装饰器及闭包

闭包:在函数中可以嵌套定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持持久性。
概括来说就是当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。

def make_printer(msg):
    def printer():
        print msg  # 夹带私货(外部变量)
    return printer  # 返回的是函数,带私货的函数

printer = make_printer('Foo!')
printer()

为何需要闭包?
闭包存在的意义就是它夹带了外部变量(私货),同一个的函数夹带了不同的私货,就实现了不同的功能。也可以理解为闭包和面向接口编程的概念很像,闭包是轻量级的接口封装。

def tag(tag_name):
    def add_tag(content):
        return "<{0}>{1}<{0}>".format(tag_name,content)
    return add_tag
content="hello"
add_tag=tag("a")
print(add_tag(content))
#结果
<a>hello<a>

在这个例子里,我们想要一个给content加tag的功能,但是具体的tag_name是什么样子的要根据实际需求来定,对外部调用的接口已经确定,就是add_tag(content)。如果按照面向接口方式实现,我们会先把add_tag写成接口,指定其参数和返回类型,然后去实现a的add_tag。
但是在闭包的概念中,add_tag就是一个函数,它需要tag_name和content两个参数,只不过tag_name这个参数是打包带走的。所以一开始时就可以告诉我怎么打包,然后带走就行。

上面的例子不太生动,其实在我们生活和工作中,闭包的概念也很常见。比如说手机拨号,你只关心电话打给谁,而不会去纠结每个品牌的手机是怎么实现的,用到了哪些模块。再比如去餐馆吃饭,你只要付钱就可以享受到服务,你并不知道那桌饭菜用了多少地沟油。这些都可以看成闭包,返回来的是一些功能或者服务(打电话,用餐),但是这些功能使用了外部变量(天线,地沟油等等)。

你也可以把一个类实例看成闭包,当你在构造这个类时,使用了不同的参数,这些参数就是闭包里的包,这个类对外提供的方法就是闭包的功能。但是类远远大于闭包,因为闭包只是一个可以执行的函数,但是类实例则有可能提供很多方法。
何时使用?
python中的装饰器Decorator,假如需要写一个带参数的装饰器,name一般都会生成闭包。
python的装饰器是一个固定的函数接口形式,它要求装饰器函数必须接受一个函数并返回一个函数,当装饰器需要自定义参数时,一般都会形成闭包(类装饰器例外)

def html_tags(tag_name):
    def wrapper_(func):
        def wrapper(*args, **kwargs):
            content = func(*args, **kwargs)
            return "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content)
        return wrapper
    return wrapper_

@html_tags('b')
def hello(name='Toby'):
    return 'Hello {}!'.format(name)

# 不用@的写法如下
# hello = html_tag('b')(hello)
# html_tag('b') 是一个闭包,它接受一个函数,并返回一个函数

print hello()  # <b>Hello Toby!</b>
print hello('world')  # <b>Hello world!</b>

装饰器

装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加
额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志,性能测试,事务处理,缓存,权限效验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
简单来说,装饰器的作用就是为已存在的函数或队形添加额外的功能

def debug(func):
    def wrapper():
        print ("[DEBUG]: enter {}()".format(func.__name__))
        return func()
    return wrapper

def say_hello():
    print ("hello!")

say_hello = debug(say_hello)  # 添加功能并保持原函数名不变

上面的dubug函数其实就是一个装饰器,它对原函数做了包装并返回了另一个函数,额外增加了一些功能(打印debug信息)。因为这样写不太优雅,在后面版本支持了@语法糖

def debug(func):
	def wrapper():
		print("[DEBUG]: enter {}()".format(func.__name__))
		return func()
	return wrapper
@debug
def say_hello():
	print("hello")

后边因为需要传入参数,

def debug(func):
	def wrapper(*args,**kwargs):   #制定宇宙无敌参数
		print("[DEBUG]: enter {}()".format(func.__name__))
		return func(*args,**kwargs)
	return wrapper  #f返回
@debug
def say(something):
	print("hello{}".format(something))

带参数的装饰器
假设我们前文的装饰器需要完成的功能不仅仅是能在进入某个函数后打出log信息,而且还需指定log的级别,那么装饰器就会是这样的。

def logging(level):
    def wrapper(func):
        def inner_wrapper(*args,**kwargs):
            print("{level}:enter function{func}()".format(level=level,func=func.__name__))
            return func(*args,**kwargs)
        return inner_wrapper
    return wrapper
#不用@语法,等同于 say=logging(level="INFO")(say)
@logging(level="INFO")
def say(something):
    print("say{}".format(something))
@logging(level="DEBUG")
def do(something):
    print("do{}".format(something))
if __name__=="__main__":
    say("hello")
    do("my work")

可以这么理解,当带参数的装饰器被打在某个函数上时,比如 @logging(level=‘DEBUG’)
它其实是一个函数,会马上被执行,只要这个它返回的结果是一个装饰器时,那就没问题

内置装饰器
@property
内置装饰器和普通的装饰器原理一样,只不过返回的不是函数,而是类对象。

class Test(object):
    def getx(self):
        return self._x


    def setx(self, value):
        if value>0:
            self._x = value
        else:
            self._x=0


    def delx(self):
        del self._x


    # create a property
    #公有属性名 = property(getter 方法名, setter 方法名)
    x = property(getx, setx, delx, "I am doc for x property")
t=Test()
t.x=-20
print(t.x)
#结果:0

上面也可以用@property内置装饰器写成:

class T(object):

    @property
    def age(self):
        return self._x

    @age.setter
    def age(self, value):
        if value>0:
            self._x = value
        else:
            self._x=0

    def delx(self):
        del self._x

    # create a property
    # x = property(getx, setx, delx)
t=T()
t.age=-20
print(t.age)

观察上述案例可以发现,通过使用@property 后,可以命名相同的方法名,且调用时,会把该方法名当成是
一个公有的属性名来使用。
Python 中的静态方法(@staticmethod)与类方法(声明 @classmethod),则可以直接采用如下语法格式:
类名.方法名 = 静态方法名或类方法名

#静态方法
@staticmethod
def run():
    print("静态方法:run()")
 # # 等同于 run = staticmethod(run)
class User(object):
    def start(self):
        print("----start---")
user=User()
user.start()
#添加方法
User.run=run
User.run()  #z执行依次
#对象拥有了方法
user.run()   #

类方法

@classmethod
def run(cls):
    print("类方法:run()")
class User(object):
    def start(self):
        print("------start()--------")
u2 = User()
u2.start()
# 添加方法
User.run = run
User.run()
# 对象
u2.run()

详情参考https://www.cnblogs.com/cicaday/p/python-decorator.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值