Python Tips, Tricks, and Hacks (часть 4, заключительная)

http://www.siafoo.net/article/52
  • Перевод
Это заключительная часть перевода статьи. Декораторы, switch для функций, некоторая информация о классах.

5. Функции


5.4 Декораторы

Декораторы функций довольно просты, но если вы не видели их раньше, вам будет трудно догадаться, как они работают. Это неочевидно, в отличие от большей части синтаксиса Python. Декоратор — это функция, оборачивающая другую функцию: сначала создается главная функция, затем она передается декоратору. Декоратор возвращает новую функцию, которая используется вместо первоначальной в остальной части программы.

Не будем больше задерживаться на этом. Вот синтаксис:

def decorator1(func):
    return lambda: func() + 1
 
def decorator2(func):
    def print_func():
        print func()
    return print_func
 
@decorator2
@decorator1
def function():
    return 41
 
function()
# печатает "42" 


В этом примере function передается в decorator1, а он возвращает функцию, которая вызывает function и возвращает число, большее ее результата на единицу. Эта функция передается в decorator2, который ее вызывает и печатает результат. Так-то.

Следующий пример делает абсолютно то же, но более многословно:

def decorator1(func):
    return lambda: func() + 1
 
def decorator2(func):
    def print_func():
        print func()
    return print_func
 
def function():
    return 41
 
function = decorator2(decorator1(function))
 
function()
# печатает "42" 

Обычно декораторы используют для добавления новых возможностей вашим функциям (см. создание методов класса). Еще чаще они не используются совсем. Но чтобы разбираться в коде, вам нужно знать, что это такое.

Чтобы узнать больше о декораторах, обратитесь к статье автора «Python Decorators Don't Have to be (that) Scary», статье «Python Decorators» на Dr. Dobbs или разделу «Function Definitions» документации.

5.5 Запуск одной из нескольких функций при помощи словаря

Не хватает оператора switch? Вы, возможно, знаете, что в Python нет эквивалента switch для функций (разве что многократное elif). Но вы можете воспроизвести его поведение, создав словарь функций. Например, пусть вы обрабатываете нажатия клавиш и у вас есть следующие функции:

def key_1_pressed():
    print 'Нажата клавиша 1'
 
def key_2_pressed():
    print 'Нажата клавиша 2'
 
def key_3_pressed():
    print 'Нажата клавиша 3'
 
def unknown_key_pressed():
    print 'Нажата неизвестная клавиша'

Обычный метод — использование elif:

keycode = 2
if keycode == 1:
   key_1_pressed()
elif keycode == 2:
   key_2_pressed()
elif number == 3:
   key_3_pressed()
else:
   unknown_key_pressed()
# выводит "Нажата клавиша 2" 

Но вы также можете положить все функции в словарь, ключами которого будут значения соответствующего keycode. Вы даже можете обработать случай получения неизвестного keycode:

keycode = 2
functions = {1: key_1_pressed, 2: key_2_pressed, 3: key_3_pressed}
functions.get(keycode, unknown_key_pressed)()

Очевидно, этот код намного понятней предыдущего (особенно при большом числе функций).

6. Классы


6.1 Передача self вручную

Метод — это обычная функция: если он вызывается у экземпляра объекта, этот экземпляр передается в качестве первого аргумента (обычно его называют self). Если по какой-то причине вы вызываете метод не у экземпляра, вы всегда можете первым аргументом передать экземпляр вручную. Например:

class Class:
    def a_method(self):
        print 'Привет, метод!'
 
instance = Class()
 
instance.a_method()
# печатает 'Привет, метод!', что неудивительно.  Вы также можете сделать:
 
Class.a_method(instance)
# печатает 'Привет, метод!' 

Изнутри эти выражения абсолютно одинаковы.

6.2 Проверка на существование метода или свойства

Хотите узнать, есть ли у объекта тот или иной метод или свойство? Вы можете использовать встроенную функцию hasattr, которая принимает объект и имя атрибута.

class Class:
    answer = 42
 
hasattr(Class, 'answer')
# возвращает True
hasattr(Class, 'question')
# возвращает False 

Вы можете также проверить существование атрибута и получить его с помощью встроенной функции getattr, которая также принимает объект и имя атрибута. Третий аргумент необязательный и устанавливает значение по умолчанию. Если атрибут не найден и третий аргумент не задан, бросается исключение AttributeError.

class Class:
    answer = 42
 
getattr(Class, 'answer')
# возвращает 42
getattr(Class, 'question', 'Сколько будет 6x9?')
# возвращает "Сколько будет 6x9?"
getattr(Class, 'question')
# бросает исключение AttributeError 

Не используйте getattr и hasattr слишком часто. Если вы написали класс так, что необходимо проверять существование свойств, то вы всё сделали неправильно. Свойство должно существовать всегда, а если оно не используется, можно установить его в None. Эти функции используются в основном для поддержки полиморфизма, т. е. возможности использовать в вашем коде любых типов объектов.

6.3 Изменение класса после создания

