[Advanced Python] 13 - "Decorator": syntactic sugar

单独一章复习:Python 函数装饰器廖雪峰讲装饰器html

 

基本概念


函数做为变量

从函数中返回函数

子函数写在外面貌似也能够,可这样就少了“封装性”。python

def greet():
    return "now you are in the greet() function"

def welcome():
    return "now you are in the welcome() function"


def hi(name="yasoob"): 
    if name == "yasoob":
        return greet
    else:
        return welcome
 
a
= hi()
print(a) #outputs: <function greet at 0x7f2143c01500> print(a()) #outputs: now you are in the greet() function

 

将函数做为参数传给另外一个函数

def hi():
    return "hi yasoob!"
 
def doSomethingBeforeHi(func):
    print("I am doing some boring work before executing hi()")
    print(func())
 
doSomethingBeforeHi(hi)
#outputs:I am doing some boring work before executing hi()
#        hi yasoob!

 

 

你的第一个“装饰器”

传统方法

a_function_requiring_decoration经过a_new_decorator得到了加强版的本身。git

def a_new_decorator(a_func):
 
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
 
        a_func()  print("I am doing some boring work after executing a_func()")
 
    return wrapTheFunction def a_function_requiring_decoration():
    print("I am the function which needs some decoration to remove my foul smell")
 
a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell" a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)  # <---- 函数(A)把本身"装饰"了一下,加强了功能 #now a_function_requiring_decoration is wrapped by wrapTheFunction() a_function_requiring_decoration() #outputs:I am doing some boring work before executing a_func() # I am the function which needs some decoration to remove my foul smell # I am doing some boring work after executing a_func()

 

@语法糖策略

写好a_new_decorator装饰器函数后,只要在须要“被装饰”的函数上添加@语法糖就行了。web

如此,就省掉了上述中的 函数(A)的步骤。闭包

一篇通俗易懂的博文:这是我见过最全面的Python装饰器详解!没有学不会这种说法!app

@a_new_decorator def a_function_requiring_decoration():
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")

 

However,只是表面上完美,抽象的还不够完全。 框架

 

 

装饰器装饰“有参函数”

难点

上面的例子没有参数,若是有参数会如何?函数

并且只会写“一个装饰器”,因此“参数个数不定”也须要考虑进去。ui

 

装饰更多函数

# 万能装饰器,修饰各类参数
def
celebrator(func):   def inner(*args, **kwargs):     print('我是新功能')     func(*args, **kwargs)   return inner
@celebrator
def myprint(a):   print(a) myprint('python小白联盟')

 

有无返回值

def celebrator(func):
  def inner(*args, **kwargs):
    print('我是新功能')
    ret = func(*args, **kwargs)
    return ret   
return inner

装饰的函数,若是有返回值,则一致;若是没有返回值,则返回 None。spa

 

 

双重语法糖

嵌套装饰器

最外层的语法糖先执行。

 

带参数的语法糖

使用带参的语法糖来进行简化;也就是再嵌套一层函数,同时也保证了“参数的独立性”

有点相似于“闭包”的如下这个例子,保持了子函数的参数的独立性。

def count():
-------------------------
def f(j):          # 由于通过了函数的封装,因此保持了函数内部变量的独立性 def g(): return j*j return g ------------------------- fs = [] for i in range(1, 4): fs.append(f(i))    # f(i)马上被执行,所以i的当前值被传入f() return fs f1, f2, f3 = count()

 

 

functools.wraps

以前带来的问题

以下可见,返回的函数名字是装饰器中的wrap函数的名字,这是个封装缺陷。

print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction

 

解决方案

经过wraps告诉wrap函数:“你的扮演人物的身份是谁”。

注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可让咱们在装饰器里面访问在装饰以前的函数的属性。

from functools import wraps def a_new_decorator(a_func):
    @wraps(a_func) 
def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()") return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): """Hey yo! Decorate me!""" print("I am the function which needs some decoration to " "remove my foul smell") print(a_function_requiring_decoration.__name__) # Output: a_function_requiring_decoration

 

 

 

使用场景


两个案例

受权(Authorization)

装饰器能有助于检查某我的是否被受权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。

这里是一个例子来使用基于装饰器的受权:

from functools import wraps
 
def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            authenticate()
        return f(*args, **kwargs)
    return decorated

 

日志(Logging)

日志是装饰器运用的另外一个亮点。这是个例子:

from functools import wraps
 
def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging
 
@logit
def addition_func(x):
   """Do some math."""
   return x + x
 
 
result = addition_func(4)
# Output: addition_func was called

 

 

装饰器类

【在类的章节再详解】

咱们的应用的某些部分还比较脆弱时,异常也许是须要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引发你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止咱们只看到过用来构建装饰器的函数。

幸运的是,类也能够用来构建装饰器。那咱们如今以一个类而不是一个函数的方式,来从新构建logit。

一个装饰类例子

from functools import wraps
 
class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
 
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile并写入
            with open(self.logfile, 'a') as opened_file:
                # 如今将日志打到指定的文件
                opened_file.write(log_string + '\n')
            # 如今,发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不作别的
        pass

 

添加email功能

如今,咱们给 logit 建立子类,来添加 email 的功能。

class email_logit(logit):
    '''
    一个logit的实现版本,能够在函数调用时发送email给管理员
    '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)
 
    def notify(self):
        # 发送一封email到self.email
        # 这里就不作实现了
        pass

 

 

End.

相关文章
相关标签/搜索