Руководство по магическим методам в питоне
Содержание:
- Краткое введение в ООП
- Принципы ООП
- Mutable default values
- Destroying Objects (Garbage Collection)
- Defining a Class in Python
- Функция в Python
- Сквозной пример по классам
- Create Multiple Functions
- Создание Вложенных Классов В Python
- Операторы перегрузки
- Операции над словарями Python
- LDA для одного предсказателя
- Поезд / тестовый сплит
- Рекомендации по ФП на языке Python
- Дескрипторы
- Объекты
- Ошибки при работе со списками Python
- Включение в последовательность
Краткое введение в ООП
Объектно-ориентированное программирование (ООП) – технология разработки сложного программного обеспечения, в которой программа строится в виде совокупности объектов и их взаимосвязей.
Объединение данных и действий, производимых над этими данными, в единое целое, которое называется объектом – является одним из основных принципов ООП.
Основными понятиями являются понятие класса и объекта.
Класс является типом данных, определяемым пользователем и представляет собой структуру в виде данных и методов для работы с данными.
Формально Класс — это шаблон, по которому будет сделан объект.
Объект является экземпляром класса. Объект и экземпляр - это одно и то же.
Вот пример. Форма для изготовления печенья – это класс, а само печенье это объект или экземпляр класса, т.е. это конкретное изделие. Печенье имеет размеры, цвет, состав – это атрибуты класса. Также в классе описываются методы, которые предназначены для чтения или изменения данных объекта.
В Python характеристики объекта, называются атрибутами, а действия, которые мы можем проделывать с объектами, — методами. Методами в Python называют функции, которые определяются внутри класса.
Объект = атрибуты + методы
Принципы ООП
Абстракция
Абстракция – это выделение основных, наиболее значимых характеристик объекта и игнорирование второстепенных.
Любой составной объект реального мира – это абстракция. Говоря «ноутбук», вам не требуется дальнейших пояснений, вроде того, что это организованный набор пластика, металла, жидкокристаллического дисплея и микросхем. Абстракция позволяет игнорировать нерелевантные детали, поэтому для нашего сознания это один из главных способов справляться со сложностью реального мира. Если б, подходя к холодильнику, вы должны были иметь дело с отдельно металлом корпуса, пластиковыми фрагментами, лакокрасочным слоем и мотором, вы вряд ли смогли бы достать из морозилки замороженную клубнику.
Полиморфизм
Полиморфизм подразумевает возможность нескольких реализаций одной идеи. Простой пример: у вас есть класс «Персонаж», а у него есть метод «Атаковать». Для воина это будет означать удар мечом, для рейнджера – выстрел из лука, а для волшебника – чтение заклинания «Огненный Шар». В сущности, все эти три действия – атака, но в программном коде они будут реализованы совершенно по-разному.
Наследование
Это способность одного класса расширять понятие другого, и главный механизм повторного использования кода в ООП. Вернёмся к нашему автосимулятору. На уровне абстракции «Автотранспорт» мы не учитываем особенности каждого конкретного вида транспортного средства, а рассматриваем их «в целом». Если же более детализировано приглядеться, например, к грузовикам, то окажется, что у них есть такие свойства и возможности, которых нет ни у легковых, ни у пассажирских машин. Но, при этом, они всё ещё обладают всеми другими характеристиками, присущими автотранспорту.
Мы могли бы сделать отдельный класс «Грузовик», который является наследником «Автотранспорта». Объекты этого класса могли бы определять все прошлые атрибуты (цвет, год выпуска), но и получить новые. Для грузовиков это могли быть грузоподъёмность, снаряженная масса и наличие жилого отсека в кабине. А методом, который есть только у грузовиков, могла быть функция сцепления и отцепления прицепа.
Инкапсуляция
Инкапсуляция – это ещё один принцип, который нужен для безопасности и управления сложностью кода. Инкапсуляция блокирует доступ к деталям сложной концепции. Абстракция подразумевает возможность рассмотреть объект с общей точки зрения, а инкапсуляция не позволяет рассматривать этот объект с какой-либо другой.
Вы разработали для муниципальных служб класс «Квартира». У неё есть свойства вроде адреса, метража и высоты потолков. И методы, такие как получение информации о каждом из этих свойств и, главное, метод, реализующий постановку на учёт в Росреестре. Это готовая концепция, и вам не нужно чтобы кто-то мог добавлять методы «открыть дверь» и «получить место хранения денег». Это А) Небезопасно и Б) Избыточно, а также, в рамках выбранной реализации, не нужно. Работникам Росреестра не требуется заходить к вам домой, чтобы узнать высоту потолков – они пользуются только теми документами, которые вы сами им предоставили.
Mutable default values
Python stores default member variable values in class attributes.
Consider this example, not using Data Classes:
class C: x = [] def add(self, element): self.x += element o1 = C() o2 = C() o1.add(1) o2.add(2) assert o1.x == assert o1.x is o2.x
Note that the two instances of class C share the same class
variable x, as expected.
Using Data Classes, if this code was valid:
@dataclass class D: x: List = [] def add(self, element): self.x += element
it would generate code similar to:
class D: x = [] def __init__(self, x=x): self.x = x def add(self, element): self.x += element assert D().x is D().x
This has the same issue as the original example using class C.
That is, two instances of class D that do not specify a value for
x when creating a class instance will share the same copy of
x. Because Data Classes just use normal Python class creation
they also share this problem. There is no general way for Data
Classes to detect this condition. Instead, Data Classes will raise a
TypeError if it detects a default parameter of type list,
dict, or set. This is a partial solution, but it does protect
against many common errors. See in the Rejected Ideas section for more details.
Using default factory functions is a way to create new instances of
mutable types as default values for fields:
Destroying Objects (Garbage Collection)
Python deletes unneeded objects (built-in types or class instances) automatically to free the memory space. The process by which Python periodically reclaims blocks of memory that no longer are in use is termed Garbage Collection.
Python’s garbage collector runs during program execution and is triggered when an object’s reference count reaches zero. An object’s reference count changes as the number of aliases that point to it changes.
An object’s reference count increases when it is assigned a new name or placed in a container (list, tuple, or dictionary). The object’s reference count decreases when it’s deleted with del, its reference is reassigned, or its reference goes out of scope. When an object’s reference count reaches zero, Python collects it automatically.
a = 40 # Create object <40> b = a # Increase ref. count of <40> c = # Increase ref. count of <40> del a # Decrease ref. count of <40> b = 100 # Decrease ref. count of <40> c = -1 # Decrease ref. count of <40>
You normally will not notice when the garbage collector destroys an orphaned instance and reclaims its space. But a class can implement the special method __del__(), called a destructor, that is invoked when the instance is about to be destroyed. This method might be used to clean up any non memory resources used by an instance.
Example
This __del__() destructor prints the class name of an instance that is about to be destroyed −
#!/usr/bin/python class Point: def __init__( self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print class_name, "destroyed" pt1 = Point() pt2 = pt1 pt3 = pt1 print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts del pt1 del pt2 del pt3
When the above code is executed, it produces following result −
3083401324 3083401324 3083401324 Point destroyed
Note − Ideally, you should define your classes in separate file, then you should import them in your main program file using import statement.
Defining a Class in Python
Like function definitions begin with the keyword in Python, class definitions begin with a keyword.
The first string inside the class is called docstring and has a brief description of the class. Although not mandatory, this is highly recommended.
Here is a simple class definition.
A class creates a new local namespace where all its attributes are defined. Attributes may be data or functions.
There are also special attributes in it that begins with double underscores . For example, gives us the docstring of that class.
As soon as we define a class, a new class object is created with the same name. This class object allows us to access the different attributes as well as to instantiate new objects of that class.
Output
10 <function Person.greet at 0x7fc78c6e8160> This is a person class
Функция в Python
Если какая то задача выполняется многократно в программе, то не обязательно эту задачу расписывать во всех разделах программы, достаточно поместить код в функцию и в последующем вызывать эту функцию по мере необходимости.
Напишем функцию, которая вычисляет квадрат своего аргумента и выводит на экран:
>>> def square(number):
… «»»Вычисление квадрата числа»»»
… (number 2)
…
>>> square(5)25
>>> square(124.45)15487.802500000002
Определение функции начинается с ключевого слова def, за которым следует имя функции — square. Имя функции, как и имена переменных рекомендуется писать с букв нижнего регистра, а в именах, состоящих из нескольких слов, составляющие должны разделяться символами подчеркивания. Далее в круглых скобках записываются параметры (аргументы) функции, разделенные запятыми. Функция square имеет только один аргумент с именем number — значение, возводимое в квадрат. В случае отсутствия параметров у функции пустые круглые скобки обязательны. В конце строки за параметрами всегда ставится двоеточие ().
После двоеточия новая строка должна идти с отступом (4 пробела). Все строки с отступом образуют тело или блок функции. В «Руководстве по стилю кода Python» указано, что первой строкой блока функции должна быть doc-строка, кратко поясняющая назначение функции: «»»Вычисление квадрата числа»»». Сам код в теле функции состоит всего из одной строки (number 2).
Команда squre(5) вызывает функции square() и передает ей значение аргумента, для выполнения команды . Функция возводит число в квадрат и выводит на экран.
Сквозной пример по классам
####################################################
## 5. Классы
####################################################
# Чтобы получить класс, мы наследуемся от object.
class Human(object):
# Атрибут класса. Он разделяется всеми экземплярами этого класса
species = «H
sapiens»
# Обычный конструктор, вызывается при инициализации экземпляра класса
# Обратите внимание, что двойное подчёркивание в начале и в конце имени
# означает объекты и атрибуты, которые используются Python, но находятся
# в пространствах имён, управляемых пользователем.
# Не придумывайте им имена самостоятельно.
def __init__(self, name):
# Присваивание значения аргумента атрибуту класса name
self.name = name
# Метод экземпляра. Все методы принимают self в качестве первого аргумента
def say(self, msg):
return «{name}: {message}».format(name=self.name, message=msg)
# Метод класса разделяется между всеми экземплярами
# Они вызываются с указыванием вызывающего класса в качестве первого аргумента
@classmethod
def get_species(cls):
return cls.species
# Статический метод вызывается без ссылки на класс или экземпляр
@staticmethod
def grunt():
return «*grunt*»
# Инициализация экземпляра класса
i = Human(name=»Иван»)
print(i.say(«привет»)) # Выводит: «Иван: привет»
j = Human(«Пётр»)
print(j.say(«Привет»)) # Выводит: «Пётр: привет»
# Вызов метода класса
i.get_species() #=> «H
sapiens»
# Изменение разделяемого атрибута
Human.species = «H. neanderthalensis»
i.get_species() #=> «H. neanderthalensis»
j.get_species() #=> «H. neanderthalensis»
# Вызов статического метода
Human.grunt() #=> «*grunt*»
4.5
10
голоса
Рейтинг статьи
Create Multiple Functions
You’d normally put more than one function in a class. You can also pass arguments to the functions as you would any other function.
Here’s an example of a class that groups arithmetic related functions:
# Create the class
class Arithmetic:
def Add(self, x, y):
return x + y
def Subtract(self, x, y):
return x — y
def Multiply(self, x, y):
return x * y
def Divide(self, x, y):
return x / y
# Object 1
a = Arithmetic()
print(a.Add(2, 3))
print(a.Subtract(8, 300))
print(a.Multiply(2, 3))
print(a.Divide(30, 5))
# Object 2
b = Arithmetic()
print(b.Add(22, 33))
print(b.Subtract(88, 3333))
print(b.Multiply(77, 99))
print(b.Divide(555, 444))Result
5 -292 6 6.0 55 -3245 7623 1.25
Создание Вложенных Классов В Python
В этом разделе мы в первую очередь сосредоточимся на создании вложенных классов. Для этого давайте рассмотрим пример.
class language: def __init__(self): .specification() def show(self): print("Language:", self.language) class specification: def __init__(self): def display(self): print("type:", self.type) print("Founded:", self.founded) () out.show() .lg ppool.display()
Language: PYTHON type: HIGH-LEVEL Founded: 1991
Здесь выше мы успешно создали вложенный класс . Теперь давайте пройдемся строчка за строчкой и поймем, что мы это сделали. Итак, сначала мы создали класс с именем language. Внутри него мы использовали ключевое слово self. Ключевое слово self-это ключ, через который мы можем получить доступ к атрибутам и методам aнашего созданного класса. Внутри класса language мы создали еще один вложенный класс под названием specification. Там мы точно так же определили спецификации. Наконец, мы получаем желаемый результат.
Операторы перегрузки
Предположим, что вы создали класс Vector для представления двумерных векторов. Что произойдет, когда вы добавите оператор «плюс»? Скорее всего, Python будет кричать на вас.
Однако вы можете определить метод __add__ в вашем классе для выполнения сложения векторов, и тогда оператор плюс будет вести себя так, как ожидалось:
пример
#!/usr/bin/python class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self,other): return Vector(self.a + other.a, self.b + other.b) v1 = Vector(2,10) v2 = Vector(5,-2) print v1 + v2
Когда приведенный выше код выполняется, он дает следующий результат
Vector(7,8)
Операции над словарями Python
Если словарь, содержащий полный набор данных, большой, то разумнее использовать список lowscores, который мы только что скомпилировали, чтобы создать совершенно новый словарь (Python список в словарь). Преимущество этого приема заключается в том, что для дальнейшего анализа не нужно хранить в памяти большой словарь. Можно просто перейти к соответствующему подмножеству исходных данных.
Во-первых, мы используем ключи, хранящиеся в lowscores, для создания нового словаря. Чтобы сделать это, есть два способа: первый — извлекаем только соответствующие элементы из исходного словаря с помощью метода .get(), оставляя исходный словарь без изменений. Второй — использовать метод .pop(), который удаляет извлеченные записи из исходного словаря.
Код для подмножества может выглядеть следующим образом: subset = dict(). Такое написание может показаться незнакомым, потому что цикл задан одной строкой кода. Этот стиль называется «генерацией словаря». На самом деле это цикл for, который перебирает элементы lowscores, извлекает значения из отзывов и использует их для заполнения нового словаря.
Вы можете сравнить традиционный стиль с использованием цикла и генерацию словаря и убедиться, что они действительно дают идентичный результат:
# Метод с использованием цикла for для создания подмножества словаря forloop = {} for k in lowscores: forloop = reviews # Добавляем специальный метод извлечения релевантных элементов из словаря `reviews` dictcomp = {k : reviews.___(k) for k in lowscores} # Удостоверимся, что эти объекты аналогичны print(forloop == ________)
Предположим, что теперь вы хотите изменить словарь Python 3, чтобы оценки выступали в качестве ключей словаря, а не идентификаторов. Можно использовать для этого цикл for, указав как ключи, так и значения, и создав новый вложенный словарь. Нужно будет извлечь «score» из исходного вложенного словаря, чтобы использовать его в качестве нового ключа.
Чтобы упростить код, мы создаем в отдельной строке новый вложенный словарь как новый объект newvalues. После чего заполняем scoredict идентификаторами в качестве ключей и объектами из словаря newvalues в качестве значений:
from collections import defaultdict scoredict = defaultdict(list) for key, value in reviews.items(): newvalues = {'id' : key, "title" : value, "review" : value} # Используем 'score' из значений (!) из исходного словаря в качестве ключей для только что созданного словаря scoredict].append(newvalues) # Выводим ключи словаря, чтобы удостовериться, что это на самом деле оценки из отзывов print(scoredict.keys())
LDA для одного предсказателя
Предположим, у нас есть только один предиктор и что функция плотности нормальная. Затем вы можете выразить функцию плотности как:
Нормальная функция распределения
Теперь мы хотим назначить наблюдениеХ = хдля которогоP_k (Х)самый большой. Если вы подключите функцию плотности вP_k (Х)и взятьжурналВы обнаружите, что хотите максимально увеличить:
Дискриминантное уравнение
Уравнение выше называетсядискриминант.Как видите, это линейное уравнение. Отсюда и название:линейный дискриминантный анализ!
Теперь, предполагая только два класса с равным распределением, вы найдете:
Граничное уравнение
Это граничное уравнение. Графическое представление показано ниже.
Граничная линия для разделения 2 классов с использованием LDA
Конечно, это идеальное решение. В действительности, мы не можем точно рассчитать граничную линию.
Поэтому LDA использует следующее приближение:
Для среднего всех тренировочных наблюдений
Среднее из всех тренировочных наблюдений
Для средневзвешенной выборочной дисперсии для каждого класса
Средневзвешенное значение выборочных отклонений для каждого класса
гдеNколичество наблюдений
Важно знать, что LDA предполагаетнормальное распределениедля каждого классасреднее по классуиобщая разница,
Поезд / тестовый сплит
Прежде чем углубляться в моделирование и делать прогнозы, нам нужно разделить наш набор данных на обучающий набор и набор тестов. Таким образом, мы можем обучить алгоритм на тренировочном наборе и делать прогнозы на тестовом наборе. Метрики ошибок будут намного более релевантными, так как алгоритм будет делать прогнозы для данных, которых он не видел раньше.
Мы можем легко разделить набор данных следующим образом:
Вот,Yпросто цель (ядовитая или съедобная). Затем,Иксэто просто все особенности набора данных. Наконец, мы используемtrain_test_splitфункция.test_sizeПараметр соответствует части набора данных, которая будет использоваться для тестирования. Обычно мы используем 20%. Затемrandom_stateПараметр используется для воспроизводимости. Он может быть установлен на любое число, но он гарантирует, что при каждом запуске кода набор данных будет разделен одинаково. Если нетrandom_stateпри условии, что поезд и набор тестов будут различаться, так как функция разбивает их случайным образом.
Хорошо, мы официально готовы начать моделирование и делать прогнозы!
Рекомендации по ФП на языке Python
Понятие ФП несколько различается по строгости формулировки. Одни понимают применение только функций, немутируемость и наведение мостов с периферией (вводом-выводом). Другие определяют ФП строже и наряду с немутируемостью говорят о применении только чистых функций. Но в любом случае программирование в функциональном стиле не тождественно функциональному программированию. Применение первоклассных функций, лямбд, итераторов, включений, каррирования и сопоставления с шаблонами вовсе не означает немутируемость и чистые функции.
Что делает функции нечистыми?
-
Глобальные мутации, т.е. внесение изменений в глобальное состояние,
-
Недетерминированность функций, т.е. которые для одинаковых входных значений могут возвращать разные результаты, и
-
Операции ввода-вывода.
Пример глобальной мутации:
Пример недетерминированности:
Пример операции ввода-вывода:
Из чистых функций вытекает ссылочная (референциальная) прозрачность. Говорят, что программа или математическое выражение ссылочно прозрачны, если любое подвыражение можно заменить его значением, и это не приведет к изменению значения целого, т. е. скрытые побочные эффекты отсутствуют. Математические рассуждения, преобразования и доказательства корректности могут быть справедливыми только для выражений, обладающих этим свойством. А программы, написанные на обычных императивных языках, не являются ссылочно прозрачными, так как присваивание значений глобальным переменным, в некоторых случаях и локальным, вызывает скрытые побочные эффекты.
Ссылочная прозрачность (1) улучшает тестопригодность программ, т.е. поведение подпрограмм не зависит от контекста, повторный запуск приложения дает одинаковый результаты как следствие отсутствия мутаций, (2) обеспечивается модульность, т.е. поведение функций не зависит от контекста, и чистые функции можно легко составлять в композиции, строя новые формы поведений, (3) упрощает обеспечение конкурентности из-за отсутствия необходимости в синхронизации, т.к. отсутствие совместных мутируемых данных делает синхронизацию ненужной.
Однако, ФП имеет свои недостатки, такие как новизна парадигмы и иногда ухудшение производительности программ. Но в нашем случае главный недостаток состоит в том, что язык Python, как таковой, не является языком функционального программирования. Например, в нем нет библиотеки по работе с неизменяемыми структурами данных и оптимизации стека под хвостовую рекурсию. Однако эффективное функциональное программирование на Python вполне возможно.
В отличие от объектно-ориентированного программирования, которое строит сложные формы поведения с помощью наследования, ФП опирается на композицию функций. Этот принцип перекликается с философией Unix, состоящей из 2 правил:
-
Правило композиции — строить программы так, чтобы иметь возможность легко их соединять с другими программами.
-
Правило модульности — писать простые части, которые можно соединять чистыми интерфейсами.
Указанные выше два простых правила делают ненужными архитектурные шаблоны и принципы ООП, заменяя их функциями! А что, спросите вы, и классы тоже? В Python использование классов не противоречит ФП, если в них отсутствует мутирующие интерфейсы.
Пример класса с мутирующим интерфейсом:
Пример класса без мутирующего интерфейса:
Но лучше использовать замороженные dataclasses и копирование, где необходимо. Иными словами, все классы должны быть замороженными dataclasses.
При всем при этом dataclasses могут быть вполне себе умными!
Также следует использовать сторонние функциональные библиотеки (например, toolz), которые обеспечивают более оптимальную композиционность функций.
Как вариант, использовать декларативные включения в список, включения в словарь и включения в множество в качестве замены функций и , хотя эта рекомендация является факультативной.
И применять архитектурный шаблон «немутируемое ядро — мутируемая оболочка» (aka «функциональное ядро — императивная оболочка»), который позволяет выносить мутацию во вне и производить ее на границах приложения.
Дескрипторы
Дескрипторы — объекты, которые умеют выполнять произвольный код, когда с ними происходят какие-то действия: доступ, изменение или удаление. Пример:
Дескриптор определяет методы, которые вызываются в момент, когда происходит доступ (или удаление или редактирование) инстанса дескриптора как атрибута другого класса.
Всё это работает благодаря методу : он находит нужный атрибут и проверяет, есть ли у него метод . Если есть — вызывает его, если нет — возвращает сам атрибут.
Именно через механизм дескрипторов осуществляется доступ к атрибутам класса и инстанса. , , и тоже работают благодаря дескрипторам.
Объекты
Объект является экземпляром класса. Мы можем взять класс Shark, определенный выше, и использовать его для создания объекта или экземпляра класса.
Создадим объект Shark с именем sammy:
sammy = Shark()
Мы инициализировали объект sammy как экземпляр класса, установив его равным Shark().
Теперь используем два метода с объектом Shark sammy:
sammy = Shark() sammy.swim() sammy.be_awesome()
Объект sammy использует два метода — swim() и be_awesome(). Мы вызвали их с помощью оператора «.», который ссылается на атрибут объекта. В этом случае атрибут является методом, и он вызывается с круглыми скобками.
Поскольку ключевое слово self было параметром методов класса Shark, объект sammy передается методам. Параметр self позволяет методам ссылаться на атрибуты объекта.
Но когда мы вызываем методы, в круглых скобках ничего не передается. Объект sammy автоматически передается с помощью оператора «.» (точки).
Добавим объект в программе:
shark.py
classShark: defswim(self): print("The shark is swimming.") defbe_awesome(self): print("The shark is being awesome.") defmain(): sammy = Shark() sammy.swim() sammy.be_awesome() if __name__ == "__main__": main()
Запустим программу, чтобы узнать, что она делает:
python shark.py Вывод The shark is swimming. The shark is being awesome.
Объект sammy вызывает два метода в функции main(), указывая запустить эти методы.
Ошибки при работе со списками Python
Одна из основных ошибок при работе со списками, это ошибка индексирования. Например, вы пытаетесь вернуть элемент с индексом 5. В результате Python при работе со списком не находит элемент с этим индексом, происходит ошибка индексирования.
>>> cars =
>>> cars Traceback (most recent call last):
File «<stdin>», line 1, in <module>
IndexError: list index out of range
Индекс должен быть целым числом или выражением, дающим целочисленный результат. При попытке использования нецелочисленного индекса происходит ошибка TypeError. Так же ошибка индексирования образуется при попытке обратиться к элементу пустого списка.
Включение в последовательность
Операции отображения и фильтрации встречаются так часто, что во многих языках программирования предлагаются способы написания этих выражений в более простых формах. Например, в языке Python возвести список чисел в квадрат можно следующим образом:
Python поддерживает концепцию под названием «включение в последовательность» (от англ. comprehension, в информатике эта операция так же называется описанием последовательности), которая суть изящный способ преобразования одной последовательности в другую. Во время этого процесса элементы могут быть условно включены и преобразованы заданной функцией. Вот один из вариантов общего формата операции включения в список:
В данном общем формате выражение – это выражение или функция с участием переменной, которые возвращают значение, переменная – это элемент последовательности, список – это обрабатываемый список, и выражение2 – это логическое выражение или предикативная функция с участием переменной. Чтобы все стало понятно, приведем простой пример возведения список в квадрат без условия:
Приведенное выше включение в список эквивалентно следующему ниже фрагменту программного кода:
Такая форма записи называется синтаксическим сахаром, т.е. добавленная синтаксическая конструкция, позволяющая записывать выражения в более простых и кратких формах. Неплохой аспект конструкций включения в последовательность состоит еще и в том, что они легко читаются на обычном языке, благодаря чему программный код становится чрезвычайно понятным.
В конструкции включения в последовательность используется математическая запись построения последовательности. Такая запись в теории множеств и логике называется определением интенсионала множества и описывает множество путем определения условия, которое должно выполняться для всех его членов. В сущности, в терминах этих областей науки, выполняя данную операцию в Python, мы «описываем интенсионал» соответственно списка, словаря, множества и итерируемой последовательности. Ниже приведены примеры описания интенсионала соответственно списка, словаря, множества и итерируемой последовательности.
Таблица 1. Формы описания интенсионала
Выражение |
Описание |
Описание списка |
|
Описание словаря |
|
Описание множества |
|
Описание последовательности. Такая форма записи создает генератор последовательности. Генератор – это объект, который можно последовательно обойти (обычно при помощи инструкции ), но чьи значения предоставляются только тогда, когда они требуются, используя ленивое вычисление. |
Отметим, что приведенные в таблице выражения (за исключением описания словаря) отличаются только ограничивающими символами: квадратные скобки применяются для описания списка, фигурные скобки – для описания словаря или множества и круглые скобки – для описания итерируемой последовательности.
Таким образом, примеры из разделов о функциях и легко можно переписать с использованием включения в последовательность. Например, в строке 3 приведенного ниже интерактивного сеанса вместо функции применена операция включения в список:
Обратите внимание на квадратные скобки в определении – они сигнализируют, что в результате этой операции будет создан список. Также стоит обратить внимание, что при использовании в данной конструкции нескольких последовательностей применяется встроенная функция , которая в данном случае объединяет соответствующие элементы каждой последовательности в двухэлементные кортежи
(Если бы последовательностей было три, то они объединялись бы в кортежи из трех элементов и т.д.)
Включение в список применено и в приведенном ниже примере вместо функции :
Квадратные скобки в определении сигнализируют, что в результате этой операции будет создан список. Какой способ обработки последовательностей применять – с использованием функций более высокого порядка или включений, зачастую является предметом личных предпочтений.