Основы Python. Часть 2#

Перечислимые типы#

Базовые перечислимые типы#

Тип

Название

Объявление

Порядок

list

Список

[1, 2, 1], ["a", "b", "a"], []

Да

tuple

Кортеж

(1, 2, 1), ("a", "b", "a"), ("a",)

Да

set

Набор

{1, 3, 2}, {"a","c","b"}, set()

Нет

dict

Словарь

{"a":1, "c":2, "b":1}, {}

Сохраняет порядок вставки

Перечислимые типы являются изменяемыми. При присвоении переменные этих типов не копируются.

Типы tuple, str и bytes имеют промежуточный статус. Они является неизменяемыми, но переменные этих типов можно перечислять.

Особенности перечислимых типов#

Элементы перечислимых типов могут быть произвольного, в том числе перечислимого типа:

data = [5,7,9,12,"test",True,["ab","cd","ef"]]

Списки и кортежи сохраняют порядок, а наборы и словари - нет:

data_list = [5,4,3,6,2,1]
data_set = {5,4,3,6,2,1}
print(data_list)
print(data_set)
[5, 4, 3, 6, 2, 1]
{1, 2, 3, 4, 5, 6}

Переменные перечислимых типов (как и все составных) при присвоении не копируются:

data = [5,7,9,12]
double_data = data
double_data[1] = 42
print(data)
[5, 42, 9, 12]

Переменные перечислимых типов можно перечислять в цикле for.

Цикл перебора элементов for#

Итерационные цикл for используется для обхода элементов последовательностей.

for 〖переменные〗in 〖список или генератор〗:
    〖операторы〗

Примеры:

x = "привет"
for char in x:
    print(char)
п
р
и
в
е
т
x = [3,4,5]
for num in x:
    print(num)
3
4
5

Некоторые полезные генераторы#

Элемент с индексом

for i, elem in enumerate([1,2,3]):
    print(i,elem)
0 1
1 2
2 3

Целые числа по порядку

for i in range(5, 10, 1):
    print(i)
5
6
7
8
9

Синхронный обход списков

for elem1, elem2, elem3 in zip([1,2,3], ["a","b","c"], "qwt"):
    print(elem1, elem2, elem3)
1 a q
2 b w
3 c t

Полное декартово произведение

import itertools
for elem1, elem2 in itertools.product([1,2,3], ["a","b","c"]):
    print(elem1, elem2)
1 a
1 b
1 c
2 a
2 b
2 c
3 a
3 b
3 c

Список: list#

  • Используется в качестве замены массивам и векторам

  • Может иметь произвольную длину

  • Его можно удлинять и укорачивать

Индексирование в Python выполняется с 0.

data = ['a', 'b', 'c', 'd']
data[0] # 'a'
data[3] # 'd'
# Отрицательные индексы нумеруют список с конца и с -1
data[-1] # 'd'
data[-4] # 'a'
# Длина
x = len(data) # x == 4
# Поиск по значению
x = data.index('c') # x == 2

Срезы списков#

Срезы (slices) — это способ получать из списка его часть не прибегая к циклам.

Синтаксис: <индекс первого элемента>:<индекс последнего элемента +1>:<шаг>

data = ['a', 'b', 'c', 'd']
print(data[1:3])
print(data[2:])
print(data[:])
print(data[-2:])
print(data[1:4:2])
['b', 'c']
['c', 'd']
['a', 'b', 'c', 'd']
['c', 'd']
['b', 'd']

Срезы создают копию списка:

data = ['a', 'b', 'c', 'd']

# есть копирование temp и data это разные списки
temp = data[:]
temp[0] = 'z'
print(data)

# нет копирования temp и data это один список с двумя именами
temp = data
temp[0] = 'z'
print(data)
['a', 'b', 'c', 'd']
['z', 'b', 'c', 'd']

Модификация списков#

data = ['a', 'b', 'c', 'd']
data[1] = 42
del data[1]
data.insert(0,42)
data.append(42)
print(data)

data = data + [1,2,3]
print(data)
v = data.pop()
print(data, v)
v = data.pop(0)
print(data, v)
[42, 'a', 'c', 'd', 42]
[42, 'a', 'c', 'd', 42, 1, 2, 3]
[42, 'a', 'c', 'd', 42, 1, 2] 3
['a', 'c', 'd', 42, 1, 2] 42

Перечисление элементов списков#

data = ['a', 'b', 'c', 'd']
for v in data:
    print(v)
