RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 621861
Accepted
User New
User New
Asked:2020-02-01 02:21:34 +0000 UTC2020-02-01 02:21:34 +0000 UTC 2020-02-01 02:21:34 +0000 UTC

为什么需要装饰器?(不是他们如何工作,而是为什么)

  • 772

为什么需要装饰器?

谁看过关于装饰器的经典解释:

# Декоратор - это функция, ожидающая ДРУГУЮ функцию в качестве параметра
def my_shiny_new_decorator(a_function_to_decorate):
    # Внутри себя декоратор определяет функцию-"обёртку".
    # Она будет (что бы вы думали?..) обёрнута вокруг декорируемой,
    # получая возможность исполнять произвольный код до и после неё.

    def the_wrapper_around_the_original_function():
        # Поместим здесь код, который мы хотим запускать ДО вызова
        # оригинальной функции
        print "Я - код, который отработает до вызова функции"

        # ВЫЗОВЕМ саму декорируемую функцию
        a_function_to_decorate()

        # А здесь поместим код, который мы хотим запускать ПОСЛЕ вызова
        # оригинальной функции
        print "А я - код, срабатывающий после"

    # На данный момент функция "a_function_to_decorate" НЕ ВЫЗЫВАЛАСЬ НИ РАЗУ

    # Теперь, вернём функцию-обёртку, которая содержит в себе
    # декорируемую функцию, и код, который необходимо выполнить до и после.
    # Всё просто!
    return the_wrapper_around_the_original_function

# Представим теперь, что у нас есть функция, которую мы не планируем больше трогать.
def a_stand_alone_function():
    print "Я простая одинокая функция, ты ведь не посмеешь меня изменять?.."

a_stand_alone_function()
# выведет: Я простая одинокая функция, ты ведь не посмеешь меня изменять?..

# Однако, чтобы изменить её поведение, мы можем декорировать её, то есть
# Просто передать декоратору, который обернет исходную функцию в любой код,
# который нам потребуется, и вернёт новую, готовую к использованию функцию:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#выведет:
# Я - код, который отработает до вызова функции
# Я простая одинокая функция, ты ведь не посмеешь меня изменять?..
# А я - код, срабатывающий после

接下来是一段话:

可能现在我们希望每次调用 a_stand_alone_function 时,调用 a_stand_alone_function_decorated。没有什么比这更简单的了,只需用 my_shiny_new_decorator 返回给我们的函数覆盖 a_stand_alone_function 即可:

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#выведет:
# Я - код, который отработает до вызова функции
# Я простая одинокая функция, ты ведь не посмеешь меня изменять?..
# А я - код, срабатывающий после

那些。在这段之后 - 我们失去了以原始形式调用函数的机会。现在它总是装饰。

问题是,为什么那时需要装饰器?

1)为什么不(如果我们无论如何都失去了原来的功能)只是重写原来的功能?(只需在函数的开头和结尾添加我们需要的代码片段)。像这样:

def a_stand_alone_function():
    print "Я - код, который отработает до вызова функции"
    print "Я простая одинокая функция, ты ведь не посмеешь меня изменять?.."
    print "А я - код, срабатывающий после"

或者,如果附加代码很大并且应该单独保存,那么:

def a_stand_alone_function():
        pre_function()
        print "Я простая одинокая функция, ты ведь не посмеешь меня изменять?.."
        post_function()

2)为什么所有这些混乱,而不是像问题 1 那样提出一个简单的解决方案?

3)如果可以同时调用装饰函数和原始函数,我也会理解装饰器。但是用@写的装饰器不允许这样。为什么装饰器会覆盖原来的功能?

我将非常感谢您提供解释的答案,也许还有来自实际实践的清晰示例。

python
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    Sergey Gornostaev
    2020-02-01T02:32:29Z2020-02-01T02:32:29Z

    例如,装饰器在 Django 中被广泛使用。与其重新发明轮子并在每个控制器功能中编写自己的授权检查代码,这应该只对授权用户可用,我们只需使用适当的装饰器对其进行装饰。而如果还需要保证函数只会用于某种类型的请求呢?那所有与数据库打交道的函数都会在一个事务中执行吗?如果所有代码都被拉入一个函数中,那么很快它的原始含义就会在一堆样板代码中消失。但它在模块中并不孤单,对于每个模块,您都必须编写此样板代码,并重复多次。而不仅仅是写:

    @require_POST
    @login_required
    @transaction_atomic
    def some_view(request):
        ...
    
    • 10
  2. VladD
    2020-02-01T04:37:26Z2020-02-01T04:37:26Z

    除了@Sergey Gornostaev的回答:

    装饰器本质上是 Python 中面向方面编程的一种实现。

    您定义的逻辑或功能与将使用该功能的函数分开。也许另一个团队正在为您开发这个逻辑。您几乎随处可见的日志记录示例或相同的事务是方面功能的可能示例之一。然后您可以轻松地启用此方面的使用,而无需手动重写函数,只需添加或删除一个属性。

    在任何情况下,该属性都是您的源代码的一部分。如果你想要一个未修饰的函数可用,你可以简单地省略装饰器。或者有一个单独的未修饰函数,以及调用未修饰函数的单独修饰版本。或者添加装饰器手动贡献的功能(但在这种情况下,如果装饰器发生变化,您将需要更新代码,例如,错误修复;这是常见的代码重复问题。)

    装饰器只是让您轻松地添加功能。

    • 10
  3. vadim vaduxa
    2020-02-01T02:53:23Z2020-02-01T02:53:23Z
    # во первых ссылку на функцию можно получить из декоратора
    # во вторых теперь эти "куски кода в декораторе" дописать можно к любой функции, например логирование, проверка аргументов перед вызовом
    
    def decor(fn):
        '''декоратор'''
        def wrapper(*args, **kwargs):
            if all(args):
                out = fn(*args, **kwargs)
                with open('file.txt', 'a') as log:
                    log.write('{} {} {} {}'.format(fn, args, kwargs, out))
                print('декоратор', end=' ')
                return out
        return wrapper
    
    @decor
    def func(*args):
        '''@декоратор(изначальная функция)'''
        return sum(args)
    
    func(1, 2, 3)  # вызывать как декорируемую
    
    original_fn = func.__closure__[0].cell_contents  # func без декоратора
    original_fn(1, 2, 3)  # вызывать как изначальную функцию
    
    # или
    import inspect
    original_fn = inspect.getclosurevars(func).nonlocals['fn']
    original_fn(0, 2, 3)
    
    # если постоянный вызов дероратора не требуется, изначальную функцию не стоит декорировать @decor
    def func2(*args):
        '''изначальная функция'''
        return sum(args)
    
    func2(1, 2, 3)  # вызывать как изначальную функцию
    decor(func2)(1, 2, 3)  # вызывать как декорируемую
    
    • 4

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5