Генераторы
Создание итератора в Python требует большой работы. Класс (в ООП) должен быть построен с применением методов __iter__()
и __next__()
, внутренние состояния должны быть сохранены и обновлены, а ошибка StopIteration
должна выбрасываться, когда нет возвращаемого значения.
Чтобы избежать этого длительного процесса, генераторы Python могут автоматически создавать итераторы, выполняя все процессы, описанные выше. Генератор — это функция, возвращающая объект (итератор), который можно итерировать (покрывая одно значение за раз).
Функция-генератор определяется как функция, но использует ключевое слово yield
вместо обычного return
. Если тело функции содержит yield
, это автоматически означает, что функция является генератором.
def simple_generator():
yield 1
yield 2
yield 3
for value in simple_generator():
print(value)
Первые четыре строчки определяют генератор, который будет итеративно возвращать 1
, 2
и 3
. Последние две строчки демонстрируют итерацию, выводя значения:
1
2
3
Объект-генератор всё ещё требует функцию для схемы объекта, но он должен быть задан равным переменной, в данном случае x
.
x = simple_generator()
print(next(x))
print(next(x))
print(next(x))
Например, следующий код создаёт генератор последовательности Фибоначчи, где каждый член является суммой двух предыдущих (0, 1, 1, 2, 3, 5, 8, 13, …).
def fibonacci(limit):
a, b = 0, 1
while a < limit:
yield a
a, b = b, a+b
Числа a
и b
, используемые для отслеживания последовательности, инициализируются равными 0 и 1 соответственно. Пока a
находится ниже предела — числа, указанного в качестве параметра функции — программа возвращает значение a. Затем a
и b
обновляются одновременно, когда a
задано равным b
, а b
равен самому себе плюс a
. Это смещает последовательность на единицу вправо, сохраняя при этом соответствующую информацию (два предыдущих числа) для генерации следующего значения последовательности.
Генератор затем можно итерировать, в данном случае с помощью цикла for
:
for i in fibonacci(8):
print(i)
Итерация будет повторяться до числа 8:
0
1
1
2
3
5
Объектно-ориентированное программирование
Объектно-ориентированное программирование — это особенность Python, которая обеспечивает чистое и организованное хранение методов и переменных. ООП в Python состоит из объектов class
, содержащих информацию об этом объекте.
Скажем, мы хотим создать виртуальную собаку на Python. Персональные атрибуты класса хранятся в функции __init__
, в которую должен быть включён параметр self
, как и другие атрибуты объекта, которые должны быть определены при создании, такие как species
и age
.
class dog:
def __init__(self, species, age):
self.species = species
self.age = age
Переменные и функции объекта могут вызываться внутри объекта с помощью .
с предыдущим элементом, ссылающимся на объект, и элементом после точки, ссылающимся на вызываемый объект. self.species = species
задаёт внутреннюю переменную к тому, чем является входной параметр species
.
Функции также можно создавать в классе:
class dog:
def __init__(self, species, age):
self.species = species
self.age = age
def bark(self, call='Woof!'):
print(call)
def reveal_information(self):
print('I am a', self.species)
print('I am', self.age, 'years old')
Две внутренние функции bark
и reveal_information
являются методами, выполняемыми и прикрепляемыми классом. Затем мы можем задать переменную pepper
классу dpg
. Мы должны указать параметры инициализации species
и age
.
pepper = dog(species='German Shepard', age=3)
Затем можем вызвать атрибуты pepper
:
pepper.reveal_information()
Вот вывод:
I am a German Shepard
I am 3 years old
ООП в Python подходит для множества задач. Даже если на настройки требуется больше времени, ООП позволяет писать более читаемый код.
Замыкания
Замыкания помогают избежать использования глобальных переменных и предоставляют форму скрытия данных, обеспечивая объектно-ориентированное решение проблемы. Когда в классе реализовано мало методов, замыкания могут обеспечить альтернативное и более элегантное решение. Когда количество атрибутов и методов растёт, класс становится более подходящим решением. Следующие критерии демонстрируют замыкания в Python, когда вложенная функция ссылается на значение в своей объемлющей области:
- существует вложенная функция (функция внутри функции);
- вложенная функция ссылается на значение, объявленное в замыкающей функции;
- замыкающая функция возвращает вложенную функцию.
Продемонстрируем замыкание на примере функции make_multiplier
, которая принимает параметр n
и возвращает функцию с этим параметром.
def make_multiplier(n):
def multiplier(x):
return x * n
return multiplier
Создаём функцию, умножающую что-то на 3:
times3 = make_multiplier(3)
Поскольку make_multiplier
возвращает функцию, times3
является функцией…
print(times3(9))
возвращающей…
27
потому что 3, умноженное на 9, даёт 27. Замыкание, вероятно, лучший способ выполнить эту задачу в Python.
Встроенное перечисление
Встроенное перечисление в Python впечатляет. Возможно, наиболее частой задачей, с которой сталкиваются разработчики, — это перебор элементов в списке с отслеживанием индекса. Во многих других языках без перечисления программистам приходится делать это вручную, например так:
counter = 0
for item in a_list:
do_something_with(item)
do_something_else_with(counter)
counter += 1
Однако функция Python enumerate()
автоматически отслеживает счётчик при каждой итерации, возвращая кортеж, который можно распаковать:
for index, item in enumerate(a_list):
do_something_with(item)
do_something_with(index)
Следующий код…
for index, item in enumerate(['a','b','c']):
print(index,item)
выведет:
0 a
1 b
2 c
Декораторы
Декораторы принимают функцию, добавляют к ней некоторую функциональность и возвращают её. Это очень полезно в случаях, когда необходимы небольшие вариации родительской функции, потому что функцию можно изменять с помощью декоратора, а не переписывать заново при каждой вариации.
Скажем, у нас есть функция ordinary()
, единственная цель которой — печатать строку “I am an ordinary function.”
.
def ordinary():
print("I am an ordinary function.")
Скажем, мы хотим добавить ещё одно сообщение “I was decorated!”
. Мы можем создать функцию decorate()
, которая принимает функцию, печатает дополнительное сообщение, затем вызывает исходную функцию, чей объект становится параметром функции decorate()
. Процесс добавления строки и вызова оригинальной функции можно сохранить во внутренней функции, чей объект возвращается с помощью decorate()
.
def decorate(function):
def inner_function():
print("I was decorated!")
function()
return inner_function
Чтобы декорировать оригинальную функцию ordinary()
, вызовем функцию decorate()
в исходной функции. Переменная, в которую сохраняются выходные данные decorate()
, decorated
, является функцией (inner_function
в функции decorate()
).
decorated = decorate(ordinary)
decorated()
Вызов decorated()
возвращает:
I was decorated!
I am an ordinary function.
Декораторы используют символ @
для автоматического декорирования функции.
@decorate
def ordinary():
print("I am an ordinary function.")
Использование символа @
перед определением функции автоматически декорирует функцию. Она вычисляется так же, как ранее описанный метод декорирования функции.
Несколько декораторов можно связать друг с другом, добавив перед функцией @decorate
несколько строк.
Читайте также:
- Галерея лучших модулей Python
- 5 доказательств силы итерируемых объектов в Python
- Как я устроил пожизненный запас чесночных пицца-палочек с помощью Python и Selenium
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Andre Ye: Essential Python Concepts & Structures Any Serious Programmer Needs to Know, Explained