Last active
August 28, 2018 13:18
-
-
Save artsiomkaltovich/9b03753150a93a1510840a107d48a157 to your computer and use it in GitHub Desktop.
Python lesson
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 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