Главная страница
Навигация по странице:

  • 30.1. Перегрузка арифметических операторов 119 Самоучитель Python, Выпуск 0.2

  • 30.1. Перегрузка арифметических операторов 120

  • 31.1. Передача декоратором аргументов в функцию 123 Самоучитель Python, Выпуск 0.2

  • 31.2. Декорирование методов 124 Самоучитель Python, Выпуск 0.2

  • 31.2. Декорирование методов 125 Самоучитель Python, Выпуск 0.2 Декораторы с аргументами

  • 31.3. Декораторы с аргументами 126 Самоучитель Python, Выпуск 0.2

  • 31.3. Декораторы с аргументами 127 Самоучитель Python, Выпуск 0.2 Некоторые особенности работы с декораторами

  • 31.4. Некоторые особенности работы с декораторами 128 Самоучитель Python, Выпуск 0.2 Примеры использования декораторов

  • Самоучитель PythonВыпуск 2Дмитрий Мусинмая 07, 2017


    Скачать 0.74 Mb.
    НазваниеСамоучитель PythonВыпуск 2Дмитрий Мусинмая 07, 2017
    Дата20.01.2019
    Размер0.74 Mb.
    Формат файлаpdf
    Имя файлаPython3.pdf
    ТипПрограмма
    #64480
    страница10 из 12
    1   ...   4   5   6   7   8   9   10   11   12

    30.1. Перегрузка арифметических операторов
    118

    Самоучитель Python, Выпуск 0.2
    def
    __add__
    (
    self
    , other):
    return
    Vector2D(
    self x
    +
    other x,
    self y
    +
    other y)
    def
    __iadd__
    (
    self
    , other):
    self x
    +=
    other x
    self y
    +=
    other y
    return self def
    __sub__
    (
    self
    , other):
    return
    Vector2D(
    self x
    - other x,
    self y
    - other y)
    def
    __isub__
    (
    self
    , other):
    self x
    -=
    other x
    self y
    -=
    other y
    return self def
    __abs__
    (
    self
    ):
    return math hypot(
    self x,
    self y)
    def
    __bool__
    (
    self
    ):
    return self x
    !=
    0
    or self y
    !=
    0
    def
    __neg__
    (
    self
    ):
    return
    Vector2D(
    - self x,
    - self y)
    >>>
    x
    =
    Vector2D(
    3
    ,
    4
    )
    >>>
    x
    Vector2D(
    3
    ,
    4
    )
    >>>
    print
    (x)
    (
    3
    ,
    4
    )
    >>>
    abs
    (x)
    5.0
    >>>
    y
    =
    Vector2D(
    5
    ,
    6
    )
    >>>
    y
    Vector2D(
    5
    ,
    6
    )
    >>>
    x
    +
    y
    Vector2D(
    8
    ,
    10
    )
    >>>
    x
    - y
    Vector2D(
    -
    2
    ,
    -
    2
    )
    >>> - x
    Vector2D(
    -
    3
    ,
    -
    4
    )
    >>>
    x
    +=
    y
    >>>
    x
    Vector2D(
    8
    ,
    10
    )
    >>>
    bool
    (x)
    True
    >>>
    z
    =
    Vector2D(
    0
    ,
    0
    )
    >>>
    bool
    (z)
    False
    >>> - z
    30.1. Перегрузка арифметических операторов
    119

    Самоучитель Python, Выпуск 0.2
    Vector2D(
    0
    ,
    0
    )
    В заключение хочу сказать, что перегрузка специальных методов - вещь хорошая, но не стоит ей слишком злоупотреблять. Перегружайте их только тогда, когда вы уверены в том, что это поможет пониманию программного кода.
    30.1. Перегрузка арифметических операторов
    120

    Глава
    31
    Декораторы
    Декораторы в Python и примеры их практического использования.
    Итак, что же это такое? Для того, чтобы понять, как работают декораторы, в первую оче- редь следует вспомнить, что функции в python являются объектами, соответственно, их можно возвращать из другой функции или передавать в качестве аргумента. Также сле- дует помнить, что функция в python может быть определена и внутри другой функции.
    Вспомнив это, можно смело переходить к декораторам. Декораторы — это, по сути,
    “обёртки”, которые дают нам возможность изменить поведение функции, не изменяя её
    код.
    Создадим свой декоратор “вручную”:
    >>>
    def my_shiny_new_decorator
    (function_to_decorate):
    # Внутри себя декоратор определяет функцию-"обёртку". Она будет обёрнута
    ˓→
    вокруг декорируемой,
    # получая возможность исполнять произвольный код до и после неё.
    def the_wrapper_around_the_original_function
    ():
    print
    (
    "Я - код, который отработает до вызова функции"
    )
    function_to_decorate()
    # Сама функция print
    (
    "А я - код, срабатывающий после"
    )
    # Вернём эту функцию return the_wrapper_around_the_original_function
    >>>
    # Представим теперь, что у нас есть функция, которую мы не планируем больше
    ˓→
    трогать.
    >>>
    def stand_alone_function
    ():
    print
    (
    "Я простая одинокая функция, ты ведь не посмеешь меня изменять?"
    )
    >>>
    stand_alone_function()
    Я простая одинокая функция, ты ведь не посмеешь меня изменять?
    >>>
    # Однако, чтобы изменить её поведение, мы можем декорировать её, то есть
    ˓→
    просто передать декоратору,
    121

    Самоучитель Python, Выпуск 0.2
    >>>
    # который обернет исходную функцию в любой код, который нам потребуется, и
    ˓→
    вернёт новую,
    >>>
    # готовую к использованию функцию:
    >>>
    stand_alone_function_decorated
    =
    my_shiny_new_decorator(stand_alone_function)
    >>>
    stand_alone_function_decorated()
    Я - код, который отработает до вызова функции
    Я простая одинокая функция, ты ведь не посмеешь меня изменять?
    А я - код, срабатывающий после
    Наверное, теперь мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function,
    вместо неё вызывалась stand_alone_function_decorated. Для этого просто перезапишем stand_alone_function:
    >>>
    stand_alone_function
    =
    my_shiny_new_decorator(stand_alone_function)
    >>>
    stand_alone_function()
    Я - код, который отработает до вызова функции
    Я простая одинокая функция, ты ведь не посмеешь меня изменять?
    А я - код, срабатывающий после
    Собственно, это и есть декораторы. Вот так можно было записать предыдущий пример,
    используя синтаксис декораторов:
    >>>
    @my_shiny_new_decorator def another_stand_alone_function
    ():
    print
    (
    "Оставь меня в покое"
    )
    >>>
    another_stand_alone_function()
    Я - код, который отработает до вызова функции
    Оставь меня в покое
    А я - код, срабатывающий после
    То есть, декораторы в python — это просто синтаксический сахар для конструкций вида:
    another_stand_alone_function
    =
    my_shiny_new_decorator(another_stand_alone_function)
    При этом, естественно, можно использовать несколько декораторов для одной функции,
    например так:
    >>>
    def bread
    (func):
    def wrapper
    ():
    print
    ()
    func()
    print
    (
    "<\______/>"
    )
    return wrapper
    >>>
    def ingredients
    (func):
    def wrapper
    ():
    print
    (
    "#помидоры#"
    )
    func()
    print
    (
    "

    салат"
    )
    return wrapper
    122

    Самоучитель Python, Выпуск 0.2
    >>>
    def sandwich
    (food
    =
    "--ветчина--"
    ):
    print
    (food)
    >>>
    sandwich()
    --ветчина--
    >>>
    sandwich
    =
    bread(ingredients(sandwich))
    >>>
    sandwich()
    #помидоры#
    --ветчина--
    салат
    <\______/>
    И используя синтаксис декораторов:
    >>>
    @bread
    @ingredients def sandwich
    (food
    =
    "--ветчина--"
    ):
    print
    (food)
    >>>
    sandwich()
    #помидоры#
    --ветчина--
    салат
    <\______/>
    Также нужно помнить о том, что важен порядок декорирования. Сравните с предыдущим примером:
    >>>
    @ingredients
    @bread def sandwich
    (food
    =
    "--ветчина--"
    ):
    print
    (food)
    >>>
    sandwich()
    #помидоры#
    --ветчина--
    <\______/>
    салат
    Передача декоратором аргументов в функцию
    Однако, все декораторы, которые мы рассматривали, не имели одного очень важно- го функционала — передачи аргументов декорируемой функции. Собственно, это тоже несложно сделать.
    31.1. Передача декоратором аргументов в функцию
    123

    Самоучитель Python, Выпуск 0.2
    >>>
    def a_decorator_passing_arguments
    (function_to_decorate):
    def a_wrapper_accepting_arguments
    (arg1, arg2):
    print
    (
    "Смотри, что я получил:"
    , arg1, arg2)
    function_to_decorate(arg1, arg2)
    return a_wrapper_accepting_arguments
    >>>
    # Теперь, когда мы вызываем функцию, которую возвращает декоратор, мы вызываем
    ˓→
    её "обёртку",
    >>>
    # передаём ей аргументы и уже в свою очередь она передаёт их декорируемой
    ˓→
    функции
    >>>
    @a_decorator_passing_arguments def print_full_name
    (first_name, last_name):
    print
    (
    "Меня зовут"
    , first_name, last_name)
    >>>
    print_full_name(
    "Vasya"
    ,
    "Pupkin"
    )
    Смотри, что я получил: Vasya Pupkin
    Меня зовут Vasya Pupkin
    Декорирование методов
    Один из важных фактов, которые следует понимать, заключается в том, что функции и методы в Python — это практически одно и то же, за исключением того, что методы все- гда ожидают первым параметром ссылку на сам объект (self ). Это значит, что мы можем создавать декораторы для методов точно так же, как и для функций, просто не забывая про self.
    >>>
    def method_friendly_decorator
    (method_to_decorate):
    def wrapper
    (
    self
    , lie):
    lie
    -=
    3
    return method_to_decorate(
    self
    , lie)
    return wrapper
    >>>
    class
    Lucy
    :
    def
    __init__
    (
    self
    ):
    self age
    =
    32
    @method_friendly_decorator def sayYourAge
    (
    self
    , lie):
    print
    (
    "Мне
    {}
    лет, а ты бы сколько дал?"
    format(
    self age
    +
    lie))
    >>>
    l
    =
    Lucy()
    >>>
    l sayYourAge(
    -
    3
    )
    Мне 26 лет, а ты бы сколько дал?
    Конечно, если мы создаём максимально общий декоратор и хотим, чтобы его можно было применить к любой функции или методу, то можно воспользоваться распаковкой аргу- ментов:
    31.2. Декорирование методов
    124

    Самоучитель Python, Выпуск 0.2
    >>>
    def a_decorator_passing_arbitrary_arguments
    (function_to_decorate):
    # Данная "обёртка" принимает любые аргументы def a_wrapper_accepting_arbitrary_arguments
    (
    *
    args,
    **
    kwargs):
    print
    (
    "Передали ли мне что-нибудь?:"
    )
    print
    (args)
    print
    (kwargs)
    function_to_decorate(
    *
    args,
    **
    kwargs)
    return a_wrapper_accepting_arbitrary_arguments
    >>>
    @a_decorator_passing_arbitrary_arguments def function_with_no_argument
    ():
    print
    (
    "Python is cool, no argument here."
    )
    >>>
    function_with_no_argument()
    Передали ли мне что-нибудь?:
    ()
    {}
    Python is cool, no argument here.
    >>>
    @a_decorator_passing_arbitrary_arguments def function_with_arguments
    (a, b, c):
    print
    (a, b, c)
    >>>
    function_with_arguments(
    1
    ,
    2
    ,
    3
    )
    Передали ли мне что-нибудь?:
    (1, 2, 3)
    {}
    1 2 3
    >>>
    @a_decorator_passing_arbitrary_arguments def function_with_named_arguments
    (a, b, c, platypus
    =
    "Почему нет?"
    ):
    print
    (
    "Любят ли
    {}
    ,
    {}
    и
    {}
    утконосов?
    {}
    "
    format(a, b, c, platypus))
    >>>
    function_with_named_arguments(
    "Билл"
    ,
    "Линус"
    ,
    "Стив"
    , platypus
    =
    "Определенно!"
    )
    Передали ли мне что-нибудь?:
    ('Билл', 'Линус', 'Стив')
    {'platypus': 'Определенно!'}
    Любят ли Билл, Линус и Стив утконосов? Определенно!
    >>>
    class
    Mary
    (
    object
    ):
    def
    __init__
    (
    self
    ):
    self age
    =
    31
    @a_decorator_passing_arbitrary_arguments def sayYourAge
    (
    self
    , lie
    =-
    3
    ):
    # Теперь мы можем указать значение по
    ˓→
    умолчанию print
    (
    "Мне
    {}
    лет, а ты бы сколько дал?"
    format(
    self age
    +
    lie))
    >>>
    m
    =
    Mary()
    >>>
    m sayYourAge()
    Передали ли мне что-нибудь?:
    (<__main__.Mary object at 0x7f6373017780>,)
    {}
    Мне 28 лет, а ты бы сколько дал?
    31.2. Декорирование методов
    125

    Самоучитель Python, Выпуск 0.2
    Декораторы с аргументами
    А теперь попробуем написать декоратор, принимающий аргументы:
    >>>
    def decorator_maker
    ():
    print
    (
    "Я создаю декораторы! Я буду вызван только раз: когда ты попросишь
    ˓→
    меня создать декоратор."
    )
    def my_decorator
    (func):
    print
    (
    "Я - декоратор! Я буду вызван только раз: в момент декорирования
    ˓→
    функции."
    )
    def wrapped
    ():
    print
    (
    "Я - обёртка вокруг декорируемой функции.\n"
    "Я буду вызвана каждый раз, когда ты вызываешь декорируемую
    ˓→
    функцию.\n"
    "Я возвращаю результат работы декорируемой функции."
    )
    return func()
    print
    (
    "Я возвращаю обёрнутую функцию."
    )
    return wrapped print
    (
    "Я возвращаю декоратор."
    )
    return my_decorator
    >>>
    # Давайте теперь создадим декоратор. Это всего лишь ещё один вызов функции
    >>>
    new_decorator
    =
    decorator_maker()
    Я создаю декораторы! Я буду вызван только раз: когда ты попросишь меня создать
    ˓→
    декоратор.
    Я возвращаю декоратор.
    >>>
    >>>
    # Теперь декорируем функцию
    >>>
    def decorated_function
    ():
    print
    (
    "Я - декорируемая функция."
    )
    >>>
    decorated_function
    =
    new_decorator(decorated_function)
    Я - декоратор! Я буду вызван только раз: в момент декорирования функции.
    Я возвращаю обёрнутую функцию.
    >>>
    # Теперь наконец вызовем функцию:
    >>>
    decorated_function()
    Я - обёртка вокруг декорируемой функции.
    Я буду вызвана каждый раз, когда ты вызываешь декорируемую функцию.
    Я возвращаю результат работы декорируемой функции.
    Я - декорируемая функция.
    Теперь перепишем данный код с помощью декораторов:
    >>>
    @decorator_maker
    ()
    def decorated_function
    ():
    print
    (
    "Я - декорируемая функция."
    )
    Я создаю декораторы! Я буду вызван только раз: когда ты попросишь меня создать
    ˓→
    декоратор.
    Я возвращаю декоратор.
    Я - декоратор! Я буду вызван только раз: в момент декорирования функции.
    31.3. Декораторы с аргументами
    126

    Самоучитель Python, Выпуск 0.2
    Я возвращаю обёрнутую функцию.
    >>>
    decorated_function()
    Я - обёртка вокруг декорируемой функции.
    Я буду вызвана каждый раз когда ты вызываешь декорируемую функцию.
    Я возвращаю результат работы декорируемой функции.
    Я - декорируемая функция.
    Вернёмся к аргументам декораторов, ведь, если мы используем функцию, чтобы созда- вать декораторы “на лету”, мы можем передавать ей любые аргументы, верно?
    >>>
    def decorator_maker_with_arguments
    (decorator_arg1, decorator_arg2):
    print
    (
    "Я создаю декораторы! И я получил следующие аргументы:"
    ,
    decorator_arg1, decorator_arg2)
    def my_decorator
    (func):
    print
    (
    "Я - декоратор. И ты всё же смог передать мне эти аргументы:"
    ,
    decorator_arg1, decorator_arg2)
    # Не перепутайте аргументы декораторов с аргументами функций!
    def wrapped
    (function_arg1, function_arg2):
    print
    (
    "Я - обёртка вокруг декорируемой функции.\n"
    "И я имею доступ ко всем аргументам\n"
    "\t- и декоратора:
    {0}
    {1}
    \n"
    "\t- и функции:
    {2}
    {3}
    \n"
    "Теперь я могу передать нужные аргументы дальше"
    format(decorator_arg1, decorator_arg2,
    function_arg1, function_arg2))
    return func(function_arg1, function_arg2)
    return wrapped return my_decorator
    >>>
    @decorator_maker_with_arguments
    (
    "Леонард"
    ,
    "Шелдон"
    )
    def decorated_function_with_arguments
    (function_arg1, function_arg2):
    print
    (
    "Я - декорируемая функция и я знаю только о своих аргументах:
    {0}
    "
    "
    {1}
    "
    format(function_arg1, function_arg2))
    Я создаю декораторы! И я получил следующие аргументы: Леонард Шелдон
    Я - декоратор. И ты всё же смог передать мне эти аргументы: Леонард Шелдон
    >>>
    decorated_function_with_arguments(
    "Раджеш"
    ,
    "Говард"
    )
    Я - обёртка вокруг декорируемой функции.
    И я имею доступ ко всем аргументам
    - и декоратора: Леонард Шелдон
    - и функции: Раджеш Говард
    Теперь я могу передать нужные аргументы дальше
    Я - декорируемая функция и я знаю только о своих аргументах: Раджеш Говард
    Таким образом, мы можем передавать декоратору любые аргументы, как обычной функ- ции. Мы можем использовать и распаковку через *args и **kwargs в случае необходимо- сти.
    31.3. Декораторы с аргументами
    127

    Самоучитель Python, Выпуск 0.2
    Некоторые особенности работы с декораторами
    • Декораторы несколько замедляют вызов функции, не забывайте об этом.
    • Вы не можете “раздекорировать” функцию. Безусловно, существуют трюки, позво- ляющие создать декоратор, который можно отсоединить от функции, но это плохая практика. Правильнее будет запомнить, что если функция декорирована — это не отменить.
    • Декораторы оборачивают функции, что может затруднить отладку.
    Последняя проблема частично решена добавлением в
    модуле functools функции functools.wraps, копирующей всю информацию об оборачиваемой функции (её имя, из какого она модуля, её документацию и т.п.) в функцию-обёртку.
    Забавным фактом является то, что functools.wraps тоже является декоратором.
    >>>
    def foo
    ():
    print
    (
    "foo"
    )
    >>>
    print
    (foo
    __name__
    )
    foo
    >>>
    # Однако, декораторы мешают нормальному ходу дел:
    def bar
    (func):
    def wrapper
    ():
    print
    (
    "bar"
    )
    return func()
    return wrapper
    >>>
    @bar def foo
    ():
    print
    (
    "foo"
    )
    >>>
    print
    (foo
    __name__
    )
    wrapper
    >>>
    import functools
    # "functools" может нам с этим помочь
    >>>
    def bar
    (func):
    # Объявляем "wrapper" оборачивающим "func"
    # и запускаем магию:
    @functools wraps(func)
    def wrapper
    ():
    print
    (
    "bar"
    )
    return func()
    return wrapper
    >>>
    @bar def foo
    ():
    print
    (
    "foo"
    )
    >>>
    print
    (foo
    __name__
    )
    foo
    31.4. Некоторые особенности работы с декораторами
    128

    Самоучитель Python, Выпуск 0.2
    Примеры использования декораторов
    Декораторы могут быть использованы для расширения возможностей функций из сто- ронних библиотек (код которых мы не можем изменять), или для упрощения отладки
    (мы не хотим изменять код, который ещё не устоялся).
    Также полезно использовать декораторы для расширения различных функций одним и тем же кодом, без повторного его переписывания каждый раз, например:
    >>>
    def benchmark
    (func):
    """
    Декоратор, выводящий время, которое заняло выполнение декорируемой функции.
    """
    import time def wrapper
    (
    *
    args,
    **
    kwargs):
    t
    =
    time clock()
    res
    =
    func(
    *
    args,
    **
    kwargs)
    print
    (func
    __name__
    , time clock()
    - t)
    return res return wrapper
    >>>
    def logging
    (func):
    """
    Декоратор, логирующий работу кода.
    (хорошо, он просто выводит вызовы, но тут могло быть и логирование!)
    """
    def wrapper
    (
    *
    args,
    **
    kwargs):
    res
    =
    func(
    *
    args,
    **
    kwargs)
    print
    (func
    __name__
    , args, kwargs)
    return res return wrapper
    >>>
    def counter
    (func):
    """
    Декоратор, считающий и выводящий количество вызовов декорируемой функции.
    """
    def wrapper
    (
    *
    args,
    **
    kwargs):
    wrapper count
    +=
    1
    res
    =
    func(
    *
    args,
    **
    kwargs)
    print
    (
    "
    {0}
    была вызвана:
    {1}
    x"
    format(func
    __name__
    , wrapper count))
    return res wrapper count
    =
    0
    return wrapper
    >>>
    @benchmark
    @logging
    @counter def reverse_string
    (string):
    return
    ''
    join(
    reversed
    (string))
    1   ...   4   5   6   7   8   9   10   11   12


    написать администратору сайта