(翻译)30天学习Python👨‍💻第十三天——装饰器

467 阅读4分钟

30天学习Python👨‍💻第十三天——装饰器

今天我探索了一个有趣的主题,装饰器。在探讨面向对象编程时,我使用了两个装饰器,@classmethod@staticmethod。然而,我当时并没有详细地去讲。

装饰器是一种编程模式,装饰器是简化版的伪装函数。

使用装饰器给函数添加更多的功能或者使它们更加强大成为了可能。大量的Python库广泛使用装饰器,让人们觉得很神奇。但是,理解装饰器之前还有一些概念需要先理解。

函数是一等公民

在Python中函数是一等公民,这意味着函数可以像其他数据类型一样作为参数传给其他函数,也可以像其他数据类型一样作为函数的返回值。在JavaScript里函数也是类似的,因此我把它们联系了起来。

def multiplier(num1, num2):
  return num1 * num2

some_variable = multiplier # (创建了一个函数引用)

del multiplier # (删除一个函数)

print(some_variable(2,4)) # 8 (函数仍然能够被调用!)

在Python中,函数作为一个值被传给另一个函数的能力对于创建装饰器是必要的。

高阶函数

当满足下面的任意条件时,函数被称为高阶函数

  • 接受另一个函数作为参数
  • 返回另一个函数
  • 二者都满足
def logger(func, args):  # higher order function
    print(f'The result of the passed function is {func(*args)}')


def sum(num1, num2):
    return num1 + num2


logger(sum, (1, 5))
def random(): # 高阶函数
  def special():
    print('I am something special')
  return special

random_value = random()
random_value() 
# 只用一行的方式,与上面两行的功能一样
random()() 

自定义装饰器

根据上面的原则,一个装饰器看起来应该是这个样子:

def starmaker(func):
  '''
  A decorator function which accepts a function
  and then wraps some goodness into it and
  returns it back!
  '''
  def wrapper():
    func()
    print('You are a star now!')
    print('*********')
  return wrapper

@starmaker
def layman():
  print('I am just a layman')

layman()

starmaker 装饰器函数赋予了layman 函数更强大的功能。它基本是在函数上添加了一个包装器。现在装饰器@starmaker可以添加在任何函数的顶部,使另一个函数变成最靓的仔。牛逼!

Python编译器能够识别出@decoratorname,并将其实时转换为一个函数并对其进行处理。上面的代码和下面不使用@decorator的代码基本一致。

def starmaker(func):
  '''
  A decorator function which accepts a function
  and then wraps some goodness into it and
  returns it back!
  '''
  def wrapper():
    func()
    print('You are a star now!')
    print('*********')
  return wrapper

def layman():
  print('I am just a layman')

starmaker(layman)()

一开始接触装饰器的时候我也感到很疑惑。但是当我搞清楚它的基本原理之后,它对我就变得特别自然,我把它加入到了我的知识体系中。

如果和JavaScript对比,JavaScript语言中没有装饰器的存在。但是,JavaScript的超集TypeScript中是有装饰器的。像Angular,NestJS这样的框架重度使用装饰器。

一个装饰器函数能够接受参数,并且能够根据传递的参数进行定制。

def emojifier(func):
  def wrapper(emoji):
    print(emoji)
    func()
  return wrapper

@emojifier
def random():
  pass

random('😀') # 😀

为什么装饰器这么有用

装饰器是一种非常重要的编程模式,如果使用得当,它能带来很多好处。它能使代码具有高可重用性,并将添加的功能绑定到函数中,保证了编程的DRY原则。

# 创造一个@authenticated装饰器
# 要运行的函数是对象的'valid'设置为True:
test_user = {
    'name': 'Jackson',
    'valid': True
}

another_user = {
  'name': 'Nathan',
  'valid': False
}

def authenticated(fn):
  def wrapper(*args, **kwargs):
    if args[0]['valid']:
      fn(args)
  return wrapper

@authenticated
def message_friends(user):
    print('message has been sent')

message_friends(test_user) # 输出 message has been sent
message_friends(another_user) # (什么的都不做)

上面经过身份验证的装饰器函数只根据指定的条件调用message_friends函数。这提供了很大的灵活性,并根据用户身份验证的状态执行条件操作。

Python装饰器的参考文章:

这就是今天的全部了。明天我将继续探讨Python中关于错误处理技术,这是接下来另一个重要的主题。

原文链接

30 Days of Python 👨‍💻 - Day 13 - Decorators