for i,v in enumerate(data):
    print(i,v)
a
b
c
d
0 a
1 b
2 c
3 d

Если элемент списка неизменяемого типа, то при присвоение переменной созданной в цикле for (в примере, v) нового значение не меняет содержание массива.

i = 0
while i < len(data):
    data[i] = 42
    i += 1
    
for i in range(0,len(data)):
    data[i] = 42

Сортировка списков#

adata = [5,4,2,1,4]
# Сортировка с копированием
bdata = sorted(adata)
print(bdata)
# Сортировка на месте (in-place)
adata.sort()
print(adata)
# Обратная сортировка
bdata = sorted(adata, reverse=True)
print(bdata)
adata.sort(reverse=True)
print(adata)
[1, 2, 4, 4, 5]
[1, 2, 4, 4, 5]
[5, 4, 4, 2, 1]
[5, 4, 4, 2, 1]

Кортеж: tuple#

Кортеж это неизменяемый список. Часто встречается, как возвращаемое значение функций и генераторов.

tple = (1, 2, 3)
print(tple[0])
1

Набор: set#

Набор не сохраняет порядок. Все элементы набора различны.

vset = {4, 3, 2, 3, 1}
print(vset)
vset.add(0)
print(vset)
vset.add(4)
print(vset)
vset.remove(4)
print(vset)
{1, 2, 3, 4}
{0, 1, 2, 3, 4}
{0, 1, 2, 3, 4}
{0, 1, 2, 3}

Типичное применение — хранение данных имеющих структуру множества и удаление дубликатов из списка:

data = [1, 1, 2, 2, 2]
data = list(set(data))
print(data)
[1, 2]

Словарь: dict#

Набор пар ключ-значение. Ключи всегда различны.

Значения могут быть любого типа, ключи — с некоторыми ограничениями.

Модификация словарей#

data = {"a":1, "b":2, "c":3}
data["b"] = 3
print(data)
del data["b"]
print(data)
data["f"] = 99
print(data)
print(len(data))
data.update({"a":99, "q":64})
print(data)
{'a': 1, 'b': 3, 'c': 3}
{'a': 1, 'c': 3}
{'a': 1, 'c': 3, 'f': 99}
3
{'a': 99, 'c': 3, 'f': 99, 'q': 64}

Перечисление элементов словарей#

data = {"a":1, "b":2, "c":3}

for k in data:
    print(k)
    print(data[k]) 

for k in sorted(data.keys()):
    print(k,data[k])
a
1
b
2
c
3
a 1
b 2
c 3

Операторы над составными типами#

Оператор

Действие

Пример

in, not in

Содержание, возвращает bool

x in data

y[x]

Индексирование переменных перечислимых типов

data[x]

y.x

Обращение к полю или методу объекта или модуля

data.foo(2)

Приведение перечислимых типов#

Исходный тип

Целевой тип

Приведение для переменных data и keys

list

tuple

tuple(data)

list

set

set(data)

list

dict

dict(zip(keys,data))

tuple

list

list(data)

tuple

set

set(data)

tuple

dict

dict(zip(keys,data))

set

list

list(data)

set

tuple

tuple(data)

set

dict

dict(zip(keys,data))

dict

list

list(data.keys()), list(data.values())

dict

set

set(data.keys()), set(data.values())

dict

tuple

tuple(data.keys()), tuple(data.values())

Объявление функций#

Как и в большинстве языков структурного программирования, блоки операторов можно оформлять в виде функций для дальнейшего использования.

def 〖имя функции〗(〖аргументы〗):
    〖операторы〗
    return 〖возвращаемое значение〗

Функция может возвращать одно или несколько значений (в виде кортежа) или None.

Объявление функции следует рассматривать, как присвоение значения соответствующей переменной.

Модификация аргументов#

Базовые типы данных при вызове функций копируются, а составные нет.

def foo(x):
    x = x + 1
    return x
    
w = foo(5)
print(w)
x = 1
x = foo(x)
print(x)
6
2
def foo(x):
    x[0] = "42"

w = [1, 2, 3]
foo(w)
print(w)
['42', 2, 3]

Область видимости#

Функция видит переменные объявленные вне ее тела, но при попытке присвоения создает новые, локальные.

Присвоение значений аргументам функции в ее теле равносильно созданию новых переменных.

y = 1

def foo(x):
    y = x + 2
    return x,y

a,b = foo(1)
print(a,b)

def foo(x):
    global y # Явно используем y из глобальной области
    x = x + y 
    y = y + 1
    return x,y

