match (дописать полностью когда появятся словари)??
regex
строки (done)
with as, in, not, (done)
file in/out
args?
date/time
локальная переменная перекрывает глобальную (при ее изменении будет меняться локально созданный объект)
Если же мы хотим изменить в локальной функции глобальную переменную, а не определить локальную, то необходимо использовать ключевое слово global
:
name = "Tom"
def say_hi():
global name
name = "Bob" # изменяем значение глобальной переменной
print("Hello", name)
say_hi() # Hello Bob
Выражение nonlocal
прикрепляет идентификатор к переменной из ближайщего окружающего контекста (за исключением глобального контекста). Обычно nonlocal
применяется во вложенных функциях, когда надо прикрепить идентификатор за переменной или параметром окружающей внешней функции. Рассмотрим ситуацию, где это выражение может пригодиться:
def outer(): # внешняя функция
n = 5
def inner(): # вложенная функция
n = 25 #создаваться новая переменная n,
#которая скроет переменную n из окружающей внешней
# функции outer и получит значение 25
print(n)
inner() # 25
print(n)
outer() # 5
# 25 - inner
# 5 - outer
def outer(): # внешняя функция
n = 5
def inner(): # вложенная функция
nonlocal n # указываем, что n - это переменная из окружающей
# функции присваиваем значение 25 outer::n
n = 25
print(n)
inner() # 25
print(n)
outer()
# 25 25
Лямбда-выражения в языке Python представляют небольшие анонимные функции, которые определяются с помощью оператора lambda
. Формальное определение лямбда-выражения:
lambda [параметры] : инструкция
message = lambda: print("hello")
message() # output: hello
sum = lambda a, b: a + b
print(sum(4, 5)) # 9
lambda
-выражения можно так же передавать в качестве параметров функции и (или) возвращать из функции (return
)
Замыкание (closure) представляет функцию, которая запоминает свое лексическое окружение даже в том случае, когда она выполняется вне своей области видимости.
Технически замыкание включает три компонента:
-
внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные и параметры - лексическое окружение
-
переменные и параметры (лексическое окружение), которые определены во внешней функции
-
вложенная функция, которая использует переменные и параметры внешней функции
Для определения замыканий в Python применяются локальные функции:
def outer(): # внешняя функция
n = 5 # лексическое окружение - локальная переменная
def inner(): # локальная функция
nonlocal n
n += 1 # операции с лексическим окружением
print(n)
return inner
fn = outer() # fn = inner, так как функция outer возвращает функцию inner
# вызываем внутреннюю функцию inner
fn() # 6
fn() # 7
fn() # 8
Здесь функция outer
определяет локальную переменную n
- это и есть лексическое окружение для внутренней функции:
Внутри функции outer определена внутренняя функция - локальная функция inner, которая обращается к своему лексическому окружению - переменной n
- увеличивает ее значение на единицу и выводит на консоль.
Переменная fn
и представляет собой замыкание, то есть объединяет две вещи: функцию и окружение, в котором функция была создана. И несмотря на то, что мы получили локальную функцию и можем ее вызывать вне ее окружающей функции, в которой она определена, тем не менее она запомнила свое лексическое окружение и может к нему обращаться и изменять, что мы увидим по консольному выводу.
Кроме внешних переменных к лексическому окружению также относятся параметры окружающей функции. Рассмотрим использование параметров:
def multiply(n):
def inner(m): return n * m
return inner
fn = multiply(5)
print(fn(5)) # 25
print(fn(6)) # 30
print(fn(7)) # 35
Здесь внешняя функция - multiply
возвращает функцию, которая принимает число и возвращает число.
Вызов функции multiply()
возвращает локальную функцию inner.
Эта функция запоминает окружение, в котором она была создана, в частности, значение параметра n
. Кроме того, сама принимает параметр и возвращает произведение параметров n
и m
.
В итоге при вызове функции multiply
определяется переменная fn
, которая получает локальную функцию inner
и ее лексическое окружение - значение параметра n
:
Декораторы в Python представляют функцию, которая в качестве параметра получает функцию и в качестве результата также возвращает функцию. Декораторы позволяют модифицировать выполняемую функцию, значения ее параметров и ее результат без изменения исходного кода этой функции.
Рассмотрим простейший пример:
# определение функции декоратора
def select(input_func):
def output_func(): # определяем функцию,
#которая будет выполняться
#вместо оригинальной
# перед выводом оригинальной функции выводим всякую звездочки
print("*****************")
input_func() # вызов оригинальной функции
# после вывода оригинальной функции выводим всякую звездочки
print("*****************")
return output_func # возвращаем новую функцию
# определение оригинальной функции
@select # применение декоратора select
def hello():
print("Hello METANIT.COM")
# вызов оригинальной функции
hello()
#ouput:
#*****************
#Hello METANIT.COM
#*****************
Вначале определяется собственно функция декоратора, которая в данном случае называется select()
. В качестве параметра декоратор получает функцию (в данном случае параметр input_func
), к которой этот декоратор будет применяться.
Результатом декоратора в данном случае является локальная функция output_func
, в которой вызывается входная функция input_func. Для простоты здесь перед и после
вызыва input_func для красоты просто выводим набор символов "#".
Далее определяется стандартная функция, к которой применяется декоратор - в данном случае это функция hello
, которая просто выводит на консоль некоторую строку.
Для применения декоратора перед определением функции указывается символ @
, после которого идет имя декоратора. То есть в данном случае к функции hello() применяется декоратор select().
Поскольку к этой функции применяется декоратор select, то в результате функциия hello передается в декоратор select()
в качестве параметра input_func
.
И поскольку декоратор возвращает новую функцию - output_func, то
фактически в данном случае будет выполняться именно эта функция output_func()
В итоге мы получим следующий консольный вывод:
*****************
Hello METANIT.COM
*****************
Декоратор может перехватывать передаваемые в функцию аргументы:
# определение функции декоратора
def check(input_func):
def output_func(*args):
name = args[0]
age = args[1] # получаем значение второго параметра
if age < 0: age = 1
input_func(name, age) # передаем функции значения для параметров
#либо input_func(*args)
return output_func
# определение оригинальной функции
@check
def print_person(name, age):
print(f"Name: {name} Age: {age}")
# вызов оригинальной функции
print_person("Tom", 38)
print_person("Bob", -5)
Здесь функция print_person()
принимает два параметра: name (имя) и age (возраст). К этой функции применяется декоратор check()
В декораторе check
возвращается локальная функция output_func()
, которая принимает некоторый набор значений в виде параметра *args
- это те
значения, которые передаются в оригинальную функцию, к которой применяется декоратор. То есть в данном случае *args
будет содержать значения параметров name и age.
args фактически представляет набор значений, и, используя индексы, мы можем получить значения параметров по позиции и что-то с ними сделать. Так, здесь, если значение возраста меньше 0, то устанавливаем 1. Затем передаем эти значения в вызов функции.
Начиная с версии 3.10 в языке Python появилась такая функциональность как pattern matching (сопоставление шаблонов). Pattern matching представляет применение конструкции match, которая позволяет сопоставить выражение с некоторым шаблоном. И если выражение соответствует шаблону, то выполняются определенные действия. match также позволяет извлечь данные из составных типов и применить действия к различным частям объектов.
Конструкция match
имеет следующее формальное определение:
match выражение:
case шаблон_1:
действие_1
case шаблон_2:
действие_2
................
case шаблон_N:
действие_N
case _:
действие_по_умолчанию
также можно определить блок case, который позволяет сравнивать сразу с несколькими знечениями. В этом случае значения разделяются вертикальной чертой:
def print_hello(language):
match language:
case "russian":
print("Привет")
case "american english" | "british english" | "english":
print("Hello")
case _:
print("Undefined")
print_hello("english") # Hello
print_hello("american english") # Hello
print_hello("spanish") # Undefined
message = "Hello World!"
print(message) # Hello World!
name = 'Tom'
print(name) # Tom
Если строка длинная, ее можно разбить на части и разместить их на разных строках кода. В этом случае вся строка заключается в круглые скобки, а ее отдельные части - в кавычки:
text = ("Laudate omnes gentes laudate "
"Magnificat in secula ")
print(text)
Если же мы хотим определить многострочный текст, то такой текст заключается в тройные двойные или одинарные кавычки:
'''
Это комментарий
'''
text = '''Laudate omnes gentes laudate
Magnificat in secula
Et anima mea laudate
Magnificat in secula
'''
print(text)
При использовани тройных одинарных кавычек не стоит путать их с комментариями: если текст в тройных одинарных кавычках присваивается переменной, то это строка, а не комментарий.
Строка может содержать ряд специальных символов - управляющих последовательностей или escape-последовательности. Некоторые из них:
- : позволяет добавить внутрь строки слеш
- ': позволяет добавить внутрь строки одинарную кавычку
- ": позволяет добавить внутрь строки двойную кавычку
- \n: осуществляет переход на новую строку
- \t: добавляет табуляцию (4 отступа)
Python позволяет встравивать в строку значения других переменных. Для этого внутри строки переменные размещаются в фигурных скобках {}, а перед всей строкой ставится символ f: В данном случае на место {userName} будет вставляться значение переменной userName. Аналогично на вместо {userAge} будет вставляться значение переменной userAge.
userName = "Tom"
userAge = 37
user = f"name: {userName} age: {userAge}"
print(user) # name: Tom age: 37
И мы можем обратиться к отдельным символам строки по индексу в квадратных скобках:
string = "hello world"
c0 = string[0] # h
print(c0)
c6 = string[6] # w
print(c6)
c11 = string[11] # ошибка IndexError: string index out of range
print(c11)
Индексация начинается с нуля, поэтому первый символ строки будет иметь индекс 0. А если мы попытаемся обратиться к индексу, которого нет в строке, то мы получим исключение IndexError. Например, в случае выше длина строки 11 символов, поэтому ее символы будут иметь индексы от 0 до 10.
Чтобы получить доступ к символам, начиная с конца строки, можно использовать отрицательные индексы. Так, индекс -1 будет представлять последний символ, а -2 - предпоследний символ и так далее:
string = "hello world"
c1 = string[-1] # d
print(c1)
c5 = string[-5] # w
print(c5)
С помощью цикла for можно перебрать все символы строки:
string = "hello world"
for char in string:
print(char)
При необходимости мы можем получить из строки не только отдельные символы, но и подстроку. Для этого используется следующий синтаксис:
-
string[:end]
: eизвлекается последовательность символов начиная с 0-го индекса по индекс end (не включая) -
string[start:end]
: извлекается последовательность символов начиная с индекса start по индекс end (не включая) -
string[start:end:step]
: извлекается последовательность символов начиная с индекса start по индекс end (не включая) через шаг step
Используем все варианты получения подстроки:
string = "hello world"
# с 0 до 5 индекса
sub_string1 = string[:5]
print(sub_string1) # hello
# со 2 до 5 индекса
sub_string2 = string[2:5]
print(sub_string2) # llo
# с 2 по 9 индекса через один символ
sub_string3 = string[2:9:2]
print(sub_string3) # lowr
Одной из самых распространенных операций со строками является их объединение или конкатенация. Для объединения строк применяется операция сложения:
name = "Tom"
age = 33
info = "Name: " + name + " Age: " + str(age)
print(info) # Name: Tom Age: 33
Для повторения строки определенное количество раз применяется операция умножения:
print("a" * 3) # aaa
print("he" * 4) # hehehehe
Особо следует сказать о сравнении строк. При сравнении строк принимается во внимание символы и их регистр. Так, цифровой символ условно меньше, чем любой алфавитный символ. Алфавитный символ в верхнем регистре условно меньше, чем алфавитные символы в нижнем регистре. Например:
str1 = "1a"
str2 = "aa"
str3 = "Aa"
print(str1 > str2) # False, так как первый символ в str1 - цифра
print(str2 > str3) # True, так как первый символ в str2 - в нижнем регистре
Поэтому строка "1a" условно меньше, чем строка "aa". Вначале сравнение идет по первому символу. Если начальные символы обоих строк представляют цифры, то меньшей считается меньшая цифра, например, "1a" меньше, чем "2a".
Если начальные символы представляют алфавитные символы в одном и том же регистре, то смотрят по алфавиту. Так, "aa" меньше, чем "ba", а "ba" меньше, чем "ca".
Если первые символы одинаковые, в расчет берутся вторые символы при их наличии.
Зависимость от регистра не всегда желательна, так как по сути мы имеем дело с одинаковыми строками. В этом случае перед сравнением мы можем привести обе строки к одному из регистров.
Функция lower()
приводит строку к нижнему регистру, а функция upper()
- к верхнему.
str1 = "Tom"
str2 = "tom"
print(str1 == str2) # False - строки не равны
print(str1.lower() == str2.lower()) # True
Поскольку строка содержит символы Unicode, то с помощью функции ord()
мы можем получить числовое значение для символа в кодировке Unicode:
print(ord("A")) # 65
Для получения длины строки можно использовать функцию len()
:
string = "hello world"
length = len(string)
print(length) # 11
С помощью выражения term in string
можно найти подстроку term в строке string. Если подстрока найдена, то выражение вернет значение True
, иначе возвращается значение False
:
string = "hello world"
exist = "hello" in string
print(exist) # True
exist = "sword" in string
print(exist) # False
The following code:
with EXPRESSION as TARGET:
SUITE
is semantically equivalent to:
manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not exit(manager, *sys.exc_info()):
raise
finally:
if not hit_except:
exit(manager, None, None, None)
Позволяет взаимодействовать с объектом, возвращемым EXPRESSION
через 'ссылку' TARGET
, правильно передает и удаляет объект (вызывает деструктор __exit__
)
With as
это конструкция, которая способна выполниться сто процентов. Что это означает? Если при работе с файлами мы используем метод open, то дополнительно нам необходимо еще и закрывать файл, иначе это чревато последствиями.
При работе с
With as
мы можем быть уверены в том, что файл точно будет закрыт, даже в случае неправильного выполнения функции.
with open('test.txt', 'wt', encoding='utf-8') as inFile: # Открытие файла words = input() # Получение данных от пользователя inFile.write (words) # Запись данных
Также эта конструкция удобна тем, что вам не надо помнить про закрытие файла, так > как это выполняется автоматически.
Оператор in возвращает True
если в некотором наборе значений есть определенное значение. Он имеет следующую форму:
значение in набор_значений
Например, строка представляет набор символов. И с помощью оператора in
мы можем проверить, есть ли в ней какая-нибудь подстрока:
message = "hello world!"
hello = "hello"
print(hello in message) # True - подстрока hello есть в строке "hello world!"
gold = "gold"
print(gold in message) # False - подстроки "gold" нет в строке "hello world!"
Если нам надо наоборот проверить, нет ли в наборе значений
какого-либо значения, то мы може использовать модификацию оператора - not in
.
Она возвращает True
, если в наборе значений НЕТ определенного значения:
message = "hello world!"
hello = "hello"
print(hello not in message) # False
gold = "gold"
print(gold not in message) # True
Класс определяется с помощью ключевого слова class
:
class название_класса:
атрибуты_класса
методы_класса
Для создания объекта применяется специальная функция - конструктор, которая называется по имени класса и которая возвращает объект класса.
Пример:
class Person:
pass
tom = Person() # определение объекта tom
Методы класса фактически представляют функции, которые определенны внутри класса и которые определяют его поведение.При определении методов любого класса следует учитывать, что все они должны принимать в качестве первого параметра ссылку на текущий объект, который согласно условностям называется self
. Через эту ссылку внутри класса мы можем обратиться к функциональности текущего объекта. Но при самом вызове метода этот параметр не учитывается.
Если метод должен принимать другие параметры, то они определяются после параметра self
, и при вызове подобного метода для них необходимо передать значения
Например, определим класс Person с одним методом:
class Person: # определение класса Person
def say_hello(self):
print("Hello")
tom = Person()
tom.say_hello() # Hello
Через ключевое слово self можно обращаться внутри класса к функциональности текущего объекта:
self.атрибут # обращение к атрибуту
self.метод # обращение к методу
Так же при помощи self
в конструкторе можно объявить атрибут (поле) класса.
И вообще атрибуты (поля) класса можно генерить прямо в рантайме просто обратившись к ним (классный способ выстрелить себе в ногу)
Для создания объекта класса используется конструктор. Так, выше когда мы создавали объекты класса Person, мы использовали конструктор по умолчанию, который не принимает параметров и который неявно имеют все классы:
Однако мы можем явным образом определить в классах конструктор с помощью специального метода, который называется __init__()
(по два прочерка с каждой стороны). К примеру, изменим класс Person, добавив в него конструктор:
class Person:
# конструктор
def __init__(self):
print("Создание объекта Person")
def say_hello(self):
print("Hello")
tom = Person() # Создание объекта Person
tom.say_hello() # Hello
По умолчанию атрибуты в классах являются общедоступными, а это значит, что из любого места программы мы можем получить атрибут объекта и изменить его.
Касательно инкапсуляции непосредственно в языке программирования Python скрыть атрибуты класса можно сделав их приватными или закрытыми и ограничив доступ к ним через специальные методы, которые еще называются свойствами.
Изменим выше определенный класс, определив в нем свойства:
class Person:
def __init__(self, name):
self.__name = name # устанавливаем имя
self.__age = 1 # устанавливаем возраст
def set_age(self, age):
if 1 < age < 110:
self.__age = age
else:
print("Недопустимый возраст")
def get_age(self):
return self.__age
def get_name(self):
return self.__name
def display_info(self):
print(f"Имя: {self.__name}\tВозраст: {self.__age}")
tom = Person("Tom")
tom.display_info() # Имя: Tom Возраст: 1
tom.set_age(-3486) # Недопустимый возраст
tom.set_age(25)
tom.display_info() # Имя: Tom Возраст: 25
Для создания приватного атрибута в начале его наименования ставится двойной прочерк: self.__name
. К такому атрибуту мы сможем обратиться только из того же класса. Но не сможем обратиться вне этого класса.
А попытка получить его значение приведет к ошибке выполнения (если ранее не была определена переменная __age
).
Выше мы рассмотрели, как создавать свойства. Но Python имеет также еще один - более элегантный способ определения свойств. Этот способ предполагает использование аннотаций, которые предваряются символом @
.
Для создания свойства-геттера над свойством ставится аннотация @property
.
Для создания свойства-сеттера над свойством устанавливается аннотация имя_свойства_геттера.setter
.
Перепишем класс Person с использованием аннотаций:
class Person:
def __init__(self, name):
self.__name = name # устанавливаем имя
self.__age = 1 # устанавливаем возраст
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if 1 < age < 110:
self.__age = age
else:
print("Недопустимый возраст")
@property
def name(self):
return self.__name
def display_info(self):
print(f"Имя: {self.__name}\tВозраст: {self.__age}")
tom = Person("Tom")
tom.display_info() # Имя: Tom Возраст: 1
tom.age = -3486 # Недопустимый возраст
print(tom.age) # 1
tom.age = 36
tom.display_info()
Во-первых, стоит обратить внимание, что свойство-сеттер определяется после свойства-геттера.
Во-вторых, и сеттер, и геттер называются одинаково - age
. И поскольку геттер называется age
, то над сеттером устанавливается аннотация @age.setter
.
После этого, что к геттеру, что к сеттеру, мы обращаемся через выражение tom.age
.
Ключевыми понятиями наследования являются подкласс и суперкласс. Подкласс наследует от суперкласса все публичные атрибуты и методы. Суперкласс еще называется базовым (base class) или родительским (parent class), а подкласс - производным (derived class) или дочерним (child class). Синтаксис для наследования классов выглядит следующим образом:
class подкласс (суперкласс):
методы_подкласса
возможно множественное наследование, в таком случае суперклассы перечисляют через запятую
Кроме атрибутов объектов в классе можно определять атрибуты классов. Подобные атрибуты определяются в виде переменных уровня класса. Например:
class Person:
type = "Person"
description = "Describes a person"
print(Person.type) # Person
print(Person.description) # Describes a person
Person.type = "Class Person"
print(Person.type) # Class Person
Здесь в классе Person
определено два атрибута: type
, который хранит имя класса, и description
, который хранит описание класса.
Для обращения к атрибутам класса мы можем использовать имя класса, например: Person.type
, и, как и атрибуты объекта, мы можем получать и изменять их значения.
Подобные атрибуты являются общими для всех объектов класса:
class Person:
type = "Person"
def __init__(self, name):
self.name = name
tom = Person("Tom")
bob = Person("Bob")
print(tom.type) # Person
print(bob.type) # Person
# изменим атрибут класса
Person.type = "Class Person"
print(tom.type) # Class Person
print(bob.type) # Class Person
Атрибут объекта "главнее" атрибута класса, то есть если для какого-то объекта задан атрибут совпадающий по имени с атрибутом класса - при обращении python вернет атрибут класса
Кроме обычных методов класс может определять статические методы. Такие методы предваряются аннотацией @staticmethod
и относятся в целом к классу. Статические методы обычно определяют поведение, которое не зависит от конкретного объекта:
class Person:
__type = "Person"
@staticmethod
def print_type():
print(Person.__type)
Person.print_type() # Person - обращение к статическому методу через имя класса
tom = Person()
tom.print_type() # Person - обращение к статическому методу через имя объекта
Начиная с 3-й версии в языке программирования Python все классы неявно имеют один общий суперкласс - object
и все классы по умолчанию наследуют его методы.
Одним из наиболее используемых методов класса object является метод __str__()
. Когда необходимо получить строковое представление объекта или вывести объект в виде строки, то Python как раз вызывает этот метод. И при определении класса хорошей практикой считается переопределение этого метода.
К примеру, возьмем класс Person и выведем его строковое представление:
class Person:
def __init__(self, name, age):
self.name = name # устанавливаем имя
self.age = age # устанавливаем возраст
def display_info(self):
print(f"Name: {self.name} Age: {self.age}")
tom = Person("Tom", 23)
print(tom)
При запуске программа выведет что-то наподобие следующего:
<__main__.Person object at 0x10a63dc00>
Это не очень информативная информация об объекте. Мы, конечно, можем выйти из положения, определив в классе Person
дополнительный метод, который выводит данные объекта - в примере выше это метод display_info
.
Но есть и другой выход - определим в классе Person метод __str__()
(по два подчеркивания с каждой стороны):
class Person:
def __init__(self, name, age):
self.name = name # устанавливаем имя
self.age = age # устанавливаем возраст
def display_info(self):
print(self)
# print(self.__str__()) # или так
def __str__(self):
return f"Name: {self.name} Age: {self.age}"
tom = Person("Tom", 23)
print(tom) # Name: Tom Age: 23
tom.display_info() # Name: Tom Age: 23
Метод __str__
должен возвращать строку. И в данном случае мы возвращаем базовую информацию о человеке. Если нам потребуется использовать эту информацию в других методах класса, то мы можем использовать выражение self.__str__()
И теперь консольный вывод будет другим:
Name: Tom Age: 23
Name: Tom Age: 23
string = "hello"
number = int(string)
print(number)
При выполнении этого скрипта будет выброшено исключение ValueError
, так как строку "hello" нельзя преобразовать в число:
ValueError: invalid literal for int() with base 10: 'hello'
При возникновении исключения работа программы прерывается, и чтобы избежать подобного поведения и обрабатывать исключения в Python есть конструкция try..except
.
try:
инструкции
except [Тип_исключения]:
инструкции
При обработке исключений также можно использовать необязательный блок finally
. Отличительной особенностью этого блока является то, что он выполняется вне зависимости, было ли сгенерировано исключение:
try:
number = int(input("Введите число: "))
print("Введенное число:", number)
except:
print("Преобразование прошло неудачно")
finally:
print("Блок try завершил выполнение")
print("Завершение программы")
В примере выше обрабатывались сразу все исключения, которые могут возникнуть в коде. Однако мы можем конкретизировать тип обрабатываемого исключения, указав его после слова except
:
try:
number = int(input("Введите число: "))
print("Введенное число:", number)
except ValueError:
print("Преобразование прошло неудачно")
print("Завершение программы")
В данном случае блок execpt
обрабатывает только исключения типа ValueError
, которые могут возникнут при неудачном преобразовании строки в число.
В Python есть следующие базовые типы исключений:
-
BaseException
: базовый тип для всех встроенных исключений -
Exception:
базовый тип, который обычно применяется для создания своих типов исключений -
ArithmeticError
: базовый тип для исключений, связанных с арифметическими операциями (OverflowError, ZeroDivisionError, FloatingPointError). -
BufferError
: тип исключения, которое возникает при невозможности выполнить операцию с буффером -
LookupError
: базовый тип для исключений, которое возникают при обращении в коллекциях по некорректному ключу или индексу (например, IndexError, KeyError)
От этих классов наследуются все конкретные типы исключений. В Python обладает довольно большим списком встроенных исключений. Весь этот список можно посмотреть в документации. Перечислю только некоторые наиболее часто встречающиеся:
-
IndexErro
r: исключение возникает, если индекс при обращении к элементу коллекции находится вне допустимого диапазона -
KeyError
: возникает, если в словаре отсутствует ключ, по которому происходит обращение к элементу словаря. -
OverflowError
: возникает, если результат арифметической операции не может быть представлен текущим числовым типом (обычно типом float). -
RecursionError
: возникает, если превышена допустимая глубина рекурсии. -
TypeError
: возникает, если операция или функция применяется к значению недопустимого типа. -
ValueError
: возникает, если операция или функция получают объект корректного типа с некорректным значением. -
ZeroDivisionError
: возникает при делении на ноль. -
NotImplementedError
: тип исключения для указания, что какие-то методы класса не реализованы -
ModuleNotFoundError
: возникает при при невозможности найти модуль при его импорте директивойimport
-
OSError
: тип исключений, которые генерируются при возникновении ошибок системы (например, невозможно найти файл, память диска заполнена и т.д.)
И если ситуация такова, что в программе могут быть сгенерированы различные типы исключений, то мы можем их обработать по отдельности, используя дополнительные выражения except
. И при возникновении исключения Python будет искать нужный блок except, который обрабатывает данный тип исключения:
try:
number1 = int(input("Введите первое число: "))
number2 = int(input("Введите второе число: "))
print("Результат деления:", number1/number2)
except ValueError:
print("Преобразование прошло неудачно")
except ZeroDivisionError:
print("Попытка деления числа на ноль")
except BaseException:
print("Общее исключение")
print("Завершение программы")
С помощью оператора as
мы можем передать всю информацию об исключении в переменную, которую затем можно использовать в блоке except
:
try:
number = int(input("Введите число: "))
print("Введенное число:", number)
except ValueError as e:
print("Сведения об исключении", e)
print("Завершение программы")
Иногда возникает необходимость вручную сгенерировать то или иное исключение. Для этого применяется оператор raise
. Например, сгенерируем исключение
try:
number1 = int(input("Введите первое число: "))
number2 = int(input("Введите второе число: "))
if number2 == 0:
raise Exception("Второе число не должно быть равно 0")
print("Результат деления двух чисел:", number1/number2)
except ValueError:
print("Введены некорректные данные")
except Exception as e:
print(e)
print("Завершение программы")
Введите первое число: 1
Введите второе число: 0
Второе число не должно быть равно 0
Завершение программы
В языке Python мы не ограничены только встроенными типами исключений и можем, Применяя наследование, при необходимости создавать свои типы исключений. Например, возьмем следующий класс Person:
class PersonAgeException(Exception):
def __init__(self, age, minage, maxage):
self.age = age
self.minage = minage
self.maxage = maxage
def __str__(self):
return f"Недопустимое значение: {self.age}. " \
f"Возраст должен быть в диапазоне от {self.minage} до {self.maxage}"
class Person:
def __init__(self, name, age):
self.__name = name # устанавливаем имя
minage, maxage = 1, 110
if minage < age < maxage: # устанавливаем возраст, если передано корректное значение
self.__age = age
else: # иначе генерируем исключение
raise PersonAgeException(age, minage, maxage)
def display_info(self):
print(f"Имя: {self.__name} Возраст: {self.__age}")
вроде как список (но асимптотика подойдет и массиву)
Operation | Average Case | Amortized Worst Case |
---|---|---|
Copy | O(n) | O(n) |
Append[1] | O(1) | O(1) |
Pop last | O(1) | O(1) |
Pop intermediate[2] | O(n) | O(n) |
Insert | O(n) | O(n) |
Get Item | O(1) | O(1) |
Set Item | O(1) | O(1) |
Delete Item | O(n) | O(n) |
Iteration | O(n) | O(n) |
Get Slice | O(k) | O(k) |
Del Slice | O(n) | O(n) |
Set Slice | O(k+n) | O(k+n) |
Extend[1] | O(k) | O(k) |
Sort | O(n log n) | O(n log n) |
Multiply | O(nk) | O(nk) |
x in s | O(n) | |
min(s), max(s) | O(n) | |
Get Length | O(1) | O(1) |
Для создания списка применяются квадратные скобки []
, внутри которых через запятую перечисляются элементы списка. Например, определим список чисел:
numbers = [1, 2, 3, 4, 5]
Также для создания списка можно использовать функцию-конструктор list()
. Оба этих определения списка аналогичны - они создают пустой список:
numbers1 = []
numbers2 = list()
Список необязательно должен содержать только однотипные объекты. Мы можем поместить в один и тот же список одновременно строки, числа, объекты других типов данных:
objects = [1, 2.6, "Hello", True]
Конструктор list
может принимать набор значений, на основе которых создается cписок:
numbers1 = [1, 2, 3, 4, 5]
numbers2 = list(numbers1) # КОНСТРУКТОР КОПИРОВАНИЯ!!!!
print(numbers2) # [1, 2, 3, 4, 5]
Если необходимо создать список, в котором повторяется одно и то же значение несколько раз, то можно использовать символ звездочки *
, то есть фактически применить операцию умножения к уже существующему списку:
numbers = [5] * 6 # 6 раз повторяем 5
print(numbers) # [5, 5, 5, 5, 5, 5]
Для обращения к элементам списка надо использовать индексы, которые
представляют номер элемента в списка. Индексы начинаются с нуля. То есть первый элемент будет иметь индекс 0, второй элемент - индекс 1 и так далее.
Для обращения к элементам с конца можно использовать отрицательные индексы, начиная с -1. То есть у последнего элемента будет индекс -1, у предпоследнего - -2 и так далее.
people = ["Tom", "Sam", "Bob"]
# получение элементов с начала списка
print(people[0]) # Tom
print(people[1]) # Sam
print(people[2]) # Bob
print(people[-2]) # Sam
print(people[-1]) # Bob
print(people[-3]) # Tom
Для изменения элемента списка достаточно присвоить ему новое значение:
people = ["Tom", "Sam", "Bob"]
people[1] = "Mike" # изменение второго элемента
print(people[1]) # Mike
print(people) # ["Tom", "Mike", "Bob"]
Python позволяет разложить список на отдельные элементы:
people = ["Tom", "Bob", "Sam"]
tom, bob, sam = people
print(tom) # Tom
print(bob) # Bob
print(sam) # Sam
В данном случае переменным tom, bob и sam последовательно присваиваются элементы из списка people. Однако следует учитывать, что количество переменных должно быть равно числу элементов присваиваемого списка.
Для перебора элементов можно использовать как цикл for, так и цикл while.
Перебор с помощью цикла for:
people = ["Tom", "Sam", "Bob"]
for person in people:
print(person)
Два списка считаются равными, если они содержат один и тот же набор элементов:
numbers1 = [1, 2, 3, 4, 5]
numbers2 = list([1, 2, 3, 4, 5])
if numbers1 == numbers2:
print("numbers1 equal to numbers2")
else:
print("numbers1 is not equal to numbers2")
Если необходимо получить какую-то определенную часть списка, то мы можем применять специальный синтаксис, который может принимать следующие формы:
-
list[:end]
: через параметр end передается индекс элемента, до которого нужно копировать (вернуть подсписок ссылкой) список -
list[start:end]
: параметр start указывает на индекс элемента, начиная с которого надо скопировать (вернуть подсписок ссылкой) элементы -
list[start:end:step]
: параметр step указывает на шаг, через который будут копироваться (возвращаться подсписок ссылкой) элементы из списка. По умолчанию этот параметр равен 1.
people = ["Tom", "Bob", "Alice", "Sam", "Tim", "Bill"]
slice_people1 = people[:3] # с 0 по 3
print(slice_people1) # ["Tom", "Bob", "Alice"]
slice_people2 = people[1:3] # с 1 по 3
print(slice_people2) # ["Bob", "Alice"]
slice_people3 = people[1:6:2] # с 1 по 6 с шагом 2
print(slice_people3) # ["Bob", "Sam", "Bill"]
Можно использовать отрицательные индексы, тогда отсчет будет идти с конца, например, -1 - предпоследний, -2 - третий сконца и так далее.
Для управления элементами списки имеют целый ряд методов. Некоторые из них:
-
append(item): добавляет элемент item в конец списка
-
insert(index, item): добавляет элемент item в список по индексу index
-
extend(items): добавляет набор элементов items в конец списка
-
remove(item): удаляет элемент item. Удаляется только первое вхождение элемента. Если элемент не найден, генерирует исключение ValueError
-
clear(): удаление всех элементов из списка
-
index(item): возвращает индекс элемента item. Если элемент не найден, генерирует исключение ValueError
-
pop([index]): удаляет и возвращает элемент по индексу index. Если индекс не передан, то просто удаляет последний элемент.
-
count(item): возвращает количество вхождений элемента item в список
-
sort([key]): сортирует элементы. По умолчанию сортирует по возрастанию. Но с помощью параметра key мы можем передать функцию сортировки.
-
reverse(): расставляет все элементы в списке в обратном порядке
-
copy(): копирует список
Кроме того, Python предоставляет ряд встроенных функций для работы со списками:
-
len(list): возвращает длину списка
-
sorted(list, [key]): возвращает отсортированный список
-
min(list): возвращает наименьший элемент списка
-
max(list): возвращает наибольший элемент списка
а еще есть операторы:
-
in - проверить вхождение элемента в список
-
del - удалить элемент (принимает на вход объект)
Если определенный элемент не найден, то методы remove и index генерируют исключение. Чтобы избежать подобной ситуации, перед операцией с элементом можно проверять его наличие с помощью ключевого слова in:
people = ["Tom", "Bob", "Alice", "Sam"]
if "Alice" in people:
people.remove("Alice")
print(people) # ["Tom", "Bob", "Sam"]
Python также поддерживает еще один способ удаления элементов списка - с помощью оператора del. В качестве параметра этому оператору передается удаляемый элемент или набор элементов:
people = ["Tom", "Bob", "Alice", "Sam", "Bill", "Kate", "Mike"]
del people[1] # удаляем второй элемент
print(people) # ["Tom", "Alice", "Sam", "Bill", "Kate", "Mike"]
Для изменения подсписка - набора элементов в списке можно использовать вышерассмотренный синтаксис [start:end]
:
nums = [10, 20, 30, 40, 50]
nums[1:4]=[11, 22]
print(nums) # [10, 11, 22, 50]
При копировании списков следует учитывать, что списки представляют изменяемый (mutable) тип, поэтому если обе переменных будут указывать на один и тот же список, то изменение одной переменной, затронет и другую переменную:
people1 = ["Tom", "Bob", "Alice"]
people2 = people1
people2.append("Sam") # добавляем элемент во второй список
# people1 и people2 указывают на один и тот же список
print(people1) # ["Tom", "Bob", "Alice", "Sam"]
print(people2) # ["Tom", "Bob", "Alice", "Sam"]
Это так называемое "поверхностное копирование" (shallow copy). И, как правило, такое поведение нежелательное. И чтобы происходило копирование элементов, но при этом переменные указывали на разные списки, необходимо выполнить глубокое копирование (deep copy). Для этого можно использовать метод copy():
people1 = ["Tom", "Bob", "Alice"]
people2 = people1.copy() # копируем элементы из people1 в people2
people2.append("Sam") # добавляем элемент ТОЛЬКО во второй список
# people1 и people2 указывают на разные списки
print(people1) # ["Tom", "Bob", "Alice"]
print(people2) # ["Tom", "Bob", "Alice", "Sam"]
Кортеж (tuple) представляет последовательность элементов, которая во многом похожа на список за тем исключением, что кортеж является неизменяемым (immutable) типом. Поэтому мы не можем добавлять или удалять элементы в кортеже, изменять его.
Для создания кортежа используются круглые скобки, в которые помещаются его значения, разделенные запятыми:
tom = ("Tom", 23)
print(tom) # ("Tom", 23)
Также для определения кортежа мы можем просто перечислить значения через запятую без применения скобок:
tom = "Tom", 23
print(tom) # ("Tom", 23)
Если вдруг кортеж состоит из одного элемента, то после единственного элемента кортежа необходимо поставить запятую:
tom = ("Tom",)
Для создания кортежа из другого набора элементов, например, из списка, можно передать список в функцию tuple()
, которая возвратит кортеж:
data = ["Tom", 37, "Google"]
tom = tuple(data)
print(tom) # ("Tom", 37, "Google")
С помощью встроенной функции len()
можно получить длину кортежа:
tom = ("Tom", 37, "Google")
print(len(tom)) # 3
Обращение к элементам в кортеже происходит также, как и в списке, по индексу. Индексация начинается также с нуля при получении элементов с начала списка и с -1 при получении элементов с конца списка:
tom = ("Tom", 37, "Google", "software developer")
print(tom[0]) # Tom
print(tom[1]) # 37
print(tom[-1]) # software developer
При необходимости мы можем разложить кортеж на отдельные переменные:****
name, age, company, position = ("Tom", 37, "Google", "software developer")
print(name) # Tom
print(age) # 37
print(position) # software developer
print(company) # Google
Как и в списках, можно получить часть кортежа в виде другого кортежа
tom = ("Tom", 37, "Google", "software developer")
# получем подкортеж с 1 по 3 элемента (не включая)
print(tom[1:3]) # (37, "Google")
# получем подкортеж с 0 по 3 элемента (не включая)
print(tom[:3]) # ("Tom", 37, "Google")
# получем подкортеж с 1 по послдений элемент
print(tom[1:]) # (37, "Google", "software developer")
Особенно удобно использовать кортежи, когда необходимо возвратить из функции сразу несколько значений. Когда функция возвращает несколько значений, фактически она возвращает в кортеж:
def get_user():
name = "Tom"
age = 22
company = "Google"
return name, age, company
user = get_user()
print(user) # ("Tom", 37, "Google")
При передаче кортежа в функцию с помощью оператора *
его можно разложить на отдельные значения, которые передаются параметрам функции:
def print_person(name, age, company):
print(f"Name: {name} Age: {age} Company: {company}")
tom = ("Tom", 22)
print_person(*tom, "Microsoft") # Name: Tom Age: 22 Company: Microsoft
bob = ("Bob", 41, "Apple")
print_person(*bob) # Name: Bob Age: 41 Company: Apple
Для перебора кортежа можно использовать стандартные циклы for и while
. С помощью цикла for:
tom = ("Tom", 22, "Google")
for item in tom:
print(item)
Как для списка с помощью выражения элемент in кортеж
можно проверить наличие элемента в кортеже:
user = ("Tom", 22, "Google")
name = "Tom"
if name in user:
print("Пользователя зовут Tom")
else:
print("Пользователь имеет другое имя")
Диапазоны или range представляют неизменяемый последовательный набор чисел. Для создания диапазов применяетя range, которая имеет следующие формы:
-
range(stop)
: возвращает все целые числа от 0 до stop -
range(start, stop)
: возвращает все целые числа в промежутке от start (включая) до stop (не включая). -
range(start, stop, step)
: возвращает целые числа в промежутке от start (включая) до stop (не включая), которые увеличиваются на значение step
Преимуществом диапазонов над стандартными списками и кортежами является то, что диапазон всегда будет занимать одно и то же небольшое количество памяти вне зависимости от того, какой набор чисел представляет этот диапазон. В действительности диапазон хранит только начальное, конечное значение и приращение.
Operation | Average Case | Amortized Worst Case |
---|---|---|
k in d | O(1) | O(n) |
Copy[3] | O(n) | O(n) |
Get Item | O(1) | O(n) |
Set Item[1] | O(1) | O(n) |
Delete Item | O(1) | O(n) |
Iteration[3] | O(n) | O(n) |
выглядит как hash-set
Словарь (dictionary) в языке Python хранит коллекцию элементов, где каждый элемент имеет уникальный ключ и ассоциированое с ним некоторое значение. Определение словаря имеет следующий синтаксис:
dictionary = { ключ1:значение1, ключ2:значение2, ....}
В фигурных скобках через запятую определяется последовательность элементов, где для каждого элемента сначала указывается ключ и через двоеточие его значение. Ключи и значения не обязаны быть однотипными (еще один хороший способ выстрелить себе в ногу)
Несмотря на то, что словарь и список - непохожие по структуре типы, но тем не менее существует возможности для отдельных видов списков преобразования их в словарь с помощью встроенной функции dict()
. Для этого список должен хранить набор вложенных списков. Каждый вложенный список должен состоять из двух элементов - при конвертации в словарь первый элемент станет ключом, а второй - значением:
users_list = [
["+111123455", "Tom"],
["+384767557", "Bob"],
["+958758767", "Alice"]
]
users_dict = dict(users_list)
print(users_dict)
Подобным образом можно преобразовать в словарь двухмерные кортежи, которые в свою очередь содержать кортежи из двух элементов:
users_tuple = (
("+111123455", "Tom"),
("+384767557", "Bob"),
("+958758767", "Alice")
)
users_dict = dict(users_tuple)
print(users_dict)
Для обращения к элементам словаря после его названия в квадратных скобках указывается ключ элемента:
dictionary[ключ]
users = {
"+11111111": "Tom",
"+33333333": "Bob",
"+55555555": "Alice"
}
# получаем элемент с ключом "+11111111"
print(users["+11111111"]) # Tom
# установка значения элемента с ключом "+33333333"
users["+33333333"] = "Bob Smith"
print(users["+33333333"]) # Bob Smith
Если при установки значения элемента с таким ключом в словаре не окажется, то произойдет его добавление:
users["+4444444"] = "Sam"
Но если мы попробуем получить значение с ключом, которого нет в словаре, то Python сгенерирует ошибку KeyError:
user = users["+4444444"] # KeyError
И чтобы предупредить эту ситуацию перед обращением к элементу мы можем проверять наличие ключа в словаре с помощью выражения ключ in
словарь. Если ключ имеется в словаре, то данное выражение возвращает True
:
key = "+4444444"
if key in users:
user = users[key]
print(user)
else:
print("Элемент не найден")
Также для получения элементов можно использовать метод get, который имеет две формы:
-
get(key)
: возвращает из словаря элемент с ключом key. Если элемента с таким ключом нет, то возвращает значение None -
get(key, default)
: возвращает из словаря элемент с ключом key. Если элемента с таким ключом нет, то возвращает значение по умолчанию default
users = {
"+11111111": "Tom",
"+33333333": "Bob",
"+55555555": "Alice"
}
user1 = users.get("+55555555")
print(user1) # Alice
user2 = users.get("+33333333", "Unknown user")
print(user2) # Bob
user3 = users.get("+44444444", "Unknown user")
print(user3) # Unknown user
Для удаления элемента по ключу применяется оператор del
:
users = {
"+11111111": "Tom",
"+33333333": "Bob",
"+55555555": "Alice"
}
del users["+55555555"]
print(users) # { "+11111111": "Tom", "+33333333": "Bob"}
Но стоит учитывать, что если подобного ключа не окажется в словаре, то будет выброшено исключение KeyError. Поэтому опять же перед удалением желательно проверять наличие элемента с данным ключом.
users = {
"+11111111": "Tom",
"+33333333": "Bob",
"+55555555": "Alice"
}
key = "+55555555"
if key in users:
del users[key]
print(f"Элемент с ключом {key} удален")
else:
print("Элемент не найден")
Другой способ удаления представляет метод pop()
. Он имеет две формы:
-
pop(key)
: удаляет элемент по ключу key и возвращает удаленный элемент. Если элемент с данным ключом отсутствует, то генерируется исключение KeyError -
pop(key, default)
: удаляет элемент по ключу key и возвращает удаленный элемент. Если элемент с данным ключом отсутствует, то возвращается значение default
users = {
"+11111111": "Tom",
"+33333333": "Bob",
"+55555555": "Alice"
}
key = "+55555555"
user = users.pop(key)
print(user) # Alice
user = users.pop("+4444444", "Unknown user")
print(user) # Unknown user
Если необходимо удалить все элементы, то в этом случае можно воспользоваться методом clear()
:
users.clear()
Метод copy()
копирует содержимое словаря, возвращая новый словарь:
users = {"+1111111": "Tom", "+3333333": "Bob", "+5555555": "Alice"}
students = users.copy()
print(students) # {"+1111111": "Tom", "+3333333": "Bob", "+5555555": "Alice"}
Метод update()
объединяет два словаря:
users = {"+1111111": "Tom", "+3333333": "Bob"}
users2 = {"+2222222": "Sam", "+6666666": "Kate"}
users.update(users2)
print(users) # {"+1111111": "Tom", "+3333333": "Bob", "+2222222": "Sam", "+6666666": "Kate"}
print(users2) # {"+2222222": "Sam", "+6666666": "Kate"}
При этом словарь users2 остается без изменений. Изменяется только словарь users, в который добавляются элементы другого словаря.
Для перебора словаря можно воспользоваться циклом for:
users = {
"+11111111": "Tom",
"+33333333": "Bob",
"+55555555": "Alice"
}
for key in users:
print(f"Phone: {key} User: {users[key]} ")
При переборе элементов мы получаем ключ текущего элемента и по нему можем получить сам элемент.
Другой способ перебора элементов представляет использование метода items()
:
users = {
"+11111111": "Tom",
"+33333333": "Bob",
"+55555555": "Alice"
}
for key, value in users.items():
print(f"Phone: {key} User: {value} ")
Метод items()
возвращает набор кортежей. Каждый кортеж содержит ключ и значение элемента, которые при переборе мы тут же можем получить
в переменные key и value.
Также существуют отдельно возможности перебора ключей и перебора значений. Для перебора ключей мы можем вызвать у словаря метод keys()
:
for key in users.keys():
print(key)
Для перебора только значений мы можем вызвать у словаря метод values()
:
for value in users.values():
print(value)
Operation | Average case | Worst Case | notes |
---|---|---|---|
x in s | O(1) | O(n) | |
Union s|t | O(len(s)+len(t)) | ||
Intersection s&t | O(min(len(s), len(t))) | O(len(s) * len(t)) | replace "min" with "max" if t is not a set |
Multiple intersection s1&s2&..&sn | (n-1)*O(l) where l is max(len(s1),..,len(sn)) | ||
Difference s-t | O(len(s)) | ||
s.difference_update(t) | O(len(t)) | ||
Symmetric Difference s^t | O(len(s)) | O(len(s) * len(t)) | |
s.symmetric_difference_update(t) | O(len(t)) | O(len(t) * len(s)) |
Множество (set) представляют еще один вид набора, который хранит только уникальные элементы. Для определения множества используются фигурные скобки, в которых перечисляются элементы:
users = {"Tom", "Bob", "Alice", "Tom"}
print(users) # {"Alice", "Bob", "Tom"}
Также для определения множества может применяться функция set()
, в которую передается список или кортеж элементов:
people = ["Mike", "Bill", "Ted"]
users = set(people)
print(users) # {"Mike", "Bill", "Ted"}
Для получения длины множества применяется встроенная функция len()
:
users = {"Tom", "Bob", "Alice"}
print(len(users)) # 3
Для добавления одиночного элемента вызывается метод add()
:
users = set()
users.add("Sam")
print(users)
Для удаления одного элемента вызывается метод remove(), в который передается удаляемый элемент. Но следует учитывать, что если такого элемента не окажется в множестве, то будет сгенерирована ошибка. Поэтому перед удалением следует проверять на наличие элемента с помощью оператора in:
users = {"Tom", "Bob", "Alice"}
user = "Tom"
if user in users:
users.remove(user)
print(users) # {"Bob", "Alice"}
Также для удаления можно использовать метод discard()
, который не будет генерировать исключения при отсутствии элемента:
users = {"Tom", "Bob", "Alice"}
users.discard("Tim") # элемент "Tim" отсутствует, и метод ничего не делает
print(users) # {"Tom", "Bob", "Alice"}
users.discard("Tom") # элемент "Tom" есть, и метод удаляет элемент
print(users) # {"Bob", "Alice"}
Для удаления всех элементов вызывается метод clear()
:
users.clear()
Для перебора элементов можно использовать цикл for:
users = {"Tom", "Bob", "Alice"}
for user in users:
print(user)
При переборе каждый элемент помещается в переменную user.
С помощью метода copy()
можно скопировать содержимое одного множества в другую переменную:
users = {"Tom", "Bob", "Alice"}
students = users.copy()
print(students) # {"Tom", "Bob", "Alice"}
Метод union()
объединяет два множества и возвращает новое множество:
users = {"Tom", "Bob", "Alice"}
users2 = {"Sam", "Kate", "Bob"}
users3 = users.union(users2)
print(users3) # {"Bob", "Alice", "Sam", "Kate", "Tom"}
Пересечение множеств позволяет получить только те элементы, которые есть одновременно в обоих множествах.
Метод intersection()
производит операцию пересечения множеств и возвращает новое множество:
users = {"Tom", "Bob", "Alice"}
users2 = {"Sam", "Kate", "Bob"}
users3 = users.intersection(users2)
print(users3) # {"Bob"}
Вместо метода intersection мы могли бы использовать операцию логического умножения:
users = {"Tom", "Bob", "Alice"}
users2 = {"Sam", "Kate", "Bob"}
print(users & users2) # {"Bob"}
Модификация метода - intersection_update() заменяет пересеченными элементами первое множество:
users = {"Tom", "Bob", "Alice"}
users2 = {"Sam", "Kate", "Bob"}
users.intersection_update(users2)
print(users) # {"Bob"}
Еще одна операция - разность множеств возвращает те элементы, которые есть в первом множестве, но отсутствуют во втором. Для получения разности множеств можно использовать метод difference
или операцию вычитания:
users = {"Tom", "Bob", "Alice"}
users2 = {"Sam", "Kate", "Bob"}
users3 = users.difference(users2)
print(users3) # {"Tom", "Alice"}
print(users - users2) # {"Tom", "Alice"}
Отдельная разновидность разности множеств - симметрическая разность производится с помощью метода symmetric_difference()
или с помощью операции ^
.
Она возвращает все элементы обоих множеств за исключением общих:
users = {"Tom", "Bob", "Alice"}
users2 = {"Sam", "Kate", "Bob"}
users3 = users.symmetric_difference(users2)
print(users3) # {"Tom", "Alice", "Sam", "Kate"}
users4 = users ^ users2
print(users4) # {"Tom", "Alice", "Sam", "Kate"}
Метод issubset
позволяет выяснить, является ли текущее множество подмножеством (то есть частью) другого множества:
users = {"Tom", "Bob", "Alice"}
superusers = {"Sam", "Tom", "Bob", "Alice", "Greg"}
print(users.issubset(superusers)) # True
print(superusers.issubset(users)) # False
Метод issuperset
, наоборот, возвращает True
, если текущее множество является надмножеством (то есть содержит) для другого множества:
users = {"Tom", "Bob", "Alice"}
superusers = {"Sam", "Tom", "Bob", "Alice", "Greg"}
print(users.issuperset(superusers)) # False
print(superusers.issuperset(users)) # True
Тип frozen set
является видом множеств, которое не может быть изменено. Для его создания используется функция frozenset
:
users = frozenset({"Tom", "Bob", "Alice"})
В функцию frozenset передается набор элементов - список, кортеж, другое множество.
В такое множество мы не можем добавить новые элементы, как и удалить из него уже имеющиеся. Собственно поэтому frozen set поддерживает ограниченный набор операций:
-
len(s)
: возвращает длину множества -
x in s
: возвращает True, если элемент x присутствует в множестве s -
x not in s
: возвращает True, если элемент x отсутствует в множестве s -
s.issubset(t)
: возвращает True, если t содержит множество s -
s.issuperset(t)
: возвращает True, если t содержится в множестве s -
s.union(t)
: возвращает объединение множеств s и t
-
s.intersection(t)
: возвращает пересечение множеств s и t -
s.difference(t)
: возвращает разность множеств s и t -
s.copy()
: возвращает копию множества s
Функциональность list comprehension предоставляет более краткий и лаконичный синтаксис для создания списков на основе других наборов данных. Она имеет следующий синтаксис:
newlist = [expression for item in iterable (if condition)]
Синтаксис list comprehension
состоит из следующих компонентов:
-
iterable
: перебираемый источник данных, в качестве которого может выступать список, множество, последовательность, либо даже функция, которая возвращает набор данных, например,range()
-
item
: извлекаемый из источника данных элемент -
expression
: выражение, которое возвращает некоторое значение. Это значение затем попадает в генерируемый список -
condition
: условие, которому должны соответствовать извлекаемые из источника данных элементы. Если элемент НЕ удовлетворяет условию, то он НЕ выбирается. Необязательный параметр.
Рассмотрим небольшой пример. Допустим, нам надо выбрать из списка все числа, которые больше 0. В обшем случае мы бы могли написать так:
numbers = [-3, -2, -1, 0, 1, 2, 3]
positive_numbers = []
for n in numbers:
if n > 0:
positive_numbers.append(n)
print(positive_numbers) # [1, 2, 3]
Теперь изменим этот код, применив list comprehension
:
numbers = [-3, -2, -1, 0, 1, 2, 3]
positive_numbers = [n for n in numbers if n > 0]
print(positive_numbers) # [1, 2, 3]
Выражение [n for n in numbers if n > 0]
говорит выбрать из списка numbers каждый элемент в переменную n, если n больше 0 и возврать n в результирующий список.
В качестве источника данных iterable
может использоваться любой перебираемый объект, например, другой список, словарь и т.д. Например, функция range()
возвращает все числя нуля до указанного порога не включая:
numbers = [n for n in range(10)]
print(numbers) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Нередко данная конструкция применяется, чтобы создать из словаря список. Например, выберем из словаря все ключи:
dictionary = {"red": "красный", "blue": "синий", "green": "зеленый"}
words = [word for word in dictionary]
print(words) # ['red', 'blue', 'green']
Параметр expression
представляет выражение, которое возвращает некоторое значение. Это значение затем помещается в генерируемый список. В примерах выше это был текущий элемент, который извлекается из источника данных:
numbers = [-3, -2, -1, 0, 1, 2, 3]
new_numbers = [n for n in numbers]
print(new_numbers) # [-3, -2, -1, 0, 1, 2, 3]
Условие - параметр condition
определяет фильтр для выбора элементов из источника данных. Применим условие для конкретизации выборки, например, выберем только четные числа:
numbers = [n for n in range(10) if n % 2 == 0]
print(numbers) # [0, 2, 4, 6, 8]
Распаковка (unpacking, также называемая Деструктуризация) представляет разложение коллекции (кортежа, списка и т.д.) на отдельные значения.
Так, как и многие языки программирования, Python поддерживает концепцию множественного присваивания. Например:
x, y = 1, 2
print(x) # 1
print(y) # 2
Данный пример в действительности уже представляет деструктуризацию или распаковку. Значения 1, 2
фактически являются кортежом, поскольку именно запятые между значениями говорят о том, что это кортеж.
Только кортежами мы не ограничены и можем "распаковывать" и другие коллекции, например, списки:
people = ["Tom", "Bob", "Sam"]
first, second, third = people
print(first) # Tom
print(second) # Bob
print(third) # Sam
При разложении словаря переменные получают ключи словаря:
dictionary = {"red": "красный", "blue": "синий", "green": "зеленый"}
r, b, g = dictionary
print(r) # red
print(b) # blue
print(g) # green
# получаем значение по ключу
print(dictionary[g]) # зеленый
Циклы в Python позволяют разложить коллекции на отдельные составляющие:
people = [
("Tom", 38, "Google"),
("Bob", 42, "Microsoft"),
("Sam", 29, "JetBrains")
]
for name, age, company in people:
print(f"Name: {name}, Age: {age}, Company: {company}")
Здесь мы перебираем список кортежей people
. Каждый кортеж состоит из трех элементов, соответственно при переборе мы можем их передать в переменные name
, age
и company
.
Другой пример - функция enumerate()
. Она принимает в качестве параметра коллекцию, создает для каждого элемента кортеж и возвращает набор из подобных кортежей. Каждый кортеж содержит индекс, который увеличивается с каждой итерацией:
people = ["Tom", "Bob", "Sam"]
for index, name in enumerate(people):
print(f"{index}.{name}")
# результат
# 0.Tom
# 1.Bob
# 2.Sam
Оператор *
упаковывает значение в коллекцию. Например:
num1=1
num2=2
num3=3
*numbers,=num1,num2,num3
print(numbers) #[1, 2, 3]
Здесь мы упаковываем значения из кортежа (num1
,num2
,num3
) в список numbers. Причем, чтобы получить список, после numbers указывается запятая.
Как правило, упаковка применяется для сбора значений, которые остались после присвоения результатов деструктуризации. Например:
head, *tail = [1, 2, 3, 4, 5]
print(head) # 1
print(tail) # [2, 3, 4, 5]
head, *middle, tail = [1, 2, 3, 4, 5]
print(head) # 1
print(middle) # [2, 3, 4]
print(tail) # 5
first, second, *other = [1, 2, 3, 4, 5]
print(first) # 1
print(second) # 2
print(other) # [3, 4, 5]
Другой пример - нам надо получить только первый, третий и последний элемент, а остальные элементы нам не нужны. В общем случае мы должны предоставить переменные для всех элементов коллекции. Однако если коллекция имеет 100 элементов, а нам нужно только три, не будем же мы определять все сто переменных. И в этом случае опять же можно применить упаковку:
first, _, third, *_, last = [1, 2, 3, 4, 5, 6, 7, 8]
print(first) # 1
print(third) # 3
print(last) # 8
Также можно получить ключи словаря:
red, *other, green = {"red":"красный", "blue":"синий", "yellow":"желтый", "green":"зеленый"}
print(red) # red
print(green) # green
print(other) # ['blue', 'yellow']
Оператор *
вместе с оператором **
также может применяться для распаковки значений. Оператор *
используется для распаковки кортежей, списков, строк, множеств, а оператор ** - для распаковки словарей. Особенно это может быть полезно, когда на основе одних коллекций создаются другие. Например, распаковка кортежей и списков:
nums1 = [1, 2, 3]
nums2 = (4, 5, 6)
# распаковываем список nums1 и кортеж nums2
nums3 = [*nums1, *nums2]
print(nums3) # [1, 2, 3, 4, 5, 6]
Здесь распаковывем значения из списка nums1 и кортежа nums2 и помещаем их в список nums3.
Подобным образом раскладываются словари, только применяется оператор **
:
dictionary1 = {"red":"красный", "blue":"синий"}
dictionary2 = {"green":"зеленый", "yellow":"желтый"}
# распаковываем словари
dictionary3 = {**dictionary1, **dictionary2}
print(dictionary3) # {'red': 'красный', 'blue': 'синий', 'green': 'зеленый', 'yellow': 'желтый'}
Одной из распространенных сфер, где применяются упаковка и распаковка - это параметры функций. Так, в определениях различных функций нередко можно увидеть, что они принимают такие параметры как *args
и **kwargs
.
Термины args
и kwargs
— это соглашения по программированию на Python, в реальности вместо них можно использовать любые именования. *args
представляет параметры, которые передаются по позиции. А **kwargs
означает параметры, которые передаются по имени.
обозначает аргументы ключевого слова.
Оператор *
применяется с любым итерируемым объектом (например, кортежем, списком и строками). Тогда как оператор **
можно использовать только со словарями.
Оператор *
позволяет передать в функцию несколько значений, и все они будут упакованы в кортеж:
def fun(*args):
# обращаемся к первому элементу кортежа
print(args[0])
# выводим весь кортеж
print(args)
fun("Python", "C++", "Java", "C#")
Здесь функция fun
принимает кортеж значений. При вызове мы можем передать ей различное количество значений. Так, в примере выше передается четыре строки, которые образуют кортеж. Консольный вывод программы:
Python
('Python', 'C++', 'Java', 'C#')
Благодаря такой возможности мы можем передавать в функцию переменное количество значений:
def sum(*args):
result = 0
for arg in args:
result += arg
return result
print(sum(1, 2, 3)) # 6
print(sum(1, 2, 3, 4)) # 10
print(sum(1, 2, 3, 4, 5)) # 15
Оператор **
упаковывает аргументы, переданные по имени, в словарь. Имена параметров служат ключами. Например, определим функцию, которая просто будет выводить все переданные параметры
def fun(**kwargs):
for key in kwargs:
print(f"{key} = {kwargs[key]}")
fun(name="Tom", age="38", company="Google")
Консольный вывод программы:
name = Tom
age = 38
company = Google
Выше было описано, как операторы *
и **
применяются для упаковки аругментов в кортеж и словарь соответственно. Но эти же операторы могут использоваться для распаковки.
Сначала рассмотрим ситуацию, где это может пригодиться.Пусть мы передаем в функцию кортеж:
def sum(*args):
result = 0
for arg in args:
result += arg
return result
numbers = (1, 2, 3, 4, 5)
print(sum(numbers))
Здесь в вызов функции sum передается кортеж. Параметр *args
по сути тоже представляет кортеж, и кажется, все должно работать. Тем не менее мы столкнемся с ошибкой
TypeError: unsupported operand type(s) for +=: 'int' and 'tuple'
То есть в данном случае кортеж numbers передается как элемент кортежа *args
.
И чтобы элементы кортежа были переданы в кортеж *args
как отдельные значения, необходимо выполнить их распаковку:
def sum(*args):
result = 0
for arg in args:
result += arg
return result
numbers = (1, 2, 3, 4, 5)
# применяем распаковку - *numbers
print(sum(*numbers)) # 15
Здесь при передачи кортежа numbers
в функцию sym
применяется распаковка: *numbers
Другим случаем распаковки может быть ситуация, когда функция принимает несколько параметров, а мы передаем один кортеж или список:
def print_person(name, age, company):
print(f"Name:{name}, Age: {age}, Company: {company}")
person =("Tom", 38, "Google")
# выполняем распаковку кортежа person
print_person(*person) # Name:Tom, Age: 38, Company: Google
В данном случае выражение *person
раскладывает кортеж person на отдельные значения, которые передаются параметрам name
, age
и company
.
Оператор **
применяется для распаковки словарей:
def print_person(name, age, company):
print(f"Name:{name}, Age: {age}, Company: {company}")
tom ={"name":"Tom", "age":38, "company":"Google"}
# выполняем распаковку словаря tom
print_person(**tom) # Name:Tom, Age: 38, Company: Google
Здесь выражение **tom
раскладывает словарь на отдельные значения, которые передаются параметрам name, age и company по названию ключей.
Параметры *args
и *kwargs
могут использоваться в функции вместе с другими параметрами. Например:
def sum(num1, num2, *nums):
result=num1+num2
for n in nums:
result += n
return result
print(sum(1,2,3)) # 6
print(sum(1,2,3,4)) # 10