Skip to content

Popov-Dmitriy-Ivanovich/python_cheetsheet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 

Repository files navigation

#TODO

match (дописать полностью когда появятся словари)??

regex

строки (done)

with as, in, not, (done)

file in/out

args?

date/time

Функциональный python

Область видимости переменных

Скрытие переменных

локальная переменная перекрывает глобальную (при ее изменении будет меняться локально созданный объект)

global

Если же мы хотим изменить в локальной функции глобальную переменную, а не определить локальную, то необходимо использовать ключевое слово global:

name = "Tom"  

def say_hi():  
    global  name  
    name = "Bob"        # изменяем значение глобальной переменной  
    print("Hello", name)  

say_hi()    # Hello Bob  

nonlocal

Выражение 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. Затем передаем эти значения в вызов функции.

match

Начиная с версии 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 отступа)

f-строки

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

Функции ord и len

Поскольку строка содержит символы 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

with - as

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) # Запись данных

Также эта конструкция удобна тем, что вам не надо помнить про закрытие файла, так > как это выполняется автоматически.

Что такое with as в Python? Онлайн справочник на itProger

Оператор in

Оператор 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

ООП python

Создание класса / объекта

Класс определяется с помощью ключевого слова 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.метод      # обращение к методу

Так же при помощи self в конструкторе можно объявить атрибут (поле) класса.

И вообще атрибуты (поля) класса можно генерить прямо в рантайме просто обратившись к ним (классный способ выстрелить себе в ногу)

Конструкторы (__init__)

Для создания объекта класса используется конструктор. Так, выше когда мы создавали объекты класса 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 - обращение к статическому методу через имя объекта

Класс object.

Начиная с 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

try...except...finally

string = "hello"
number = int(string)
print(number)

При выполнении этого скрипта будет выброшено исключение ValueError, так как строку "hello" нельзя преобразовать в число:

ValueError: invalid literal for int() with base 10: 'hello'

При возникновении исключения работа программы прерывается, и чтобы избежать подобного поведения и обрабатывать исключения в Python есть конструкция try..except.

try:
    инструкции
except [Тип_исключения]:
    инструкции

Блок finally

При обработке исключений также можно использовать необязательный блок 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 обладает довольно большим списком встроенных исключений. Весь этот список можно посмотреть в документации. Перечислю только некоторые наиболее часто встречающиеся:

  • IndexError: исключение возникает, если индекс при обращении к элементу коллекции находится вне допустимого диапазона

  • 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

Иногда возникает необходимость вручную сгенерировать то или иное исключение. Для этого применяется оператор 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}")

Контейнеры данных

списки list

вроде как список (но асимптотика подойдет и массиву)

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"]

Удаление с помощью del

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)

Кортеж (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, которая имеет следующие формы:

  • range(stop): возвращает все целые числа от 0 до stop

  • range(start, stop): возвращает все целые числа в промежутке от start (включая) до stop (не включая).

  • range(start, stop, step): возвращает целые числа в промежутке от start (включая) до stop (не включая), которые увеличиваются на значение step

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

Словари (dictionary)

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)

set (множества)

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

Тип 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

Функциональность 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

В качестве источника данных 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)

Параметр 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 означает параметры, которые передаются по имени. обозначает аргументы ключевого слова.

Оператор * применяется с любым итерируемым объектом (например, кортежем, списком и строками). Тогда как оператор ** можно использовать только со словарями.

*args

Оператор * позволяет передать в функцию несколько значений, и все они будут упакованы в кортеж:

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        

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published