a,b = foo(1)
print(a,b)
1 3
2 2

Аргументы по умолчанию#

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

def foo(x = 42):
    return x*2
    
print(foo(1))
print(foo())
2
84

Обязательные аргументы всегда должны идти перед аргументами со значениями по умолчанию при объявлении функции.

def foo(x, y, z = 42): #Верно
    return x*y*2

Порядок аргументов#

Обязательные аргументы должны передаваться по порядку.

Аргументы со значениями по умолчанию можно передавать в произвольном порядке.

def foo(a, x = 1, y = 2, z = 3):
    return (x*y*z)/a

print(foo(1, z = 7, y = 7))
49.0

Функция как аргумент#

def foo(x):
    return x**2

def bar(x):
    return x**3 / 3

def calc(f, x, a):
    return f(x) + a
    
print(calc(foo,1,2))
print(calc(bar,3,2))
3
11.0

Что еще нужно знать о функциях#

  • Функция — объект специального составного типа function

  • Объявление одной и той-же функции несколько раз не выдаст ошибки

  • Функции можно складывать в списки, словари, проверять на тождество

  • Функции можно объявлять внутри функций, кроме слова global есть слово nonlocal, которое делает тоже самое, но для локальных переменных

  • Функции с помощью специальных приемов могут обрабатывать произвольное число аргументов (подробнее)

  • Функции можно вызывать рекурсивно

  • Есть особые функции: лямбда (безымянные) и генераторы (запоминающие свое состояние)

Исключения#

Возникновение исключений#

Когда происходит ошибка — возбуждается соответствующее исключение. Обработка исключения — основой способ обработки ошибок в Python.

Когда происходит исключение Python начинает искать обработчик исключения для данного блока, при необходимости выходя из функций. Если обработчик не найден, то интерпретатор завершается (или выдает ошибку в интерактивном режиме).

Обработка исключений#

Можно перехватывать все типы исключения написав except:, но это маскирует ошибки.

Популярные типы исключений#

Исключение

Описание

IndexError

Индекс за пределами массива

KeyError

Ключ отсутствует в словаре

NameError

Обращение к необъявленной переменной (методу, классу)

UnboundLocalError

Обращение к недоступной переменной

OSError

Ошибки системы (права на файл, нет места на диске, …)

TypeError

Операция не поддерживается данным типом данных (вычитание строк, …)

ValueError

Недопустимое значение переменной (float('hello'), …)

NotImplementedError

Метод не реализован

Исключение также можно выбросить прямо в коде. Для этого используется ключевое слово raise.

Примеры использования#

value_str = "123.15"
value_float = 0.0

try:
    value_float = float(value_str)
except ValueError:
    print("Это не число!")
finally:
    print(value_float)
    
print(value_float*10)
123.15
1231.5

Примеры использования в функции#

def fix(x, fix_type = None):
    if fix_type == "round":
        return round(x)
    elif fix_type == "ceil":
        return math.ceil(x)
    elif fix_type == "trunc":
        return math.trunc(x)
    else:
        raise ValueError("Недопустимое значение переменной fix_type")

#ft = input("Введите тип округления:")
ft = 'unknown'
value = 73/13
try:
    value = fix(value, ft)
except ValueError:
    print("Неправильный тип округления")
    value = fix(value, "round")
Неправильный тип округления

Что еще нужно знать об исключениях#

  • «Я не знаю, что дальше делать, я вызываю raise»

  • Оператор try почти ничего не стоит, но обработка возникшего исключения дело более ресурсоемкое (хотя и не очень), поэтому не следует использовать исключения вместо if

  • Объект исключения можно преобразовать в str, обычно это используется для вывод сообщений об ошибках

  • Исключение можно возбудить повторно, вызвав raise без аргументов в обработчике

  • Исключения можно содержательно анализировать с помощью методов модуля traceback

Модули#

import os
os.getcwd();
from os import getcwd
getcwd();

Порядок поиск при импорте:

  • Файлы в папке программы

  • Файлы в переменной окружения $PYTHONPATH

  • Сторонние пакеты

  • Стандартная библиотека

Собственные модули#

Файл mylib.py:

def foo(x):
    return x*x

class MyClass:
    def __init__(self):
        self.v = 0

Файл program.py:

Разница между импортом и запуском#

Модуль может определить загружен ли он через import или запущен как программа.

Функция main() будет вызвана только если скрипт запущен как программа и не будет запускаться если он импортирован в другой скрипт.