Вы можете добавлять, изменять и удалять свойства и методы класса после его создания, даже после того, как будут созданы экземпляры этого класса. Для этого достаточно использовать запись Class.attribute. Изменения применятся ко всем экземплярам класса, вне зависимости от того, когда они были созданы.

class Class:
   def method(self):
        print 'Привет, Хабр'
 
instance = Class()
instance.method()
# печатает "Привет, Хабр"
 
def new_method(self):
    print 'Хабр еще торт!'
 
Class.method = new_method
instance.method()
# печатает "Хабр еще торт!"

Фантастика. Но не увлекайтесь изменением существующих методов, это дурной тон. Кроме того, это может нарушить работу методов, использующих изменяемый класс. С другой стороны, добавление методов менее опасно.

6.4 Создание методов класса

Иногда при написании класса возникает потребность в функции, вызываемой от класса, а не его экземпляра. Иногда этот метод создает новые экземпляры, иногда он не зависит от индивидуальных свойств экземпляров. В Python есть два способа это сделать, оба действуют через декораторы. Ваш выбор зависит от того, нужно ли вам знать, какой класс вызвал метод.

Метод класса (class method) принимает вызвавший его класс первым аргументом (аналогично обычные методы получают первым аргументом соответствующий экземпляр). Этому методу известно, запущен он от этого же класса или от подкласса.

Статический метод (static method) не имеет никаком информации о том, откуда он запущен. Это обычная функция, просто в другой области видимости.

Как статические методы, так и методы класса могут быть вызваны непосредственно из класса:

class Class:
    @classmethod
    def a_class_method(cls):
        print 'Вызван из класса %s' % cls
 
    @staticmethod
    def a_static_method():
         print 'Понятия не имею, откуда вызван'
 
    def an_instance_method(self):
        print 'Вызван из экземпляра %s' % self
 
instance = Class()
 
Class.a_class_method()
instance.a_class_method()
# оба печатают "Вызван из класса __main__.Class"
 
Class.a_static_method()
instance.a_static_method()
# оба печатают "Понятия не имею, откуда вызван"
 
Class.an_instance_method()
# бросает исключение TypeError
 
instance.an_instance_method()
# печатает что-то вроде "Вызван из экземпляра <__main__.Class instance at 0x2e80d0>"

Заключение


Нужно больше вдохняющих идей? Для начала неплохо посмотреть страницу Python Built-in Functions. Там довольно много хороших функций, о которых вы, возможно, никогда не слышали.

Если у вас есть полезные уловки или вещи, которые следует знать, автор предлагает добавить их в свою статью.

Счастливого программирования.

С другими статьями автора, а также с источниками информации вы можете ознакомиться здесь.

Статья целиком в PDF
Метки:
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 19
  • +3
    Ну, если часть заключительная, то можно все вместе объединить и отправить на печать.
    Большое спасибо за перевод! Буду дальше изучать столь интересный язык.
    • 0
      Спасибо большое, очень интересная информация)
      • +3
        переменуйте декораторы из первого примера, нечитабельная же каша с цифрами получается
        • 0
          спасибо, за ваши старания!
          надеюсь этот цикл поможет многим начинающим
          • 0
            Ой, по мне так самая полезная статья цикла. Понравилось про словарь.
            • 0
              Интересно, большое спасибо. Но только я Hacks так и не увидел — это всё в манах описано, даже с юзкейсами.
              • 0
                Да, у питона с яваскриптом все же довольно много общего
                • +1
                  если верить вики, то питон повлиял на яваскрипт :)
                • НЛО прилетело и опубликовало эту надпись здесь
                  • 0
                    Спасибо, особенно за pdf версию
                    • +1
                      Function decorators are fairly simple, but if you've never seen them before you'll have no idea what's going on, as unlike most of Python the syntax isn't very clear.


                      Это неочевидно, как и большая часть синтаксиса Python.

                      Переведите правильно: «Это неочевидно, в отличие от большей части синтаксиса Python».

                      Как вообще можно написать, что синтаксис Питона неочевиден в статье о Питоне? :-)
                    • 0
                      Странно, что ничего про new.instancemethod не написано.
                      • 0
                        какое new.instancemethod?
                        • 0
                          Позволяет добавить динамически новый метод к экземпляру класса, а не к классу. Использую при динамическом создании класса на основе другого класса.
                          • 0
                            ну так этож совсем очевидная вещь. чего о ней писать то?
                            • 0
                              Да здесь в списке все вещи такие. Нет?
                              • 0
                                ну вобщем да.
                                но классы тут вобще весьма поверхностно.

                      • 0
                        Мега огромное спасибо за ваши переводы статей!
                        Сам относительно недавно начал изучать питон.
                        Чтото из статей узнал, что то узнал, а что напомнил.
                        Обещаю каждый раз перечитывать, что бы напоминать себе о существовании некоторых элементов этого языка :)

                        Одна просьба.
                        Не могли бы вы в каждой статье, в начале или в конце, добавить «содержание», список ссылок на статьи из цикла.
                        Что бы из одной статьи проще было бы перебираться в другую.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.