Skip to content

Instantly share code, notes, and snippets.

@artsiomkaltovich
Last active August 28, 2018 13:18
Show Gist options
  • Save artsiomkaltovich/9b03753150a93a1510840a107d48a157 to your computer and use it in GitHub Desktop.
Save artsiomkaltovich/9b03753150a93a1510840a107d48a157 to your computer and use it in GitHub Desktop.
Python lesson
# 1. Python - интерпретируемый язык, это значит, что программа, выполняющая код (интерпретатор), извлекает команды по очереди
# и выполняет их одна за одной. Это привод к тому, что некоторые ошибки будет видно, только при выполнении кода
print("str" + 1) # -> TypeError: cannot concatenate 'str' and 'int' object
# Однако, если этот код не выполнится, то вы не узнаете об этой ошибке, в отличии от компилируемых языков,
# которые бы выдали вам ошибку во время компиляции и отказались бы запускать такой код.
if False:
print("str" + 1) # -> Process finished with exit code 0
# exit code 0 - успешное завершение команды, при все остальные значение означают, что произошла ошибка
# Это имеет и свои плюсы, так при установленном и настроенном интерпретаторе Python, вы можете вызвать интерактивный режим и
# вводить свои команды по одной в консоль и сразу видеть результат.
>python
Python <тут будет версия интерпретатора и информация о вашей ОС>
Type "help", "copyright", "credits" or "license" for more information.
>>> 2 * 2 # ввод команды
4 # её результат
>>> # ожидание ввода следующей команды, для завершения интерпретатора необходимо
# ввести ctrl+d для linux и ctrl+z, а затем enter для windows
# Так же интерактивный режим доступен и в PyCharm. Пункт главного меню Tools -> Python Console
# 2. Базовые типы и операции
# https://docs.python.org/3/library/stdtypes.html
# 2.1 Python - язык с динамической и строгой типизацией
# Динамическая типизация - при создании переменной не нужно указывать её тип, так же переменная может в любой момент
# поменять его
a = 1 # создание целочисленной переменной
a = 1.0 # присвоение ей вещественного значения
a = "hello" # присвоение ей строкового значения
# Строгая типизация - интерпретатор не будет преобразовывать типы, если вы явно не укажете ему это сделать
print(1 + int("1")) # -> 2
print(1 + "1") # -> TypeError: unsupported operand type(s) for +: 'int' and 'str'
# 2.2 Целочисленный тип и целочисленные операции (integer)
a = 1
print(a)
print(-a) # -> -1 вычисление противоположного значения. выражение +a тоже допустимо, но ничего не делает.
print(a + 2)
print(a - 2)
print(a * 5)
print(a ** 5) # -> 1 возведение в степень
print(6 // 4) # -> 1 целочисленное деление
print(6 % 4) # -> 2 остаток деления
print(5 / 4) # -> 1.25 вещественное деление
# Оператор присваивания =
# Вычисляет выражение справа от себя и присваивает результат этого значения переменной слева от себя
a = 5
a = a + 2
a = a * 3
print(a) # -> 21
# Существует сокращённая запись
a = 5
a += 2
a *= 3
print(a) # -> 21
# Имя переменной - идентификатор (любая последовательность цифр, латинских букв и знака подчёркивания,
# начинающаяся с латинской буквы или знака подчёркивания)
# допустимые идентификаторы: a, _a, a13, A3, b_, _
# недопустимые имена: 1, 34d, 78_a, db abc (имя переменной не может содержать пробел)
# Задание:
# 2.2.1 Откройте интерактивный режим интерпретатора и осуществите несколько базовых арифметических операций
# 2.3 Вещественный тип и целочисленные операции (float)
a = 1. # задание переменной вещественного типа
a = -1.5
# Для вещественного типа определены все рассмотренные выше операторы
print(1. * - 2.4) # -> -2.4
print(5.0 + 4) # -> 9.0
# 2.4 Приоритет операций
# https://www.tutorialspoint.com/python/operators_precedence_example.htm
# От старшего к младшему (Не обращайте внимания на незнакомые вам операнды, мы рассмотрим их позже)
# ** Exponentiation (raise to the power)
# ~ + - Complement, unary plus and minus (method names for the last two are +@ and -@)
# * / % // Multiply, divide, modulo and floor division
# + - Addition and subtraction
# >> << Right and left bitwise shift
# & Bitwise 'AND'
# ^ | Bitwise exclusive `OR' and regular `OR'
# <= < > >= Comparison operators
# <> == != Equality operators
# = %= /= //= -= += *= **= Assignment operators
# is is not Identity operators
# in not in Membership operators
# not or and Logical operators
# определяет в каком порядке будут выполнены операции
print(2 + 2 * 2) # -> 6
# Приоритет можно менять с помощью скобок
print((2 + 2) * 2) # -> 8
# Но лучше не смешивать операторы разного приоритета
print(4.5 ** 1/2) # -> 2.25
print(4.5 ** (1/2)) # -> 1.0
# И разных типов
print(4.5 ** 0.5) # -> 2.12132034356
# Задание :
# 2.4.1 Откройте интерактивный режим интерпретатора и осуществите несколько базовых арифметических операций
# над вещественными и целыми значениями
# 2.4.2 Вычислите значение выражения https://wikimedia.org/api/rest_v1/media/math/render/svg/849e95193ac47959d04871bd7819452dc57455b5
# при x = 0. Для выполнения этого задания достаточно только операторов.
# 2.5 Встроенные функции
# Выше были рассмотрены операнды, так же в python уже определены множество функций
# https://docs.python.org/3/library/functions.html
# Функции вызываются так же, как и в большинстве языков программирования (ЯП)
# Например: имя_функции(параметры через замятую)
print(divmod(6, 4)) # -> (1, 2) возвращает одновременно остаток и кратное
print(divmod(-6, 4)) # -> (-2, 2) допустимы отрицательные значения
print(abs(5)) # -> 5 модуль (абсолютное значение числа)
print(abs(-4)) # -> -4
print(round(2.5)) # округление
# ф-цией является и уже известная нам команда вывода print()
# так же полезной функцией является ф-ция help, которая выводит справку по объектам python
# Задание :
# 2.5.1 Прочтите справку, полученную с помощью ф-ции help, в интерактивном режиме, по нескольким остальным ф-циям
# Определение своих функций осуществляется с помощью ключевого слова def
# def имя_функции(параметры через замятую)
def my_func(): # функция без параметров
print("hello world!")
# Вычисленное в функции значение можно вернуть ключевым словом return
def sum(a, b): # функция с двумя параметрами
return a + b
print(sum(2, -2)) # вызов объявленной функции
# Return может использоваться и без параметров
def sum(a, b):
print(a + b)
return # либо не использоваться вообще, если вы не хотите, чтобы функция возвращала результат
# ВАЖНО: В стандартной библиотеке python уже определена функция sum.
# Интерпретатор позволяет вам создать свою функцию или переменную с таким же именем,
# но делать так крайне не рекомендуется, так как вы потеряете её функционал и запутаете себя и
# других людей, которые будут читать ваш код.
# Если очень хочется использовать именно это имя, а оно занято или зарезервировано,
# можно добавить символ _ (нижнее подчёркивание) в конце имени, например sum_
# 2.6 Другие системы счисления
# По умолчанию используется десятичная система, однако, числа могут задаваться и в других
print(0x54) # -> 84 16-ричная система
print(0o54) # -> 44 8-ричная
print(0b1001) # -> 9 2-ичная
print(0x54 + 0o54 // 0b1001 ) # -> разумеется, их можно смешивать
# но лучше будет избегать этого, чтобы не запутаться
# 2.7 Битовые операции
# В памяти компьютера данные хранятся в двоичном виде. Например 9 хранится в виде 0b1001.
# Над подобным представление допустимы различные операции.
print(9 >> 1) # -> 4(0b0100) битовый сдвиг вправо на 1 разряд
print(9 << 3) # -> 72(0b0100 1000) битовый сдвиг влево на 3 разряда
print(123 >> 2) # -> 30
print(123 << 2) # -> 492
# Данные операции эквивалентны следующим
print(9 // 2**1) # -> 4
print(9 * 2**3) # -> 72
# Однако, если вы точно знаете, что вам нужно умножать\целочисленно делить на целую степень двойки
# лучше использовать их. В двоичной системе умножение и деление на 2-ку такой же частный случай,
# как и в 10-чной деление\умножение на 10-ку.
# Но то в теории, на практике, конечно, всё веселее :)
# https://stackoverflow.com/questions/37053379/times-two-faster-than-bit-shift
# Однако №2. Данные операции определены для целых значений.
print(2.0 << 2) # TypeError: unsupported operand type(s) for <<: 'float' and 'int'
# Если вы не знакомы с машинными представлением двоичных чисел и не хотите тратить сейчас время на это, то
# вы можете безболезненно перейти к следующему блоку.
print(~9) # -> -10 побитовое отрицание (инверсия битов)
print(0b1001 | 0b1010) # -> 11(0b1011) дизъюнкция (логическое "или")
print(0b1001 & 0b1010) # -> 8(0b1011) конъюнкция (логическое "и")
print(0b1001 ^ 0b1010) # -> 3(0b0011) исключающее или (xor)
print(0x54 & 0o54 // 0b1001) # -> 4 Допустимо смешивание данных операндов с обычными арифметическими
# 2.8 Булевый тип
# Допускает только два значения
a = False # -> ложно
a = True # -> истинно
# 2.9 Операции сравнения
# Возвращают булевый тип
print(9 < 3) # -> False строго меньше
print(9 > 9) # -> False строго больше
print(9 >= 9) # -> True больше или равно
print(9 == 9) # -> True равно
print(9 != 9) # -> False не равно
print(9 != 'str') # -> True допустимо сравнивать разные типы
print(9 != 9.) # -> False но так как вещественные числа хранятся с ошибкой использовать строгие равенства не желательно
print(1 != True) # -> False истинное значение эквивалентно единице
print(0 == False) # -> True а ложное нулю
print(1 < 3 < 5) # -> True допустимы цепочки сравнений
# 2.10 Булевы операции
print(True and False) # -> False логическое "и" (верно, только если верны все аргументы)
print(True or False or False) # -> True логическое "или" (верно, если верен хотя бы один аргумент)
print(not True or False) # -> False логическое отрицание (меняет значение на обратное)
# Задание 2.10:
# 1. Откройте интерактивный режим интерпретатора и осуществите несколько базовых логических и булевых операций
# Вычисления являются ленивыми (вычисление прекратиться, если результат понятен)
def cond(): # никогда не выполнится
print("call")
print(False and cond()) # т.к. результат вычисления выражений
print(True or cond()) # известен по первому аргументу
print(cond() and False) # Выведет call
# None
# 2.11 Тип None
# https://docs.python.org/3/library/constants.html
# То есть ничего или отсутствие значения :)
# Проверка на равенство переменной None осуществляется оператором is
a = None
print(a is None) # -> True
print(3 is None) # -> False
# Задание:
# 1. Самое время выпить чай или кофе.
# 3 Последовательности (Sequence)
# 3.1 Кортеж (tuple)
# Задаётся круглыми скобками
a = (1, 2, 3)
a = (True, None, 1) # допустимо хранить в последовательности разные типы
print(a[0]) # -> True для получения значения по индексу используются квадратные скобки
# индексы начинаются с 0
# кортеж является неизменяемым типом
a[0] = 3 # -> TypeError: 'tuple' object does not support item assignment
# добавлять значения после создания так же нельзя
# 3.2 Список (list)
# Задаётся квадратными скобками
a = [1, 2, 3]
b = [] # создание пустого списка
# является изменяемым типом
a.append(a is None) # разрешается добавлять элементы
print(a) # -> [1, 2, 3, False]
# и изменять их
a[1] -= a[2]
print(a) # -> [1, -1, 3, False]
# Возможно "умножение" списка на число. В этом случае в список столько раз добавятся все его элементы на сколько его умножили
print([0] * 10) # -> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
a = [0, 1]
print(a * 3) # -> [0, 1, 0, 1, 0, 1]
# Пустой список умножать бессмысленно
print([] * 10) # -> []
# таким образом список можно очистить
a = [0, 1]
print(a * 0) # -> []
print(a * -1) # -> []
# умножение разрешено только для целых чисел
print(a * 2.5) # -> TypeError: can't multiply sequence by non-int of type 'float'
# 3.3 Диапазон (Range)
# Создаётся с помощью ключевого слова range
a = range(2, 10, 3)
# в данном случае 2 - начало диапазона (по умолчанию 0)
# 10 - конец диапазона (только оно является обязательным, само значение в диапазон не будет входить)
# 3 - шаг, с которым меняются значение (по умолчанию 1)
print(a[2]) # -> 8
# начало и шаг могут опускаться
a = range(10)
print(a[5]) # -> 5
# шаг может быть и отрицательным
a = range(20, 10, -1)
print(a[2]) # -> 18
# ВАЖНО: При создании диапазона его значение не вычисляются и не сохраняются в памяти.
# По сути вы задаёте правило нахождения элементов, а не сами элементы.
# Задание 3.3:
# 1. Попытайтесь "сломать" python и ввести в последнем примере нижнюю границу меньше конца диапазона range(1, 10, -1)
# 3.4 Работа с последовательностями
# Последовательности имеют единообразный синтаксис работы над ними, за исключением
# разницы накладываемой их внутренним строением
# для определения количества элементов используется ф-ция len()
a = range(10)
print(len(a)) # -> 10
# как уже упоминалось, для доступа по индексу используются квадратные скобки
a = ["a", "b", "c", "d", "e", "f"]
print(a[2]) # -> "с"
# допустимо использовать отрицательные индексы, в этом случае индексы считаются с конца
print(a[-1]) # -> "f"
print(a[-2]) # -> "e" так как допустимы все индексы из диапазона от -len(a) до len(a) + 1, и 0 тоже
# необходимо быть весьма внимательными, при работе с индексами в цикле
# чтобы не проитерироваться по списку несколько раз
# 3.4.1 Срез (slice)
# Допустимо указывать несколько индексов через двоеточие, в этом случае, вам будет возвращено несколько значений
a = ["a", "b", "c", "d", "e", "f"]
print(a[2:4]) # -> ['c', 'd']
# один из индексов можно опускать, в этом случае, вернётся значение до конца\от начала последовательности
print(a[3:]) # -> ['d', 'e', 'f']
print(a[:1]) # -> ['a']
r = range(10)
print(r[3:]) # -> range(3, 10)
# можно указывать шаг
print(a[1:5:2]) # -> ['b', 'd']
# значения можно опускать
print(a[1::3]) # -> ['b', 'e']
# Задание 3.4.1:
# 1. Поэкспериментируйте со срезами, попробуйте использовать отрицательные значения.
# 2. Переверните последовательность, используя лишь срезы.
# ["a", "b", "c", "d", "e", "f"] -> ['f', 'e', 'd', 'c', 'b', 'a']
# 3. Как показано в 3.1, кортежи являются неизменяемым типом данных.
# Напишите ф-цию, которая будет принимать на вход кортеж, индекс и элемент и возвращать
# новый кортеж, у которого по заданному индексу будет находиться новый элемент
# ("a", "b", "c"), 1, 2 -> ("a", 2, "c")
# 4. Используя решение задания 2 проверьте, является ли последовательность полиндромом.
# Данный алгоритм проверки крайне не эффективен и используется лишь в образовательных целях.
# Не надо так делать на проектах, нет, серьёзно не стоит :)
# 5. Напишите более эффективный алгоритм для решения задачи 4
# 3.4.2 Преобразование типов
# Можно от одной последовательности перейти к другой
print(tuple([1, 2, 3])) # -> (1, 2, 3)
# список можно дополнять из других последовательностей
a = range(10)
b = ["a", "b", "c"]
b.extend(a[3:]) # -> ['a', 'b', 'c', 3, 4, 5, 6, 7, 8, 9]
print(b)
# 3.4.3 Оператор del
# Служит для удаления элементов
a = ["a", "b", "c", "d", "e", "f"]
del a[2:3]
print(a) # -> ['a', 'b', 'd', 'e', 'f'] заметьте, что так же как и в диапазоны
# последний элемент не входит в срез
# 3.4.4 Оператор in
# Проверяет, входит ли элемент в последовательность
a = ["a", "b", "c", "d", "e", "f"]
print("c" in a) # -> True
del a[2:3]
print("c" in a) # -> False
# Для противоположной проверки необходимо добавить ключевое слово not перед in
print("c" not in a) # -> True
# 4 Конструкции перехода
# В python определены оператор условного перехода if и циклы while и for
# 4.1 Оператор условного перехода
if True:
print("true")
else:
print("this code will never be executed")
# 4.2 Цикл с пред-условием while-clause
# Выведет числа от 0 до 9 (включая)
i = 0
while i < 10: # условие продолжения цикла
print(i) # тело цикла
i += 1 # всё ещё тело цикла
# 4.3 Цикл for
# Та же задача может быть решена элегантнее с помощью цикла for
for i in range(10):
print(i)
# с его помощью можно пройтись по всем элементам коллекции
for letter in ["a", "b", "c", "d", "e", "f"]:
print(letter)
a = ["a", "b", "c", "d", "e", "f"]
for index in range(len(a)):
print(a[index])
# в данных циклах лучше не менять значения последовательности и текущий итерируемый объект,
# так как код станет сложнее для прочтения и сопровождения
# впрочем, интерпретатор не всегда даст вам большого простора
# следующий код выведет "b" 6 раз
a = ["a", "b", "c", "d", "e", "f"]
for index in range(len(a)):
index = 1
print(a[index])
# 4.4 Операторы continue и break
# Оператор continue пропускает весь оставшийся блок кода и переходит к проверке условия продолжения цикла
# Оператор break пропускает весь оставшийся блок кода и выходит из цикла
# Задание 4.4: Выполните следующие блоки кода
for i in range(10):
if i > 6: break
if i % 2: continue
print(i)
i = 0
while i < 10:
print("I am running", i)
i += 1
if i > 7:
break
print("You will not stop me", i)
if i > 5:
continue
print("I am still running", i)
print(i)
# 4.5 Ключевое слово pass
# Как, вы уже, наверно, заметили, в python нет операторных скобок, за уровни вложенности отвечают отступы слева.
# Однако, иногда нужно задать пустой блок кода, если не написать ничего, интерпретатор будет ругаться,
# для того, чтобы избежать этого используется ключевое слово pass
if False:
pass
else:
print("bla-bla")
def empty_func():
pass
# 4.6 Цикл с пост-условием и оператор множественной проверки switch\case
# Их нет :(
# И не пытайтесь их искать в документации
# Бесконечный цикл и оператор break вам в помощь
while True:
if условие:
break
# и цепочки сравнений
if условие:
блок кода
elif другое условие:
другой блок кода
elif ещё одно условие:
ещё один блок кода
else:
блок кода, если не выполнилось ни одно условие
# 4.7 Оператор else в циклах
# Циклы поддерживают оператор else
for i in range(10):
#if i > 6: break # * строка 4.5 со звёздочкой
if i % 2: continue
print(i)
else:
print("finish", i)
# который выполняется после завершения цикла, даже если его тело не выполнилось ни разу.
for i in []:
print(i) # тело цикла
else:
print("finish")
# else не выполняется в случае выхода на break и при возникновении исключения(о них позже)
# Задание 4.7.1: выполните пример из начала раздела 4.5 и уберите комментарий с условия в строке со звёздочкой
# 5 Генераторные выражение и if-выражение
# условие может быть записано в одну строку
a = None
print("hello user" if a is None else "hello " + a) # -> hello user
a = "vanya"
print("hello user" if a is None else "hello " + a) # -> hello vanya
# последовательности можно задавать не поэлементно, а лишь указав правило вывода
a = (2**i for i in range(5))
# данное выражение создаст генератор, по сути, правило вычисления элемента, а не саму коллекцию элементов
print(a) # -> <generator object <genexpr> at длинный адрес>
# чтобы создать коллекцию, можно воспользоваться приведением типов
print(list(a)) # -> [1, 2, 4, 8, 16]
# допустимы вложенные циклы for
a = (x + y for x in range(3) for y in range(3))
print(list(a)) # -> [0, 1, 2, 1, 2, 3, 2, 3, 4]
# данный код аналогичен обычным циклам
a = []
for x in range(3):
for y in range(3):
a.append(x + y)
print(a)
# но питоняши предпочитают первую запись, потому как она занимает меньше места
# программисты на python очень любят писать все алгоритмы в одну строку :)
# в генераторных выражениях так же допустимы условия
a = [i for i in range(10) if i % 2 == 0]
print(a) # -> [0, 2, 4, 6, 8]
# Задание:
# 1. Допустим, вы решили заняться биоинформатикой. И вас интересует,
# насколько часто в геноме встречается определённая последовательность нуклеотидов
# доп инфо https://ru.wikipedia.org/wiki/Генетический_код
# входные параметры:
# genome: секвенированный геном (последовательность из нуклеотидов, обозначаемых буквами a,g,c,t)
# pattern: последовательность, для которой необходимо вернуть количество вхождений
# возвращаемое значение: количество вхождение pattern в genome
# Используйте срезы, не используйте стандартный метод count() :)
# Решение:
def count_pattern(genome, pattern):
result = 0
for i in range(len(genome) - len(pattern) + 1):
if genome[i: i + len(pattern)] == pattern: # срезы python позволяют вам обойтись без внутреннего цикла
result += 1
return result
s = count_pattern("actgac", "ac")
print(s)
# 2. На основе функции count_pattern напишите ф-цию,
# которая будет возвращать индексы вхождений pattern в
# "acgtcgttac", "cg" - > [1, 4]
# 3. Перепишите её в генераторное выражение
# 4. Перепишите ф-цию count_pattern, чтобы она вызывала пред. ф-цию,
# а возвращала длину вычисленного ею списка
# 5. Реализуйте функцию вычисления факториала
# 6. Реализуйте функцию вычисления двойного факториала https://ru.wikipedia.org/wiki/Факториал#Двойной_факториал
# 7. Реализуйте функцию подсчёта n первых чисел последовательности Фибоначчи
# 8. Реализуйте функцию подсчёта n первых простых чисел
# 9. Напишите генераторное выражение, возвращающее пары из не повторяющихся элементов из двух последовательностей
# [1, 2], [1, 3] -> [(1, 2), (1, 3), (2, 3)]
# 10. Напишите генераторное выражение, возвращающее только положительные значения из какой-то последовательности
# a = [-3, -5, 30, 0, 34, -47]
# print([немного вашего кода for i in a if ещё немного кода])
# 11. Реализуйте функцию, которая принимает две последовательности и выдаёт новую,
# в которую входят элементы из первой последовательности, которых нет, во второй
# [0, -1, 3, 1], [2, 4, 5, 1, 0] -> [-1, 3]
# 6. Работа с датами и временем
# Прежде всего следует запомнить, что работа с датами и временем очень сложна, поэтому всегда используйте стандартные типы.
# Например, если вам нужно хранить дату, не используйте массив из трёх чисел.
# Почему не стоит так поступать?
# Задание 6:
# 1. Напишите функцию, которая принимает номер года (в виде целого числа) и возвращает високосный он или нет
# Многие люди ошибочно считают, что високосным является любой год, номер которого делится на 4 без остатка,
# однако это далеко не всегда так, например 1900 год не был високосным, в отличие от 2012 и 2000,
# на самом деле високосный год - это год, который делится на 4, а если он делится на 100, то он ещё должен делится и на 400
# Задание 6:
# 2. Если в задании 6.1 вы использовали неправильный алгоритм, исправьте его.
# Как видите, даже определение такого банального параметра оказывается не таким уж лёгким,
# А если прибавить к этому високосные секунды https://ru.wikipedia.org/wiki/Дополнительная_секунда
# Переходы на летнее\зимнее время и часовые пояса, которых совсем не 24
# https://ru.wikipedia.org/wiki/Часовой_пояс#Список_часовых_поясов то всё становится совсем весело. :)
# Для работы с датами в python существует модуль datetime, содержащий типы date, time и datetime.
# https://docs.python.org/3/library/datetime.html
# date
d = datetime.today() # получение текущей даты и времени
print(d.timetuple())
# при создании datetime необходимо указывать три параметра: год, месяц и число, время является необязательным параметром
print(datetime(2009, 2, 28, minute=1).strftime("%Y %b %d %H:%M")) # для вывода используется ф-ция strftime
# strptime выполняет обратную операцию, с датами определены операции суммирования и разности
delta = datetime.strptime("2010 Mar 28 00:01", "%Y %b %d %H:%M") - datetime(2009, 2, 28, minute=1)
print(delta)
# Странная строка "%Y %b %d %H:%M" обозначает формат, в котором строка будет\была представлена
# Подробнее о формате можно прочесть здесь https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
# Задание 6:
# 3. Рассчитаете сколько времени прошло с момента вашего рождения (в секундах)
# 4. Определите когда был\будет ваш 1 млрд-секундный юбилей.
# 5. Выведите ответы из заданий 3 и 4 в разных форматах.
# 7. Строки и форматирование вывода
# В python существует несколько способов вывода
print("result" + str(2)) # Простая конкатенация строк
print("result", 2) # print может принимать несколько аргументов через запятую,
# в таком случае они будут выводиться через пробел,
# вам не нужны преобразовывать выводимые объекты в строку,
# в отличие от предыдущего способа
print("result %d" % 2) # %-синтаксис, сделан по аналогии с языком C.
print("result %d %.2f" % (2, 2)) # https://docs.python.org/3.4/library/string.html#formatspec
print("result %(name)s" % {"name": 2}) # также разрешено создавать именованные метки
print("{}".format(2)) # У класса строки есть метод format()
# он позволяет опускать тип выводимой переменной
print("{0} {1} {0}".format(1, 2)) # так же можно указать номер переменной и таким образом
# вывести её два раза
# нумерация начинается с нуля
# если число переданных переменных меньше использованных в выводе, будет сгенерированно исключение
print("{} {}".format(2)) # -> IndexError: tuple index out of range
print("{0} {0}".format(2, 3)) # -> 2 2 Однако если передано слишком много переменных
# код отработает без ошибок
from math import pi # при таком выводе так же поддерживаются строки формата
print("{:.2f}".format(pi)) # -> 3.14
from string import Template # возможен и такой способ вывода
s = Template("result $res") # однако он не получил большого распространения
print(s.substitute(res = [3, 4]))
# В python3.6 появился новый способ форматирования - f-строки
from math import pi
result = 4
name = "user"
print(f"{name:84s} pi= {pi:.2f}, result={result}, {name[2]}")
# -> user pi= 3.14, result=4, e
# в фигурных скобках можно указывать любую переменную из области видимости,
# нет нужды дополнительно передавать переменные как параметр в функцию вывода\создания строки,
# они поддерживают индексирование по спискам, получение значений из словарей, строки формата
# в том числе и для дат.
from datetime import datetime
print(f"{datetime.now():%Y:%m-%d}")
# кроме того они быстрее всех остальных способов вывода [ https://shultais.education/blog/python-f-strings ]
# так что, если вам доступен python3.6 рекомендуется использовать именно их.
# хотя во всех предыдущих примерах форматированные строки использовались для вывода, важно понимать, что они являются
# обычными строками, над которыми разрешены все строковые операции, например:
# конкатенация
"asd" + "bbb" # -> "asdbbb" т.е. создаётся новая строка, в которую последовательно входят аргументы
# очевидно, что в общем случае a + b != b + a
# Хотя в последующих примерах для создания строк используются двойные кавычки, важно понимать, что никакой разницы
# с одинарными нет, строки точно так же можно задавать и одинарными, например 'hello'
# При создании строк одним их этих двух способов, в них не могут находиться знаки переноса строки.
a = "hello
user" # -> SyntaxError: EOL while scanning string literal
# однако они могут идти друг за другом, в этом случае используется символ \, обозначающий, что текущая команда
# продолжается на следующей строке
a = "hello" \
"user"
# для добавления знака переноса используйте служебную последовательность \n
a = "hello\nuser"
print(a) # -> hello
# -> user
# либо же создавайте строки с помощью трёх двойных кавычек, учтите, что в таком случае ваша строка сохранит всё форматирование,
# которое вы задали ей в коде
a = """hello
user""" # -> 'hello' Кавычки используются здесь лишь для подчёркивания того, что сохранится
print(a) # -> ' user' табуляция, выводиться они не будут
a = "Моя длинная строка"
print(a.index("строка")) # -> 12 - поиск подстроки в строке
print(a.index("fdfd")) # -> ValueError: substring not found - если подстрока не найдена произойдёт исключение
print(a.find("строкаdfdfd")) # -> -1 - Служит для тех же целей что и index(), но если строка не найдена вернёт -1
a = "my my my"
print(a.rfind("my")) # -> 6 - Рассмотренные выше ф-ции возвращают индекс первого вхождения,
# если вам необходимо получить индекс последнего используйте ф-ции с префиксом r-
print(a.split(" ")) # -> ['my', 'my', 'my'] - Разбивает строку на список строк,
# в качестве параметра передаётся разделитель
# параметр может опускаться, в таком случае разделителем
# будет считаться любой пробельный символ (пробел, \t, \n)
print("-".join(a.split(" "))) # -> my-my-my - осуществляет обратное преобразование, объединяет список в одну строку
# метод вызывается у строки разделителя, а не коллекции,
# возможно использовать пустую строку
print("".join(["abc", "true"])) # -> abctrue
print(" ".join(["abc", True])) # -> TypeError: sequence item 1: expected str instance, bool found
# у ф-ции есть важное ограничение: она не осуществляет автоматического
# преобразования элемента к строковому виду,
# мы научимся обходить это ограничение позже
print(a.replace("my", "your", 2)) # -> your your my - осуществляет замену k подстрок на новое значение
# k передаётся третьим параметром, по умолчанию равно -1 - замене всех вхождений
print(a.startswith("my")) # -> True - как следует из названия проверяет начинается на строка с переданной
print(a.endswith("yours")) # -> False - похожа на предыдущую, но проверяет, заканчивается ли строка переданной
print(a.count("my")) # -> 3 - подсчитывает количество вхождений подстроки
a = "my aNother linE"
print(a.capitalize()) # -> My another line - делает первый символ заглавным(прописным)
print(a.title()) # -> My Another Line - делает первый символ слова заглавным, а все остальные строчными
print(a.swapcase()) # -> MY AnOTHER LINe - меняет регистр всех букв
print(a.lower()) # -> my another line - приводит все буквы к нижнему регистру
print(a.upper()) # -> MY ANOTHER LINE - к верхнему
print(" str with spaces ".trim()) # -> 'str with spaces' - удаляет все пробельные символы в начале и конце строки
# ВАЖНО: Предыдущие ф-ции не меняют исходную строку, а лишь возвращают изменённую версию, для изменения значения строки,
# необходимо использовать оператор присваивания
a = a.title()
# можно проверять частные случаи строк с помощью ф-ций с префиксом is-
# например, осуществлять контроль пользовательских данных перед приведением типов.
print(a.isdecimal()) # -> False
print("34".isdecimal()) # -> True
# 8. Множества и словари.
# 8.1 Множество(set) - неупорядоченная совокупность элементов, при этом каждый элемент может входить в множество только один раз.
# Множества создаются перечислением элементов в фигурных скобках
a = {1, 2, 3, 2, 3, "2", None}
print(a) # -> {1, 2, 3, '2', None}
# Множества можно создавать из других коллекций и строк
str = set("Dear, mr. Martin")
print(str) # -> {'i', 'r', 'D', ',', '.', 'M', ' ', 't', 'n', 'm', 'a', 'e'}
l = set([1, 2, 3, 2, 3])
print(l) # -> {1, 2, 3}
# 8.2 Словарь(dictionary) - коллекция, хранящая пары ключ-значение.
# Похожи на словари, но ключом может выступать не только целое число, а вообще любое число, строка или кортеж.
# Так же задаются фигурными скобками
a = {1: "one", "zero": 0, (1, 2): "pair", 3.14: "pi"}
print(a) # -> {1: 'one', 'zero': 0, (1, 2): 'pair', 3.14: 'pi'}
# Словари могут создаваться с помощью вызова dict() в том числе и из других коллекций
a = dict(one=1, two=2, str="str") # этим способом можно пользоваться, когда вы не хотите вводить все кавычки сами
print(a) # -> {'one': 1, 'two': 2, 'str': 'str'}
# в том числе и из других коллекций
a = [(1, 1), (2, 2)]
a = dict(a)
print(a) # -> {1: 1, 2: 2}
# однако, необходимо передавать коллекцию из пар, чтобы python понял, где ключ, а где значение
a = (1, 1, 2, 2)
a = dict(a) # TypeError: cannot convert dictionary update sequence element #0 to a sequence
# синтаксис операций чтения, записи и удаления значений аналогичен синтаксису при работе со списками
a = dict(one=1, two=2, str="str")
print(a['one']) # -> 1
del a['str']
print(a) # -> {'one': 1, 'two': 2}
# для создания нового значения, нужно всего лишь его присвоить
a['three'] = 3
print(a) # -> {'one': 1, 'two': 2, 'three': 3}
# Если вы уже не новичок в программировании, то словари и множества в python построены на хеш-таблицах,
# если же вы новичок, то у вас мог возникнуть вопрос, зачем нужно списки, если словари тоже поддерживают целые индексы,
# однако, накладные расходы на создание и поддержание словарей обычно больше, чем при работе со списками,
# так что рекомендуется использовать словари только там, где они действительно необходимы
# 8.3 Генераторные выражения
# Словари и множества так же поддерживают создание с помощью генераторного выражения
a = {x + y for x in range(4) for y in range(4)}
print(a) # -> {0, 1, 2, 3, 4, 5, 6}
a = {i: i ** 2 for i in range(5)}
print(a) # -> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 8.4 Проверка вхождения
# здесь тоже всё стандартно - она выполняется с помощью оператора in
a = {1, 2, 3}
print(1 in a) # -> True
print(0 in a) # -> False
# в случае словарей проверяется ключ, а не значение
a = dict(moja=1, mbili=2, tatu=3)
print('moja' in a) # -> True
print(1 in a) # -> False
# 8.5 Итерирование
for a in {"a", 1, 2, 3}:
print(a) # В моём случае вывело 3 1 a 2
# ВАЖНО: Следует учесть, что интерпретатор python не сохраняет порядок вставки элементов, так что вам не стоит на него полагаться.
# Это было верно и для словарей до версии 3.7 https://www.python.org/downloads/release/python-370/
# хотя, по факту, словари стали сохранять порядок и раньше, но с версии 3.7 это часть спецификации языка,
# так что вы можете полагаться на неё, если же вы работаете на более старых версиях используйте
# collections.OrderedDict https://docs.python.org/2/library/collections.html#collections.OrderedDict
d = dict(zero='Cero', one='Uno', two='Dos', three='Tres', four='Cuatro',
five='Cinco', six='Seis', seven='Siete', eight='Ocho', night='Nueve')
# при использовании обычного цикла for итерирование происходит по ключам
for key in d:
print(key)
# обратиться к элементу можно, используя этот ключ
for key in d:
print(d[key])
# для итерирования лишь по значениям можно использовать метод values() (метод keys() так же присутсвует)
for value in d.values():
print(value)
# если вы хотите минимизировать обращения по индексам и сделать ваш код чище, можно сразу получать пару ключ-значение
# используя метод items()
for key, value in d.items():
print(key, value)
# словари так же поддерживают enumerate (данный код полагается на то, что словари сохраняют порядок вставки)
for index, (key, value) in enumerate(d.items()):
print(f"{index} is {key} in England and {value} in Spain")
# ВАЖНО: В языке python есть одна интересная особенность: в нём 1 == True, а 0 == False,
# поэтому невозможно поместить их в словарь и множество одновременно,
# во избежание не очевидных результатов, лучше их не смешивать.
a = {0, 1, 2, True, False}
print(a) # -> {0, 1, 2}
a = {0:"zero", 1:"one", 2:"two", True:"true", False:"false"}
print(a) # -> {0: 'false', 1: 'true', 2: 'two'}
# Задание 8
# 1. Язык Суахили https://ru.wikipedia.org/wiki/Суахили является агглютинативным, это значит, что выражения вроде
# "я буду читать" являются в нём одним словом - nitasoma, состоящим из трёх частей:
# a. префикс, передающий лицо или объект, совершающий действие, например:
# ni - я
# u - ты
# a - он\она
# б. показателя времени, в данном случаи -ta- - будущее время
# в. смысловой основы, в данном случаи -soma - читать
# Реализуйте простейший переводчик с английского языка на суахили (английский выбран из-за простоты словоизменения)
# I will read -> ninasoma
# Можете использовать следующие основы:
# nunua - buy
# andika - write
# safiri - travel
# cheza - play
# kula - eat
# Можете добавить свои https://glosbe.com/en/sw/write
# 2. Решите противоложную задачу: переведите слово с Суахили на английский.
# 3. http://bioinformaticsalgorithms.com/images/Antibiotics/genetic_code.png
# На данном рисунке изображена схема трансляции оснований ДНК в аминокислоты, нужно начинать от центра и двигаться к краю.
# Реализуйте ф-цию, которая будет преобразовывать последовательность букв, кодирующих основания ДНК,
# в последовательность букв, кодирующих аминокислоты.
# "CCUCGAACCGAGAUUAACUAG" -> "PRTEIN*"
# 4. Словари могут использоваться как кеш, для хранения уже вычисленных результатов ф-ции, например для квадрата
cache = {i: i ** 2 for i in range(5)}
def square(x):
if x in cache:
print("from cache")
else:
print("to cache")
cache[x] = x ** 2
return cache[x]
print(square(3))
print(square(5))
print(square(5))
# так же допустимо использовать в качестве ключа кортежи, реализуйте ф-цию принимающую два аргумента (например, умножение),
# которая так же будет кэшировать\извлекать уже вычисленные значения из словаря
# 5. Создайте строковую переменную, скопируйте в неё какой-нибудь рассказ, статью.
# Удалите все знаки препинания, подсчитайте количество уникальных слов.
# Во избежание двойного счёта слов, написанных в разном регистре, перед подсчётом преобразуйте их к нижнему регистру.
# 6. Создайте ф-цию подсчёта числа различных цифр в числе.
# 1978 -> 4
# 2020 -> 2
# -3477 -> 3
# 7. Создайте функцию перевода чисел из римской записи в арабскую
# 1 - I
# 5 - V
# 10 - X
# 50 - L
# 100 - C
# 500 - D
# 1000 - M
# 8. И наоборот
# 9. ООП
# Если вы новичёк в программировании, вам может быть полезно изучить статью на вики
# https://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование.
# Если же вы уже знакомы с принципами ООП, то можете изучить официальную документацию python
# https://docs.python.org/3/tutorial/classes.html
# Следует указать, что в языке python всё является объектом, сами объекты, ф-ции, стандартные типы, даже классы
# Классы объявляются с помощью ключевого слова class, допустимо создание пустых классов
class EmptyClass():
pass
# В круглых скобках указываются классы, от которых наследуется данный, да, классы, в python разрешено множественное наследование,
# но об этом позже.
# Ниже представлен набор классов, демонстрирующий шаблон проектирования(паттерн) декоратор.
# Шаблонам проектирования посвящена одна из книг серии Head First "Head First Object-Oriented Analysis and Design",
# есть её перевод на русский язык.
class Cup():
COFFEE_NAME = "CHAYKOVSKY INC."
__cup = None
_name = "cup"
def __init__(self, cup=None): # В языке python существует договорённость о том,
self.__cup = cup # что методы, чьё название начинается с двух знаков подчёркивания
# являются служебными, например __init__ - это конструктор
def __eq__(self, other): # а метод __eq__ вызывается при проверке двух объектов на равенство
return self._name == other._name
def price(self):
if self.__cup != None:
return self.__cup.price() + self._price
else:
return self._price
class Latte(Cup):
_price = 2
_name = "Latte"
class Espresso(Cup):
_prise = 3
class Syrup(Cup):
_price = 1
class Sugar(Syrup):
_price = 1
class Marshmellow(Syrup):
_price = 1
c = Cup()
с = Marshmellow(c)
c = Sugar(c)
print(c.price())
c2 = Latte()
c3 = Latte()
print(c2 == c) # при сравнении любых объектов python вызывает метод __eq__
print(c2 == c3)
# 10. Договорённости по оформлению кода
# У любого языка есть стандарты, которых желательно придерживаться при написании кода.
# Если все программисты придерживаются единообразного оформления, это позволяет проще понимать
# и модифицировать чужой код.
# Подобные правила для языка python собраны в документе, называемом PEP8 https://www.python.org/dev/peps/pep-0008/
# Аббревиатура PEP (Python Enhancement Proposals) обозначает документы, содержащие изменения и дополнения,
# предлагаемые к внесению в стандарт, после обсуждения они или входят в стандарт или отклоняются.
# 11. Импорт модулей
# 11.1 Базовая информация
# Код приложения принято разделять на структурные части, например, хранить каждый класс в отдельном файле,
# или разделить код на отдельные библиотеки, например, в один модуль поместить код для работы с датами,
# в другой для работы с двумерными массивами, в третий уровень абстракции от какой-то физической системы.
# Так как у unit-тестов механизм запуска отличается от механизма запуска обычного скрипта, тесты лучше выносить
# в отдельные файлы. Кроме того, это позволит не запутаться при чтении кода.
# для импортирования модулей служит команда import
import numpy
# после этого в основном коде можно вызывать методы импортированного модуля с префиксом имени модуля
a = numpy.array([2,3,4])
# можно сократить кол-во символов, которые придётся печатать дописав после import ключевое слово as
import numpy as np
a = np.array([2,3,4])
# можно вообще обойтись без необходимости указывать префикс, если указать, какие ф-ции импортировать
from numpy import pi
# можно импортировать все ф-ции следующей командой
from numpy import * # Однако, делать так не рекомендуется, так как в случае использования нескольких модулей
# не всегда понятно из какого модуля импортирована эта ф-ция, что усложнит чтение вашего кода
# Ещё большее веселье может наступить, если вы импортируете таким образом модуль, в котором
# так же имеется такая команда.
# 11.2 Механизм импортирования
# Существует возможность ограничить ф-ции, импортируемые из ваших модулей командой from import *
# Если в модуле объявлена переменная __all__, то импортируются лишь имена, указанные в ней,
# вы можете в теле своего модуля сделать
__all__ = []
# Заставив пользователей своего модуля явно перечислять все импортируемые ф-ции :)
# На практике же лучше не использовать данные возможности и обходиться без from import *
# Есть несколько исключений из этого правила, например при создании unit-тестов.
# Если вы пишите какой-то модуль, то вполне логично положить рядом с ним файл с тестами, который импортирует так
# ф-ции модуля, так как ожидается, что вы будете тестировать все ф-ции из этого модуля и только их, а для
# тестов другого модуля, соответственно создадите другой файл с тестами.
# ВАЖНО: При импортировании модуля выполняется весь его код, текст ф-ций и классов компилируется в байт-код, а все
# команды вне ф-ций (в глобальной области видимости) выполняются.
# например, создадим модуль user.py и поместим в него метод hello, который сразу и вызовем
def hello():
print("hello")
hello()
# теперь, если импортировать этот модуль в другом файле
from user import hello
# и запустить его, мы увидим hello в выводе
# чтобы избежать этого необходимо весь код, который вы хотите вызвать помещать в следующее условие
if __name__ == '__main__':
print(func(4, 3))
print(func2(4))
# "магический" атрибут __name__ содержит имя модуля, по которому ему импортировали, если модуль
# главный, т.е. именно он был вызван на исполнение то в это поле заносится строка '__main__'
# https://docs.python.org/3/library/__main__.html
# Задание 11.
# 1. Изменить модуль user.py, так чтобы вместо вызова метода hello на печать отправлялся атрибут __name__
# поэкспериментируйте с разными видами импорта, посмотрите, что заносится в __name__
# для возможности импорта имя модуля должно быть идентификатором,
# т.е. последовательностью из чисел, букв и знака подчёркивания, начинающейся на букву или знак подчёркивания
# можно помещать модули в отдельные папки, например, ваша структура проекта может иметь следующий вид
# \project папка project содержит папки libs, tests и файл main.py
# \libs папка libs содержит файлы bio.py,phys.py и geo.py
# bio.py
# phys.py
# geo.py
# \tests
# test.py
# main.py
# В таком случае для импорта из файла main любой библиотеки из папки libs потребуется более сложная форма
# с указание пути к файлу, этот путь повторяет путь в файловой системе, но разделителем между папками служит точка
from libs.bio import some_func
# 11.3 Механизм поиска
# Папка libs называется пакетом, строго говоря для того, чтобы она была пакетом терминах в неё python
# в неё ещё нужно поместить файл __init__.py (он может быть пустым, обычно он и является пустым)
# При импорте модуля python сначала обращается по пути sys.modules - специальный кэш,
# который ускоряет поиск модуля, этот кэш формируется из переменной sys.path,
# которая берёт значения из переменной окружения PYTHONPATH
# https://docs.python.org/3/library/sys.html#sys.modules
# Если python не может найти модуль он выбрасывает исключение ModuleNotFoundError,
# в данном случае сначала необходимо проверить, что имя файла и путь к нему совпадают с
# указанными в команде import и при этом являются допустимым идентификаторами,
# если это не помогло, можно явно добавить папку с этим модулем\пакетом в sys.path
import sys
sys.path.append(путь в файловой системе)
# более сложный, вариант :)
PACKAGE_PARENT = '..' # может потребоваться изменение этого значения, в зависимости от уровня вложенности файла скрипта
# в структуре проекта
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))
# https://docs.python.org/3/reference/import.html
# 12. Тестирование
# Проверка правильности кода - одна из насущных проблем в программировании.
# Пожалуй, её можно смело назвать третьей проблемой в программировании.
# При каждом изменении кода существует риск, что старый код перестанет работать, в то же время,
# как нам быть уверенными, что новый код выполняет свою задачу.
# Для этого используется тестирование, однако тестировать код каждый раз вручную довольно долго и скучно,
# поэтому эту монотонную задачу тоже решили переложить на компьютеры. Так возникли unit-тесты.
# Идея их использования следующая: мы пишем метод, который вызывает тестируемый код, а потом сравнивает результат
# вычислений с правильным (как получить этот правильный результат уже другая история).
# Существуют специальные фреймворки, которые служат для их создания. Пожалуй, самый популярный для python - unittest
# https://docs.python.org/3/library/unittest.html
# ВАЖНО: В стандартную поставку python этот модуль не входит, его необходимо установить отдельно,
# например, вызвав pip install unittest2 в консоли. При условии, что у вас уже стоит pip,
# если нет, то самое время исправить. https://pip.pypa.io/en/stable/installing/
# https://packaging.python.org/tutorials/installing-packages/
# Или в меню pycharm https://www.jetbrains.com/help/pycharm/installing-uninstalling-and-upgrading-packages.html
# 13. Разворачивание\оборачивание (wrapping) коллекций и продвинутая работа с функциями
# 13.1 Разворачивание\оборачивание
# как мы уже знаем, в python можно создать кортеж перечислением элементов через запятую
a = (1, 2)
print(a) # -> 1 2
b = 1, 2
print(b) # -> 1 2
print(a == b) # -> True
print(type(b)) # -> <class 'tuple'>
# однако, python поддерживает также и распаковку последовательностей на элементы
# для этого нужно перечислить справа столько переменных, сколько есть в кортеже
a, b = (1, 2)
print(a, b) # -> 1 2
print(type(a), type(b)) # -> <class 'int'> <class 'int'>
a, b = [3, 2]
print(a, b)) # -> 3 2
print(type(a), type(b)) # -> <class 'int'> <class 'int'>
# разрешено указывать слева несколько переменных
a, a, b = (1, 2, 3)
print(a, b) # -> 2 3 присвоение происходит слева-направо
# если некоторые элементы не нужны, их можно именовать нижним подчёркиванием _
# этим вы показываете тому, кто позже будет читать ваш код, что данная переменная использоваться не будет
_, a, b = (1, 2, 3)
# слева и справа должно быть одинаковое число элементов
a, b = (1, 2, 3) # -> 2 3 ValueError: too many values to unpack (expected 2)
# но что, если мы не знаем число элементов заранее?
# для таких случаев существуют так называемые жадные аргументы, они обозначаются *
first, *rest, last = range(10)
print(first, rest, last) # -> 0 [1, 2, 3, 4, 5, 6, 7, 8] 9
# они забирают все оставшиеся элементы, слева может быть только один жадный аргумент
# в противном случае python не поймёт, как нужно распределить элементы между ними
*first, *rest, last = range(10) # -> SyntaxError: two starred expressions in assignment
# если вам нужны только первый и последний элемент, можно явно подчеркнуть это
first, *_, last = range(10)
# разумеется с обычной коллекцией лучше всего использовать индексы
a = range(10)
first, last = a[0], a[-1]
print(first, last) # -> 0 9
# однако, данный способ работает только с последовательностями, у которых известно число элементов
# это известно далеко не всегда, например, данную конструкцию можно использовать при работе с файлами
# Ещё несколько полезных использований:
# * у строк есть метод rpartition, который принимает разделитель, ищет последнее его вхождение в строку
# и возвращает часть, идущую до разделителя, сам разделитель и часть после разделителя
# таким образом можно получить имя файла и его расширение
name, sep, extension = "my.homework.py".rpartition('.')
print(name, sep, extension) # -> my.homework . py
# или имя почты и домен
name, sep, domain = "[email protected]".rpartition('@')
print(name, sep, domain) # -> my.email @ email.com
# ВАЖНО: Данные примеры носят иллюстративный характер,
# в реальных приложениях лучше заменять их на вызовы из тематических библиотек
# это более читаемый метод, кроме того, обычно библиотечные методы работают быстрее
# можно обменять значения переменных в одну строку, т.е. присвоить одной переменной значение второй,
# в этой второй - значение первой
a = 3
b = 1
a, b = b, a
print(a, b) # -> 1 3
# 13.2 Передача параметров по ключу и распаковка/упаковка параметров ф-ции
# Создадим простую ф-цию, которая будет вычитать из первого аргумента второй
# и возвращать результат
def sub(a, b):
return a - b
# очевидно, что результат ф-ции будет зависеть от порядка передачи параметров
print(sub(2, 3)) # -> -1
print(sub(3, 2)) # -> 1
# в языке python допустима передача параметров по именам(ключам)
print(sub(a=3, b=2)) # -> 1 ключи можно перечислять в любом порядке
print(sub(b=2, a=3)) # -> 1
print(sub(3, b=2)) # -> 1 часть параметров можно передавать по ключам,
# другую часть старым позиционным методом
# при этом все ключевые аргументы должны идти после позиционных
#print(sub(a=3, 2)) # -> SyntaxError: non-keyword arg after keyword arg
# допустим, мы хотим создать ф-цию, которая бы принимала три параметра и вычитала второй и третий из первого
# мы, конечно, может создать новую ф-цию, назвать её, например sub3, и использовать её
# либо вызывать ф-цию sub цепочкой
print(sub(sub(5, 3), 2))
# оба варианта не очень красивы, а главное, если в будущем нам потребуется ф-ция sub4 или sub5
# наш код очень быстро начнёт пухнуть, либо мы можем начать путаться в цепочках вызовов
# но в python есть стандартные ф-ции, например min, которые могут принимать переменное число аргументов
print(min(2, 0))
print(min(0, 1, 3))
print(min(0, 1, 3, -1))
# подобные ф-ции можно задавать самому, для этого нужно применить операцию распаковки\упаковки последовательностей
# например, мы может передать элементы из кортежа в качестве параметров
a = (3, -1)
print(sub(*a))
# допустима и обратная операция (упаковка последовательных параметров в кортеж)
def sub(*argv): # упаковываем все переданные параметры в кортеж argv
result = argv[0] # указывать можно любое имя, однако обычно используется argv
for i in range(1, len(argv)): # рекомендуется использовать его, кроме как у вас есть
result -= argv[i] # веская причина использовать другое.
return result
a = (3, -1)
print(sub(*a)) # -> 4
print(sub(3, 2)) # -> 1
print(sub(5, 3, 2)) # -> 0
# Однако, в данной реализации есть и одна проблемы, вы её можете обнаружить?
# ...
# Проблема здесь в том, что при указании *argv нет нижнего ограничение на кол-во элементов,
# вполне возможно вызвать её совсем без аргументов, в данном случае ф-ция упадёт при обращении
# по 0-ому индексу в первой строке тела ф-ции.
# Это не совсем то поведение, которое пользователь ожидает от нашей ф-ции, кроме того, таким образом
# наружу утекает информация о внутреннем строении и реализации ф-ции, что так же считается плохой практикой.
# Конечно, всего этого можно избежать добавив проверку в теле ф-ции, но это усложнит наш код,
# да и зачем делать то, что python умеет делать сам, а именно, проверять,
# чтобы в ф-цию были переданы все необходимые параметры.
# Мы можем переписать ф-цию следующим образом:
def sub(first, *argv):
result = first
for i in argv:
result -= i
return result
print(sub()) # -> sub() missing 1 required positional argument: 'first'
# аргументы, передаваемые по ключу, тоже можно упаковывать
def func(**kwargs): # kwargs означает keyword arguments
print(kwargs) # рекомендуется без особой надобности его не менять
func(first=1, second=2, third="tatu") # при вызове ф-ции все её ключевые параметры
# будут упакованы в словарь
# ф-цию func можно вызвать без аргументов
func() # -> {}
# но нельзя вызывать с позиционными аргументами
func(1) # -> TypeError: func() takes 0 positional arguments but 1 was given
# возможно объявление ф-ции, которая применяет неограниченное число
# ключевых и позиционных параметров
def func(*args, **kwargs):
print(args)
print(kwargs)
func(0, "a", True, first=1, second=2, third="tatu")
# возможна и обратная операция: распаковывание словаря при вызове ф-ции
def sub(a, b):
return a - b
d = dict(a=4, b=3)
print(sub(**d))
# 13.3 Параметры по умолчанию
# Язык python поддерживает параметры по умолчанию, для этого нужно всего лишь указать
# данное значение после знака равно и имени параметра
def double(x, multiplier=2):
return x * multiplier
print(double(2)) # -> 4
print(double(3)) # -> 6
print(double(3, 4)) # -> 12
# однако с параметрами по умолчанию, надо быть осторожными
# объявим ф-цию, которая добавляет элемент в конец списка,
# если второй аргумент опущен, ф-ция добавляет элемент в пустой список
def add_to(elem, collection=[]):
collection.append(elem)
return collection
a = ["a", "c"]
print(add_to("b", a)) # -> ['a', 'c', 'b']
print(add_to("a")) # -> ['a']
print(add_to("b")) # -> ['a', 'b'] Откуда здесь 'a'?
# данное поведение возникает из-за того, что аргументы по умолчанию вычисляются и создаются
# в момент запуска скрипта, и в последствии не пересчитываются, т. е.
# данная ф-ция создаст список (изначально пустой) и будет использовать его как значение по умолчанию,
# не пересоздавая его каждый раз при вызове ф-ции.
# значения по умолчанию ф-ция хранит в поле __defaults__
print(add_to.__defaults__) # -> (['a', 'b'],)
# https://docs.python.org/dev/tutorial/controlflow.html#more-on-defining-functions
# исправить подобное поведение можно, если сделать значение по умолчанию неизменяемым типом,
# а список создавать в теле ф-ции
def add_to(elem, collection=None):
collection = collection or []
collection.append(elem)
return collection
# обратите внимание на команду
collection = collection or []
# это более короткий (и менее понятный, хотя не для всех) аналог
collection = collection if collection else []
# подобная операция возможна благодаря ленивости вычислений
# и тому, что None == False
print(None or []) # -> []
print([] or []) # -> []
print(() or []) # -> []
print("" or []) # -> []
print([1, 2] or []) # -> [1, 2]
# 13.4 Функции, возвращающее несколько значений
# В языке python такое возможно
def one_to_three():
return 1, 2, 3
# например, мы можем создать ф-цию, возвращающую минимальный элемент
# и его индекс в коллекции
def min(first, *args):
index = -1 # -1 из-за того, что first не входит в *args
for i, elem in enumerate(args):
if first > elem:
index = i
first = elem
return first, index + 1 # соответственно здесь мы должны увеличить индекс
print(min(3, 2)) # -> (2, 1)
print(min(2)) # -> (2, 0)
print(min(4, 5, 3)) # -> (2, 0)
# строки тоже можно распаковывать
elem, index = min(*"becon and beer") # что произойдёт, если не указать * перед строкой?
print("'{}', {}".format(elem, index)) # -> ' ', 5
# Задание 13.4
# 1. Допустим, у вас есть ф-ция записи на какое-то блочное устройство
def write(start, length, flag1=True, flag2=True, flag3=True):
print('{} {} {} {} {}'.format(start, length, flag1, flag2, flag3))
# где start - номер блока, с которого начинаем запись
# length - длина, количество блоков, которые нужно записать
# flag1-3 - какие-то ещё аргументы, например, типы записи,
# указатель на буфер и т.д.
# ф-ции работы с последовательностями зачастую имеют разный синтаксис,
# например, некоторые принимают индексы первого и последнего элемента,
# а некоторые количество элементов.
# Допустим, к вам приходят индексы первого и последнего блока,
# вам необходимо написать ф-цию, которая будет возвращать номер первого блока
# и количество блоков, которые необходимо записать, на основании
# номеров начального и конечного блока.
# 2. Передать результаты ф-ции из задания 1 в ф-цию write таким образом, чтобы они
# заняли место параметра start и length.
# То есть мы не хотим каждый раз при вызове write указывать в ней выражение для
# вычисления длины (оно может быть длинным, не читаемым и в нём мы рано или поздно ошибёмся).
# Можно вынести выражение для вычисления длины, но вызывать каждый раз
write(start, calculate_length(start, end), ...)
# тоже не хочется, так как это тоже долго, и можно сделать код короче и красивее.
#
# Разбор. (Сначала попытайтесь решить задачу сами)
def write(start, length, flag1=True, flag2=True, flag3=True):
print('{} {} {} {} {}'.format(start, length, flag1, flag2, flag3))
def get_range(start, end):
return end - start + 1
def get_start_and_range(start, end):
return start, get_range(start, end)
if __name__ == '__main__':
end = 100
begin = 10
print(get_start_and_range(begin, end))
write(begin, end - begin + 1, flag2=False) # плохо, рано или поздно мы ошибёмся,
# например, забудем + 1
write(*get_start_and_range(begin, end), flag2=False)
# 13.5 Передача аргументов только по ключу
# Допустим, мы хотим добавить в нашу ф-цию min значения начального и конечного индексов, т.о.
# ограничив поиск минимального элемента лишь каким-то диапазоном из всей коллекции
def min(first, lo=0, hi=None, *args): # что с ней может быть не так?
index = 0
hi = len(args) if hi is None else hi
print(lo, hi)
for i in range(lo + 1, hi):
if first > args[i]:
index = i
first = args[i]
return first, index + 1
a = 1, -4, 3, 10, 4, 16, 5, 6,
print(min(*a)) # в текущем виде, параметр lo
# станет равен -4,а hi - 3
# для того, чтобы это исправить, необходимо просто поменять аргументы местами
# данный метод несёт интересный побочный эффект - после этого параметры lo и hi
# можно будет задать только по ключу.
# Данный метод работает только в 3-ей версии python. Во второй не получится задать *args
# перед ключевыми аргументами. Ещё одна причина перейти на третью версию :)
def min(first, *args, lo=0, hi=None): # ф-ция всё ещё работает не верно
index = 0 # исправьте её в кач-ве упражнения
hi = len(args) if hi is None else hi
print(lo, hi)
for i in range(lo + 1, hi):
if first > args[i]:
index = i
first = args[i]
return first, index + 1
# Иногда мы просто хотим, чтобы некоторые параметры можно было передать по ключу,
# но при этом наша ф-ция не принимала неограниченное число параметров, в таком случае
# нужно просто передать * без имени
# https://www.python.org/dev/peps/pep-3102/
def min(first, *, lo=0, hi=None):
pass
min(1)
min(1, 2) # -> TypeError: min() takes 1 positional argument but 2 were given
# давно известная нам ф-ция print так же имеет параметры, которые можно указать только по ключу
# https://docs.python.org/3/library/functions.html#print
# изучите их.
# 13.6 Документирование
# в python любая строка следующая сразу после объявления ф-ции (также верно для классов и модулей)
# считается документацией этой ф-ции, в ней можно описать, что делает ф-ция,
# какие параметры принимает, их значения по умолчанию, какой результат ф-ция вернёт,
# всякие особенности реализации и примеры использования.
def pattern_indexes(genome, pattern):
"""Pattern Matching Problem: Find indexes of all occurrences of a pattern in a Genome.
:return: All starting positions in Genome where Pattern appears as a substring.
:examples: pattern_indexes("GATATATGCATATACTT", "ATAT") -> 1 3 9
"""
positions = []
for i in range(len(genome) - len(pattern) + 1):
if genome[i:i + len(pattern)] == pattern:
positions.append(i)
return positions
print(pattern_indexes("GATATATGCATATACTT", "ATAT"))
# документация помещается в поле __doc__, которое в любое время можно получить и прочесть
print(pattern_indexes.__doc__) # обратите внимание, что в данной строке отсутствуют скобки
print(sum.__doc__) # sum() - вызов ф-ции, а просто sum - объект ф-ции
print(min.__doc__) # с ним так же можно работать, как с другими объектами в python
# получать поля или даже передавать как аргументы в др. ф-ции
# 13.7 Подсказки(hints)
# Python - динамически типизированный язык, это в частности даёт возможность не указывать
# в ф-ции типы передаваемых и возвращаемых значений.
# Однако, у человека (а также иногда и интерпретатора) это может вызвать определённые
# затруднения при чтении кода, во избежание этого, можно использовать подсказки типов
def pattern_indexes(genome: str, pattern: str) -> list:
positions = []
for i in range(len(genome) - len(pattern) + 1):
if genome[i:i + len(pattern)] == pattern:
positions.append(i)
return positions
print(pattern_indexes("GATATATGCATATACTT", "ATAT"))
# Важно понимать, что данные подсказки являются именно подсказками, т.е.
# никаких проверок данных осуществлено не будет.
print(pattern_indexes([1, 2, 3, 2, 4, 5, 2, 1], [2, 1])) # -> 6
# стандартный модуль typing предоставляет дополнительные, более подробные подсказки,
# например, можно указать тип данных, содержащихся в коллекции
from typing import List
def pattern_indexes(genome: str, pattern: str) -> List[int]:
# или то, что параметр может иметь несколько типов
from typing import Union
def pattern_indexes(genome: Union[str, list], pattern: Union[str, list]):
# Подробнее о типах и подсказках
# https://www.python.org/dev/peps/pep-0484/
# https://docs.python.org/3/library/typing.html
# Проверку во время выполнения решили пока не вводить https://www.python.org/dev/peps/pep-0316/
# Однако есть сторонняя библиотека https://andreacensi.github.io/contracts/
# реализующая контрактное программирование и дающая более широкие возможности.
# N.B. Автор данного текста не тестировал данную библиотеку, поэтому не
# может дать никаких гарантий и не несёт никакой ответственности. :)
# 13.9 Параметры
# Рассмотрим следующий код
def sqr_plus_one(a):
a = a ** 2 # в теле функции несколько раз
a += 1 # меняется значение переданной переменной
return a
a = 10
print(sqr_plus_one(a)) # -> 101
print(a) # Как вы думаете, какое значение сейчас хранится в a?
# Правильный ответ - 10, в теле ф-ции можно менять значения параметров, это не
# отразится на значении одноимённых параметров вне ф-ции.
# Мы уже несколько раз использовали данный ф-ционал, сейчас настало время наконец-то
# явно это прояснить.
# Впрочем, всё не настолько просто :)
# Перепишем нашу ф-цию так, чтобы они принимала на вход список
def sqr_plus_one(a):
for index in range(len(a)):
a[index] = a[index] ** 2 + 1
return a
a = [10]
print(sqr_plus_one(a)) # -> [101]
print(a) # -> [101] Списки относятся к изменяемым типам данных
# так что значения в нём будут переписанны
# Задание 3.9
# 1. Перепишите ф-цию sqr_plus_one, с использованием генераторного выражения,
# после чего перезапустите весь код, изменилось ли его поведение? Почему?
# 13.10 Элементы функционального программирования
# 13.10.1 Немного базовой теории и практики
# https://ru.wikipedia.org/wiki/Функциональное_программирование - немного информации, что это за зверь такой.
# Следует отметить, что python не является (и никогда не стремился быть) функциональным языком, он является
# мультипарадигменным, в нём представленны отдельные элементы ф-ционального программирования.
# https://docs.python.org/3/howto/functional.html#introduction - это же, но в доке python
# Прежде чем мы начнём, следует уяснить разницу между двумя следующими операциями
>>> print() # Выведет пустую строку на печать
>>> print # -> <built-in function print> вернёт встроенную ф-цию
# Т.е. когда мы указываем круглые скобки после ф-ции, python пытается её вызвать и вернуть результат,
# без круглых скобок мы просто ссылаемся на объект ф-ции. Зачем это нужно?
# например, можно передать ф-цию как параметр в другую ф-цию
# реализуем ф-цию которая применяет переданную ей ф-цию к каждому элементу коллекции и затем возвращает новую.
import math
def for_each(function, collection):
return [function(elem) for elem in collection]
a = range(0, 5)
print(for_each(math.factorial, a)) # -> [1, 1, 2, 6, 24]
print(for_each(math.sqrt, a)) # -> [0.0, 1.0, 1.4142135623730951, 1.7320508075688772, 2.0]
# 13.10.2 Ф-ция map
# на самом деле подобная задача встречается столь часто, что в python уже есть встроенная
# ф-ция map, делающая подобное.
a = range(0, 5)
print(map(math.factorial, a)) # -> <map object at 0x7fe5977a5208>
# для экономии памяти вычисление ф-ции лениво, то есть вместо вычисления ф-ции для каждого элемента
# ф-ция вернёт специальный объект, а непосредственно значение будет выполнено при обращении к эл-ту.
# из этого объекта можно явно создать список
print(list(map(math.sqrt, a))) # -> [0.0, 1.0, 1.4142135623730951, 1.7320508075688772, 2.0]
# ф-ция map может принимать несколько последовательностей, в таком случае в ф-цию будут передаваться
# по одному значению из каждой последовательности, например в модуль math есть ф-ция определяющия
# наибольший общий делитель
import math
a = [6, 12, 7]
b = [12, 24, 48]
print(list(map(math.gcd, a, b))) # -> [6, 12, 1]
print(list(map(math.sqrt, a, b))) # -> TypeError: sqrt() takes exactly one argument (2 given)
# Данную ф-цию, в принципе, можно заменить генераторным выражением
import math
a = [9, 16, 25]
print(list(map(math.sqrt, a)))
print([math.sqrt(elem) for elem in a])
# 13.10.3 Ф-ция zip
# Существует ещё одна ф-ция zip, которая принимает несколько коллекций и выдаёт кортежи из
# элементов, находящихся на одинаковых позициях
import math
a = [6, 12, 7]
b = [12, 24, 48]
print(zip(a, b)) # -> <zip object at 0x7fc9e5e84f08>
print(list(zip(a, b))) # -> [(6, 12), (12, 24), (7, 48)]
print(list(map(math.gcd, a, b))) # -> [6, 12, 1]
print([math.gcd(a, b) for a, b in zip(a, b)]) # -> [6, 12, 1]
# ф-ции zip можно передовать более двух коллекций
a = [6, 12, 7]
b = [12, 24, 48]
c = ["a", "b", "c"]
print(list(zip(a, b, c))) # -> [(6, 12, 'a'), (12, 24, 'b'), (7, 48, 'c')]
# если передать ей последовательности разной длины, ф-ция закончит работу после исчерпания самой короткой
c = ["a"]
print(list(zip(a, b, c))) # -> [(6, 12, 'a')]
# это же верно и для map
a = [6, 12, 7]
b = [12, 24]
print(list(map(math.gcd, a, b))) # -> [(6, 12, 'a')]
# пустая последовательность не генерирует ошибки
c = []
print(list(zip(a, b, c))) # -> []
# 13.10.4 Ф-ция filter
# Принимает первым параметром ф-цию, а вторым последовательность, в результате выдаёт значения из коллекции,
# для которых ф-ция вернула True (или эквивалентное ему значение, то есть не 0, не пустую коллекцию и не None,
# которые назовём для кратности falsy)
def is_even(n):
return n % 2
print(filter(is_even, range(10))) # -> [1, 3, 5, 7, 9]
# на место первого варианта можно передать None, в этом случае вернёт все не falsy значения
a = 0, [], [0], (), {}, "a", "", " ", 100, True, False
print(filter(None, a)) # -> ([0], 'a', ' ', 100, True)
# вызовы ф-ции аналогичны следующим генераторным выражениям
print([i for i in range(10) if is_even(i)])
print([i for i in a if i])
# Данная ф-ция принимает только два параметра: ф-цию и коллекцию, если вы не хотите указывать ф-цию,
# нужно явно передать None, так же не получится передать в ф-цию несколько коллекций.
# 13.10.5 Ф-ции any и all
# Ф-ция any возвращает true, если в коллекции есть хотя бы один не falsy элемент, а ф-ция all,
# если они все в коллекции не falsy или коллекция пустая.
a = 0, "a", "", 1
print(any(a)) # -> True
print(all(a)) # -> False
print(any([]), all([])) # -> (False, True)
# 13.10.6 Ф-ция reduce
# https://docs.python.org/3/howto/functional.html#built-in-functions
# TODO (закрыто на обслуживание)
# * разделение словаря на два списка: ключей и значений, при этом значение, соответствующее ключу
# будет находиться по тому же индексу
(keys,values) = zip(*d.items())
# 13.3 Сортировка
import operator
a = [1, 2, 5, 0, 10, 4]
print(sorted(a))
print(a)
a.sort()
print(a)
a = "AbraCadaBina"
print(sorted(a, key=str.lower))
a = (1, 2), (3, 5), (8, 9), (25, -4)
print(sorted(a, key=operator.itemgetter(1)))
# кэширующий декоратор
def cacher(func):
cache = {}
def decorated(*args):
if args in cache:
print "get from cache " + " ".join(str(x) for x in args)
else:
print "save to cache " + " ".join(str(x) for x in args)
cache[args] = func(*args)
print "cache:", cache
return cache[args]
return decorated
@cacher
def func1(x):
print "calculate for {}".format(x)
return x * x
@cacher
def func2(x, y):
print "calculate for {} {}".format(x, y)
return x * y
def main():
print func1(2)
print func1(3)
print func1(2)
print
print func2(2, 2)
print func2(2, 3)
print func2(2, 2)
if __name__ == '__main__':
main()
# логирующий декоратор
def log(before=None, after=None):
def decorator(func):
def decorated(*args):
if before is not None:
print(before.format(*args))
result = func(*args)
if after is not None:
print(after.format(*args))
return result
return decorated
return decorator
@log("before call: {} {}", "after call {} {}")
def func(a, b):
print ("call")
return a + b
@log(after="after call: {}")
def func2(a):
print ("call")
return a
if __name__ == '__main__':
print(func(4, 3))
print(func2(4))
# автодобавление метода __str__
from datetime import date
def auto_str(delimiter=', '):
def decorated(cls):
def __str__(self):
return '%s(%s)' % (
type(self).__name__,
delimiter.join('%s=%s' % item for item in vars(self).items())
)
cls.__str__ = __str__
return cls
return decorated
@auto_str(delimiter='\n')
class Person(object):
@auto_str()
class DateOfBirth(object):
def __init__(self, year, month, day):
self.date = date(year, month, day)
def __init__(self, name, surname, year, month, day):
self.name = name
self.surname = surname
self.dob = self.DateOfBirth(year, month, day)
john = Person("John", "Green", 1987, 5, 4)
print(john)
import math
print(2 % 3)
print(2.0 % 3)
print(None is None)
a = None
print(a == None)
print(a is None)
a = 2.3
print(int(a))
a = 2.8
print(int(a))
print(round(2.845, 2))
print(round(2.845 * 100.0) / 100.0)
print(math.ceil(2.3))
# Оператор is
# Задание:
# 1. Как показано в 2.9, в питоне 1 равно True. Определите, является ли 0 None, а False?
# 2. Определите с помощью оператора is, является ли 1 True, а 0 False?
a = {1, True}
print(a)
a = {1: "uno", True: "true"}
print(a)
print(len(set(objects)))
https://rushter.com/blog/python-memory-managment/
https://rushter.com/blog/python-garbage-collector/
https://habr.com/post/417215/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment