В данной статье мы рассмотрим основные концепции Python, которые необходимо знать и понимать каждому профессиональному программисту. Они образуют основу продвинутого программирования на Python, нацеленного на разумное решение задач.
Создание итератора и итерируемого объекта в Python требует немалой работы: необходимо создать класс (объектно-ориентированное программирование) при помощи методов __iter__()
и __next__()
; надлежит сохранить и обновить внутренние состояния и вывести исключение StopIteration
при отсутствии возвращаемого значения.
Для сокращения этого затянутого процесса генераторы Python могут создавать итераторы, выполняя все вышеописанные действия автоматически. Генератор — это функция, возвращающая объект (итератор), который также может быть итерирован (последовательным перебором значений по одному).
Функция генератора определяется как функция, но при этом использует ключевое слово yield
вместо традиционного return
. Если тело функции содержит yield
, то она является функцией генератора.
def simple_generator():
yield 1
yield 2
yield 3for value in simple_generator():
print(value)
Первые четыре строки кода определяют генератор, который будет итерационно возвращать 1
, 2
и 3
. Две последние строки демонстрируют итерацию, выводя на экран одно значение за другим:
1
2
3
Генератор по-прежнему будет требовать функцию для выделения объекта, но нам его нужно приравнять к переменной, в данном случае x
. Атрибуты, описанные выше как .next()
, автоматически создаются Python и могут быть вызваны.
x = simple_generator()
print(x.next())
print(x.next())
print(x.next())
Например, следующий код создает генератор последовательности чисел Фибоначчи, в которой каждый член является суммой двух предыдущих (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
находится в пределах допустимого значения (limit), т. е. числа, указанного в качестве параметра функции, программа выдает (“возвращает”) значение a. Затем a
и b
одновременно обновляются, в процессе чего a
принимает значение b
, а b
— значение суммы a
+ b
. В результате этих действий последовательность смещается вправо на одну позицию, сохраняя соответствующую информацию для получения следующего значения последовательности (суммы двух предыдущих чисел).
Теперь генератор можно использовать в цикле 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
Переменные объекта и функции можно вызвать внутри объекта class
, используя .
, предшествующий ей элемент для ссылки на объект и элемент после точки, ссылающийся на вызываемый объект. self.species = species
присваивает внутренней переменной то значение, которое принимает входной параметр species
.
Функции можно создавать также внутри класса:
class dog:
def __init__(self, species, age):
self.species = species
self.age = agedef bark(self, call='Woof!'):
print(call)def reveal_information(self):
print('I am a', self.species)
print('I am', age, 'years old')
Эти две внутренние функции, bark
и reveal_information
, являются методами, выполняемыми и присоединяемыми классом. Затем нам нужно установить переменную pepper
в классе dog и конкретизировать параметры инициализации, а именно species
и age
.
После этого можно вызвать атрибуты pepper
.
На выходе получим результат:
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
также является функцией.
И возвращает
27… потому что при умножении 3 на 9 получается 27. Пожалуй, замыкания являются лучшим способом выполнить эту задачу в Python.
Отметим без ложной скромности, что встроенное перечисление Python просто превосходно. Возможно, одна из самых частых задач, с которой сталкивается разработчик — это итерация элементов в списке с сохранением отслеживания индекса. Во многих других языках без функции перечисления программисту нужно было бы выполнить это действие вручную, как в этом примере на Python:
counter = 0
for item in a_list:
do_something_with(item)
do_something_else_with(counter)
counter += 1
Однако функция enumerate()
в Python автоматически отслеживает счетчик каждой итерации, возвращая распаковываемый кортеж:
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()
. Переменная decorated
, которую мы сохраняем на выходе decorate()
, является функцией (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
перед функцией.
Перевод статьи Andre Ye: Essential Python Concepts Any Serious Programmer Needs to Know, Explained
Комментарии