Size: a a a

2019 October 24
Python in Depth
Хочу поделиться новостью. Меня внезапно пригласили выступить на Science Slam в Питере через неделю. Science Slam -- это битва учёных в формате стенд-ап. У каждого спикера по 10 минут на рассказ о своей разработке и ещё по 5 на вопросы. Победителя определяют, замеряя с помощью шумомера, кого аудитория поддерживает больше.

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

Stay tuned🐠
источник
2019 November 01
Python in Depth
​​Выиграла со своей речью про беспилотники😱 Везу домой перчатки победителя.
источник
2019 November 11
Python in Depth
Введение в множества

Set' ы в Python реализованы так, что максимально напоминают математические множества. Давайте пройдемся по основным свойствам и возможностям множеств в Python, и разберемся, как их использовать.

В математике множество -- это набор объектов произвольной природы. В Python множество тоже может содержать переменные разных типов, например:
 
>>> A = {"My hovercraft is full of eels", 42, (3.14, 2.72)}

Но есть одно ограничение: элементы множества должны быть хэшируемыми: например, в множество можно добавить строки, числа и кортежи, но нельзя словари и списки:
 
>>> A.add([-1, 0])
TypeError: unhashable type: 'list'

При этом поскольку сами множества мутабельны, то множество множеств, как в известном парадоксе про брадобрея, сделать не получится. Но если очень хочется, можно использовать frozenset’ы: эти объекты в остальном ведут себя почти так же, но они иммутабельны и их можно добавить в множество.

Инициализировать множество можно используя фигурные скобки или через конструктор класса set(). Эти инициализации эквивалентны:
 
>>> B = {1, 2, 3}
>>> B = set((1, 2, 3))

Только не запутайтесь: такая инструкция  
 
C = {}

создаст не множество, а словарь.

В версиях интерпретатора 2.7 и выше работают set comprehensions:
 
>>> C = {x for x in range(1, 5)}
>>> C
{1, 2, 3, 4}

Элементы множества, как и в математике, должны быть уникальными. На практике этим пользуются для того, чтобы исключить повторяющиеся элементы:
 
>>> D = [1, 2, 3, 3]
>>> D = list(set(D))
>>> D
[1, 2, 3]

Кстати, множества не сохраняют порядок, поэтому если он нужен, то добро пожаловать в списки. Из-за того, что порядка нет, во множествах нет ни индексирования, ни слайсов. В то же время циклы по множеству можно делать обычным pythonic-способом:
 
>>> for elem in C:
...     print(elem)

Над множествами в Python можно делать те же операции, что и в математике: находить объединение, пересечение, проверять принадлежность к множеству и так далее. Для этого можно пользоваться операторами, а можно методами множеств:
 
A | B   A.union(B)
A & B   A.intersection(B)
A - B   A.difference(B)
A <= B  A.issubset(B)
A => B  A.issuperset(B)


Обратите внимание, что операторы принимают только set’ы, а методы -- любые iterable контейнеры. Есть мнение, что операторы менее читаемые, но оба подхода в целом равноправны.

И еще у класса set есть методы, которые удобны для работы со множествами как с коллекциями:

add() — добавить элемент,
remove() — удалить элемент,
pop() — извлечь с удалением,
update() — объединить с другим множеством,
clear() — очистить множество.

И напоследок: один раз я больно отстрелила себе ногу, когда хотела добавить составной элемент в множество с помощью неправильного инструмента. Например, если у нас есть множество строк и мы пытаемся добавить в него еще один элемент вот так:
 
>>> F = {"Seregia", "Vasia"}
>>> F.update("Alisa")

то получаем ожидаемый результат:
 
>>> F
{'l', 'a', 'Seregia', 'Vasia', 'A', 'i', 's'}

Это абсолютно валидный код и он отработает, поэтому такую ошибку по невнимательности можно искать довольно долго.  Так что не попадайте в ловушку методов add() и update().

В общем, Python set'ы сильно напоминают математические множества: могут содержать объекты разных типов,  требуют уникальности элементов, не сохраняют порядок и имеют методы, позволяющие их объединять, пересекать, etc... Кроме того, множества имеют интерфейс для работы с ними как с коллекциями.

Как определиться в выборе коллекции?
🐙 Если важен порядок — используйте списки.
🐙 Если нужно отображение ключ-значение — используйте словари.
🐙 Если нужен набор уникальных элементов — используйте множества.

#основы #типы_данных #comprehensions #списки #селяви #коллекции
Telegram
Python in Depth
Чем мутабельные объекты отличаются от иммутабельных?

В python объекты бывают мутабельные и иммутабельные. Значние иммутабельного объектра нельзя изменить после того, как он был создан, а значение мутабельного можно.

Рассмотрим на примере. В Python есть встроенная функция id(). В реализации CPython она возвращает адрес, по которому объект находится в памяти. Создадим список и посмотрим, в ячейке с каким номером он окажется.

>>>beatles = [“Ringo”, “Paul”, “John”, “George”]
>>>id(beatles)
140181376779336

Видим, что список лежит по адресу 140181376779336. Теперь изменим какой-нибудь элемент списка и посмотрим, изменился ли адрес.

>>>beatles[0] = “Pete”
>>>beatles
[“Pete”, “Paul”, “John”, “George”]
>>>id(beatles)
140181376779336

Видим, что адрес списка не меняется, если мы изменяем элементы, которые в него входят. Теперь создадим в памяти строку

>>>bass = "Paul"
>>>id(bass)
140068833595776

и добавим в нее что-нибудь

>>>bass += " McCartney"
>>>bass
Paul McCartney
>>>id(bass)
140068833600432

Видим, что…
источник
2019 November 16
Python in Depth
Умножение контейнеров

Один интересный факт из Python. Вы, наверное, знали, что для строк определен оператор умножения и что можно делать так:

>>> 'привет ' * 4
'привет привет привет привет '

То же, вообще говоря, работает с другими контейнерами, например, с кортежами:

>>> ('привет',) * 3
('привет', 'привет', 'привет')

И с этим все хорошо, пока внутри контейнера не появляется ссылка на изменяемый объект. Например, на словарь:

>>> d = [{'key': 'val'}] * 2
>>> d
[{'key': 'val'}, {'key': 'val'}]

Потому что теперь каждый из двух словарей доступен по одной и той же ссылке:
 
>>> d[0]['key'] = ''
>>> d
[{'key': ''}, {'key': ''}]

Упс, отстрелили себе ногу. Умножение -- это валидный способ создавать составные объекты только если вложенные неизменяемы. 🐠

#типы_данных #funfact #контейнеры #мутабельность #иммутабельность
источник
2019 November 19
Python in Depth
Как зарешивать, если нервничаешь.

На моём самом первом собеседовании мне предложили вот такую задачу: поменяйте местами две переменные таким количеством способов, которым сможете.

Задача не страшная, но на собеседованиях я обычно слишком нервничаю. Думать аналитически в таком состоянии я не могу, поэтому раньше это выглядело так: кандидатка получает условие и зависает в тишине на следущие пять минут. А когда атмосфера уже накаляется, начинает молча писать первое запутанное и неоптимальное решение. То ещё первое впечатление🧐  

Мне легче прийти в чувство на вайтбординге, если я решаю вслух. Действую по шагам: сначала я читаю условие для всех в комнате. Это помогает мне сфокусироваться на смысле, а не на своих потных ладошках. Если есть вопросы по требованиям, тут же их задаю: так я не трачу время на последующее переписывание кода из-за неправильно понятого текста.

Я не бросаюсь сразу писать, а сначала вслух проговариваю первый наивный алгоритм, который пришел в голову. На этом шаге я не хочу супероптимально, мне надо придумать хотя бы что-то. Это даст мне ощущение первого успеха, успокоит и поможет установить контакт с собеседующим. А интервьюеру даст возможность навести меня на мысль уточняющим вопросом, если он захочет.

Теперь я обдумываю краевые условия. Например, если я пишу функцию, которая обрабатывает строку, то что будет, если строка пустая? Или если я получаю два числа, то какие патологические варианты этих чисел могут прийти на вход? Проговариваю, как буду обрабатывать эти ситуации.

Потом я задаю себе вопрос, иногда прямо вслух: могу ли я оптимизировать решение? Пару минут я пытаюсь, давая собеседующим возможность подключиться к обсуждению😉, но они могут и просто ждать. И только теперь я берусь за ручку и пишу. Если оптимизировать не получается, я записываю первое неоптимальное решение: сейчас его наличие важнее эстетики. На работе так же: трагедия спринтов обычно в том, что можно красиво, а надо вчера.

И, если у меня ещё остаётся время, прямо на бумаге пишу тесты.

В общем, начинайте с малого и не молчите. Таким образом получается зафреймить процесс, сделать его более управляемым. Я делаю вот так:

🐢 Читаю условие вслух,
🐢 Проговариваю, как собираюсь решать,
🐢 Обдумываю краевые условия,
🐢 Пытаюсь оптимизировать,
🐢 Только теперь пишу код,
🐢 И, если надо -- тесты.

В то время у меня ещё не было опыта и то собеседование я провалила. Сейчас у меня есть какая-то тактика прохождения собеседований (и я ее придерживаюсь), и все проходит заметно легче.

А несколько вариантов, как поменять две переменные местами, я нашла тут. Если эта задача вам встретится, будете во всеоружии. Если на ум приходит ещё решение, отправьте его мне в обратную связь, а я сделаю отдельный пост с самым классным. 🐠
источник
2019 December 09
Python in Depth
Pickles

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

Во многих языках программирования есть встроенные инструменты для сериализации. Например, в Java есть java.io.Serializeable, в Ruby — Marshal, а в Python есть pickle. Смотрите, как просто этим инструментом сериализовать почти любой питонский объект:
 
import pickle

original = [1, 2, 3]

with open('myfile.pickle', 'wb') as outfile:
   pickle.dump(original, outfile)
with open('myfile.pickle', 'rb') as infile:
   identical = pickle.load(infile)

print(original == identical)  # True

Вся прелесть здесь в том, что можно в пару строк сохранить объект как он есть, без необходимости писать свой сериализатор или конвертировать данные в примитивные типы JSON.

Как это работает? Идея в том, что pickle содержит информацию о том, как реконструировать объект. Если мы попробуем запринтить строку, которую формирует pickle после сериализации списка выше, то увидим две вещи: ссылку на объявление класса, какое-то количество нечитаемых символов, и значения полей. Приблизительно это читается интерпретатором как “возьми вот этот класс и положи в него значения 1, 2, 3”.

Обратите внимание, что мы сохраняем состояние объекта, а не объявление. То есть, мы знаем содержимое полей этого класса, но остаемся в неведении относительно его структуры. Это важно для пользовательских типов, потому что в программе, в которой мы собираемся распаковывать pickle, этот класс должен быть объявлен. Иначе мы не сможем его распаковать -- непонятно, какой именно объект инициализировать этими данными.

За простоту использования pickle полюбили в data science. Оказалось удобно сохранять промежуточные результаты вычислений в полях класса, чтобы к расчетам можно было вернуться позже, без необходимости обучать модель заново.

Но у pickle есть несколько минусов.

1. Piсkle -- это питонский формат сериализации и пиклы не годятся для передачи объектов не то что между разными языками, а и иногда даже разными версиями интерпретатора Python. Поэтому если в вашей архитектуре есть модули, которые написаны не на Python, то, может быть, pickle для обмена данными -- не лучший выбор.

2. Pickle не человеко-читаемы. У pickle есть несколько серий протокола, ранние старались делать такими, чтобы их можно было читать глазами, поздние уже нет, но это все равно мало что изменило: если потом захочется поисследовать данные с помощью cat/head/grep и других полезных утилит bash, то вас будет ждать разочарование. Чтобы прочитать pickle с файловой системы, понадобится Python скрипт -- и это не очень удобно.

3. Pickle не слишком быстрые, по крайней мере, проигрывают модулю json в скорости.

4. И последнее: pickle небезопасны. Первое, что написано в документации к модулю -- это предупреждение:
 
Warning The pickle module is not secure. Only unpickle data you trust.

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

В общем, pickle это питонский формат для сериализации данных. Он удобен, когда нужно  на скорую руку сохранить состояние объекта. Pickle хорошо использовать в случаях, когда думаете, что файл не проживет долго и когда нет фокуса на вопросах безопасности. Зная все это, там, где можно, я все же выбираю JSON: это быстрее, это читаемее, это безопаснее. Правда, кода нужно писать чуть больше🐠

PS: в посте пара ссылок на доку и статьи, пооткрывайте, если есть минутка.
источник
2019 December 13
Python in Depth
Гид по множественному присваиванию

Уверена, что множественное присваивание в Python видели все. Выглядит оно вот так:

>>> x, y, z = 1, 2, 3
>>> x
1
>>> z
3

На самом деле на низком уровне создается tuple и в цикле инициализируется значениями 1, 2, 3. Поэтому с тем же успехом можно не опускать скобки и написать вот так:    

>>> (x, y, z) = (1, 2, 3)
>>> x
1
>>> z
3

И это тоже сработает. Поэтому множественное присваивание еще называют tuple unpacking. Да и чаще всего его, кажется, используют именно с кортежами. Но вообще так можно писать с любыми iterable типами:

>>> x, y = [1, 2]
>>> y
2

И со строками тоже:

>>> x, y = 'hi'
>>> x
'h'
>>> y
'i'

Множественное присваивание, если применять его с оператором *, позволяет делать еще вот такие крутые штуки:

>>> numbers = [1, 2, 3, 4, 5, 6]
>>> first, *rest = numbers
>>> first
1
>>> rest
[2, 3, 4, 5, 6]

По сути * здесь заменяет слайсы. С тем же успехом можно было написать:

>>> first, last = numbers[0], numbers[1:]
>>> first
1
>>> last
[2, 3, 4, 5, 6]

Но через распаковку получается читаемее. Можно даже <<вырезать>> середину:

>>> first, *middle, last = numbers

И если вам не нужны все промежуточные индексы, то хороший тон это вообще использовать нижнее подчеркивание:

>>> first, *_, last = [1, 2, 3, 4, 5, 6]

Кстати, если речь о вложенных объектах, то обратите внимание, что копия будет глубокой:

>>> color, point = ('blue', (0, 0, 255))
>>> color
'blue'
>>> point
(0, 0, 255)

В некоторых случаях это очень удобно.

В общем, множественное присваивание можно использовать не только с кортежами, но и списками, строками и любыми iterable типами. Его плюс в том, что оно позволяет не использовать индексы, а значит, уменьшает склонность к ошибкам и делает код читаемее. А еще оно используется в <<args, kwargs>> синтаксисе, который часто смущает начинающих, но об этом в следующий раз. 🐠
источник
2019 December 22
Python in Depth
Материалы

Уже не в первый раз в фидбэке просите посоветовать материалы по Python. В этом посте подборка ресурсов, которые нравятся мне самой.

Люди часто учатся на платных курсах, потому что это заставляет их ответственнее подходить к обучению: раз вложился деньгами, то надо закрывать дедлайны. Но я сама несколько травмирована системой образования с сессиями. Я считаю, что обучение это процесс личный, даже интимный, и надо давать студентам время. Иначе люди начинают либо забивать, либо сдавать домашки, не понимая их, либо ненавидеть процесс. Поэтому ниже набор бесплатных или условно бесплатных курсов и лекций, которые можно пройти в своем темпе. По возрастанию сложности:

🐟Базовый курс по Python (циклы, словари/строки/списки и прочие основы языка) есть на Stepik.

🐟Еще есть хорошая специализация на Coursera. Сначала основы языка, потом более продвинуто: детали, ООП на Python, тестирование, многопоточность, асинхронность и веб-программирование. Мне нравится, что все подробно, с примерами, с задачами на грейдере и близко к реальному миру.

🐟Дока к языку — это must. Можно начать с PEP8, PEP20 и FAQ.  

🐟Для продолжающих мне нравится Дэн Бейдер <<Чистый Пайтон>>. Книжка базовая, но очень человечная, автор объясняет вещи простым языком и дает примеры из своего опыта разработки. Но перед покупкой книжки советую посмотреть бложек, потому что книга сделана по его мотивам, только тексты расположены в порядке, удобном для усвоения.

🐟Часто рекомендуют Лутца <<Изучаем Python>> в двух томах, но я его не очень люблю. Есть такой буддистский коан, в котором учитель льет чай в чашку ученика, пока чай не начинает литься через край. Так же происходит, когда мы пытаемся слишком быстро что-то усвоить и в действительности материал не укладывается в голове. ИМХО, сложность в Лутце растет слишком быстро, но если это и в кайф, то вперед.

🐟В Питере, на моей исторической родине, есть Computer Science Center. Это что-то вроде независимой магистратуры, где читают курсы по программированию и открытые тематические лекции. У них есть бомбический канал на YouTube, где можно заценить уровень ребят. Очень советую этот курс по Python за глубину и хорошую проработку материала. На канале есть и другие курсы.

🐟Если хотите с головой окунуться в многопоточность, асинхронность и разобраться, что такое GIL, есть великолепные доклады с ливкодингом от гуру Python Дэвида Бизли.

🐟И если все еще не хватает хардкора, то Филип Гуо на протяжении 10 лекций разбирается в исходниках CPython. Это прям самое крутое из того, что находила в последнее время.

Не то чтобы это был необходимый запас для разработки, но если начал рекомендовать курсы, становится трудно остановиться. Еще есть алгоритмы (1, 2), основы линукса (1, 2), bash, сетей, git и баз данных(1, 2). Можно дополнить пет-проектом и наскребете аналог одного-двух лет бакалавриата по специальности.

А еще всегда можно оставаться на канале Python in depth, я здесь говорю о простых и сложных штуках. Если есть еще какие-нибудь запросы или можете чем-нибудь дополнить мой список, пишите в фидбэк, обсудим.

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

Учитесь и берегите себя! Всем классной, вашей, продуктивной недели. 🐠

#лекции #курсы #век_живи #основы
источник
2020 January 13
Python in Depth
Грусти пост о формулах в коде

Подсмотрела, как коллеги, которые пишут на C++, оформляют формулы. Здесь закомментировали черту и разбили ей формулу геометрической прогрессии на числитель и знаменатель. Получилась очень читаемо, а еще функция документирует себя сама!
 
//Sum of first n terms of a geometric series.
// S = a + ar + ar^2 + ... + ar^n

float geom_sum(int n, float a, float r)
{
   return
       (a*(1 - pow(r, n + 1)))
   / //-----------------------
              (1 - r);
}


Мне этот стиль очень нравится, но жаль, что интерпретатор так не позволяет писать в исполняемом коде. По крайней мере, у меня не получилось сделать, чтобы похожее форматирование заработало. Поэтому в теле функции я обычно пишу числитель и знаменатель отдельно, чтобы не было очень длинно, а выражение в docstring'е оформляю именно так. На Python получается что-то вроде:
 
def geom_sum(n, a, r):
   """
   Sum of first n terms of a geometric series.
   S = a + ar + ar^2 + ... + ar^n

         1 - pow(r, n + 1)
   S = a -----------------
               1 - r

   :param n: Number of terms
   :param a: First term
   :param r: Common ratio, r < 1
   :return: Sum of series
   """

   num = a*(1 - pow(r, n + 1))
   denom = 1 - r    
   return num/denom


Не стала обрабатывать случай расхождения ряда, я сейчас не об этом. А так хорошо бы. Такие дела! 🐠

#кодстайл #докстринг #формулы #комментарии #вообще_другой_язык #cpp
источник
2020 January 21
Python in Depth
​​Введение в декораторы

В нескольких следующих постах я хочу поговорить о декораторах. Будет базовое определение, мотивация их использовать, всякие хитрости, а еще куча примеров.

Прежде, чем говорить о декораторах, нужно кое- что узнать о функциях в Python. Допустим, у нас есть функция, которая здоровается с Юпи:

def hey_Jupi():
   print("Привет, Юпи!")

Функции в Python -- это объекты первого класса, ничем не хуже, чем int'ы или словари. Это значит, что:

🐙 Функцию можно присвоить переменной:

say_hi = hey_Jupi
say_hi()
# Привет, Юпи!

🐙 Функцию можно вернуть из функции:

def wrapper(func):
   print("Юпи пришла.")
   return func

hello_Jupi = wrapper(hey_Jupi)
# Юпи пришла.
hello_Jupi()
# Привет, Юпи!

🐙 Функцию можно определить внутри другой функции:

def deco(func):
   def wrapper():
       print("Юпи пришла.")
       func()
   return wrapper

hey_Jupi = deco(hey_Jupi)
hey_Jupi()
# Юпи пришла.
# Привет, Юпи!

Смотрите, что получилось на последнем шаге. На этапе создания deco никакой код не выполняется -- мы заходим в deco, видим, что здесь определена функция wrapper и возвращаем ее. Таким образом мы подменяем исходную hey_Jupi на wrapper и получаем новое поведение hey_Jupi, не изменяя ее код!

Это и называется декоратор. Это настолько удобный и мощный инструмент, что в Python для него придумали специальный синтаксический сахар. При условии, что функция deco у нас уже определена так же, как выше, можно добавить название декоратора с символом @ перед определением функции и получить эквивалентное поведение:

@deco
def hey_Jupi():
   print("Привет, Юпи!")

hey_Jupi()
# Юпи пришла.
# Привет, Юпи!

Кстати, этот же декоратор можно применить и к любой другой функции:

@deco
def take_five():
   print("Юпи, дай пять!")

take_five()
# Юпи пришла.
# Юпи, дай пять!

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

🐙 Декораторы используют в веб фреймворках для проверки авторизации или для разделения групп пользователей. Например, часть методов доступна только авторизованным пользователям, либо пользователям с определенной ролью, а остальные методы -- всем. Для этого нужные методы оборачивают в декораторы, которые делают необходимые проверки.
🐙 Декораторы позволяют проверить, что аргументы функции имеют нужный тип и значения. Это можно сделать на входе в функцию, но иногда проверки переносят в функцию-обертку.
🐙 С помощью декоратора можно замерять время выполнения функций.

В следующих постах разберемся, как комбинировать декораторы и передавать в декоратор параметры. Всем пять!🐠

#декораторы #функциональный_стиль #функции #основы
источник
2020 January 29
Python in Depth
Комбинируем декораторы

Допустим, у нас есть функции, которые работают со строками. Возьмем, например, функцию, которая возвращает строчку из Zen of Python:

def readability():
   return 'Readability counts'

print(readability())
Readability counts

А теперь мы хотим, чтобы можно было показать текст в двух вариантах: жирным и курсивом. Вот два декоратора, один из которых добавляет тег <<жирный>>, а второй <<курсив>> :

def bold(func):
   def wrapper():
       return '<b>' + func() + '</b>'
   return wrapper

def italic(func):
   def wrapper():
       return '<i>' + func() + '</i>'
   return wrapper

Как уже договаривались в прошлом посте, запись

@bold
def readable():
   return 'Readability counts'

Эквивалентна записи

readable = bold(readable)

И после применения декоратора вызов функции дает текст

print(readable())
<b>Readability counts</b>

А что если мы хотим получить текст одновременно и жирный и с курсивом? Можно сделать композицию декораторов:  

@italic
@bold
def readable():
   return 'Readability counts'

print(readable())
<i><b>Readability counts</b></i>

Обратите внимание, что результат в зависимости от порядка применения декораторов будет разным:

@bold
@italic
def readable():
   return 'Readability counts'

print(readable())
<b><i>Readability counts</i></b>

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

readable = italic(bold(readable))

А во втором -- наоборот:

readable = bold(italic(readable))

Читаемость имеет значение! И порядок тоже. 🐠

#декораторы #композиция #функциональный_стиль
Telegram
Python in depth
​​Введение в декораторы

В нескольких следующих постах я хочу поговорить о декораторах. Будет базовое определение, мотивация их использовать, всякие хитрости, а еще куча примеров.

Прежде, чем говорить о декораторах, нужно кое- что узнать о функциях в Python. Допустим, у нас есть функция, которая здоровается с Юпи:

def hey_Jupi():
   print("Привет, Юпи!")

Функции в Python -- это объекты первого класса, ничем не хуже, чем int'ы или словари. Это значит, что:

🐙 Функцию можно присвоить переменной:

say_hi = hey_Jupi
say_hi()
# Привет, Юпи!

🐙 Функцию можно вернуть из функции:

def wrapper(func):
   print("Юпи пришла.")
   return func

hello_Jupi = wrapper(hey_Jupi)
# Юпи пришла.
hello_Jupi()
# Привет, Юпи!

🐙 Функцию можно определить внутри другой функции:

def deco(func):
   def wrapper():
       print("Юпи пришла.")
       func()
   return wrapper

hey_Jupi = deco(hey_Jupi)
hey_Jupi()
# Юпи пришла.
# Привет, Юпи!

Смотрите, что получилось на последнем шаге. На этапе создания deco никакой код не…
источник
2020 February 03
Python in Depth
Знакомая, которая недавно устроилась на работу джуниором, рассказала, что в ее команде новым сотрудникам ставят цель либо закоммитить кусочек кода за день, либо задать два вопроса.

По-моему, это прекрасная практика. Многим сложно спрашивать: не хочешь отвлекать коллег от задач, боишься показать, что чего-то не знаешь, ... Особенно в начале карьеры. Особенно на новом месте. Ну, и застреваешь постоянно, как следствие.

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

#онбординг #soft_skillz #личное
источник
2020 February 09
Python in Depth
Давайте познакомимся

Я Маша Чакчурина, back-end разработчица🐠 Закончила физико-технический факультет в Питерском Политехе, занималась физикой Солнца, работала в компании СтарЛайн, и в их беспилотном автомобиле тоже. А год назад я переехала в Москву и проработала этот год в Касперском.

Канал этот веду потому что люблю объяснять другим то, что сама уже поняла. А еще я попала в программирование через непростые карьерные повороты и мне приятно думать, что я могу и другим помочь изменить свою жизнь к лучшему 🐳

Я не верю, что можно научиться чему-то серьезному за пару дней. Для меня всегда работало узнавать вещи по чуть-чуть. Лучше дать себе время разобраться, чем потом жалеть, что пропустила и не успела. Поэтому я выбрала формат канала, где я могу делиться информацией маленькими порциями, чтобы я была уверена, что она укладывается.

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

Надеюсь, что этот канал вам помогает узнать что-то новое или повторить уже известное.

Что можно сделать для меня:

🐬 Если у вас есть друзья, которые учат или хотят учить Python, поделитесь с ними ссылкой.
🐬 Напишите мне, если хочется читать о чем-нибудь определенном, и я об этом расскажу.
🐬 Если думаете, что я могу быть вам полезна, предложите мне сотрудничество.
🐬 О личном я пишу в инстаграме, добро пожаловать в мой профиль.  

Ну и всем успехов! А еще интересных, своих, драйвовых задач 🐠

#личное
источник
2020 March 25
Python in Depth
​​IT во время COVID-19

Эпидемия для всех разная: кому-то не хватает общения в карантине, у кого-то болеет знакомый, кто-то успел лишиться работы, у некоторых трещит по швам бизнес.

Но не надо думать, что борьба эпидемией касатеся только медиков: это социальный и экономический кризис. И мы, программисты и айтишники, можем в нем себя применить.

Ниже подборка хакатонов и проектов, в которые можно вписаться pro bono, чтобы помочь в борьбе с эпидемией и ее последствиями:

Хакатоны:

Pandemic Response Hackathon пытаются лучше понять эпидемию и бороться с распростраением вируса.

CodeVsCOVID19 с очень большим количеством идей, как можно помочь.

Hack quarantine с нескольктими треками: supporting quarantined, tech and health, remote working, improving awareness and behaviour.

Задача на моделирование распространения вируса из Африки с призовым фондом.

На Kaggle:

Предлагают предсказать скорость распространения вируса, есть открытый датасет.

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

ВОЗ хотят вычислить, какие именно факторы влияют на распространение вируса и смертность.  

Проекты:

COVIDарность в России помогают создавать сообщества для взаимопомощи в эпидемию. Сейчас ищут фронтенд, бэкенд разработчиков и тех, у кого есть опыт с ботами.  

Folding@home занимаются численным моделированием, чтобы найти лекарство от коронавируса и им можно помочь с распределенными вычислениями.

Другие списки:

helpwithcovid.com, который делает CEO OpenAI, собирает инициативы, там можно нафильтровать себе проект по навыкам/профессии.

Coronavirus handbook: еще больше проектов и сообществ для тех, кто может прогать.

Еще один список от 8000hours с большим гуглдоком, который постоянно обновляют.

В Twitter:

Ищите инициативы по хэштегу #COVTECH.

Если встретите еще интересные ссылки, скиньте их мне, пожалуйста, в личку или в фидбэк, а я дополню список. Лайк, репост, санитайзер. 🐠
источник
2020 July 01
Python in Depth
​​​​Представьте, приходите вы на новый проект, заглядываете в логи, а там вперемешку с успешными запросами на сервер вот такие сообщения:  

Unexpected exception:


Что-то постоянно ломается, но молча.

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

try:
   raise KeyError
except Exception as e:
   print(f"Unexpected exception: {e}")


Если выполнить этот пример, то и получится

Unexpected exception:


Дело в том, что есть четыре способа вывести сообщение пойманного исключения:

🐟 print(e)
🐟 print(str(e))
🐟 print(e.message)
🐟 print(repr(e))

Первые два варианта не очень информативны. Например, если попытаться обратиться к несуществующему ключу словаря (ошибка топ 1 в Python), то эти варианты выведут только название ключа.

my_dict = {}
try:
   b = my_dict["bad"]
except Exception as e:
   print(f"Unexpected exception: {e}")

Unexpected exception: 'bad'


Это потому, что str(e) и e выводят сообщение исключения, но не его тип.  Чаще всего этого достаточно, но исключения для того и нужны, чтобы сообщать о ситуациях, которых мы не ожидаем.

Иногда пишут print(e.message). Здесь проблемы целых две: во-первых, мы по-прежнему получаем только сообщение. А во-вторых, атрибут message определен не у всех исключений. Если не снабдить наш print условием, то мы получим только новую ошибку:

AttributeError: 'KeyError' object has no attribute 'message'


А вот магический метод repr, который и нужен для того, чтобы давать максимально точное описание, все сделает отлично. Сравним:

try:
   raise KeyError
except Exception as e:
   print(f"Unexpected exception: {repr(e)}")

Unexpected exception: KeyError()


А в примере со словарем получилось бы

Unexpected exception: KeyError('bad')


что более явно, чем все три варианта выше 🐠

#исключения
источник
2020 July 04
Python in Depth
​​​​Хочу похвастаться: на этой неделе произошли сразу две приятные вещи.

1. После моего поста про исключения мне прислали крутой комментарий. Привожу целиком.

repr(e) -- это, конечно хорошо, но ведь есть ещё лучше, а именно:

from traceback import print_exc
...
my_dict = {}
try:
   b = my_dict["bad"]
except Exception as e:
   print_exc()

что выведет:

Traceback (most recent call last):
 File "<pyshell#1>", line 2, in <module>
KeyError: 'bad'

и при этом программа продолжит свою работу. А если ошибку надо выводить не в stdout, то можно сделать так:

from traceback import print_exc
from io import StringIO
...
try:
   b = my_dict["bad"]
except Exception:
   buffer = StringIO()
   print_exc(file=buffer)
   out_var = buffer.getvalue()

2. Еще мне писали, что повторяли перед собеседованием Python по моему каналу. То есть, пользовались им ровно так, как я задумывала!

Вот ради таких моментов я и веду канал.

Кстати, если есть что-нибудь, о чем вы хотите прочитать, про Python, профессию или вообще, пишите @chakchurina, ответы на самые частые вопросы я публикую.
источник
2020 July 08
Python in Depth
Как устроены словари в Python, часть 1.

Представьте себе огромную библиотеку, в которой вы хотите найти «Пикник на обочине». Как это сделать?

Наивный способ — перебирать. Взять первую книгу, понять, что это не Стругацкие, поставить обратно, взять следующую, ... и так далее. В лучшем случае «Пикник на обочине» окажется в первой ячейке и мы справимся за один ход. В худшем придется перебрать все n книг библиотеки, за за O(n) шагов. Но можно быстрее.

Для этого определим функцию, которая получает название книги и возвращает число. Такая функция-справочник:

«Пикник на обочине» -> 1
«Декамерон» -> 2
«Уловка 22» -> 3
...

Положим «Пикник на обочине» на первую полку, «Декамерон» на вторую, и так далее. Когда нам понадобится книга, мы отправим название в эту функцию и сразу получим номер ячейки. Теперь книгу можно найти всего за два шага:

1) вычислить номер книги по названию,
2) найти ее на полке с этим номером.  

Получается сложность O(1) и это очень быстро! Положить книгу на свое место тоже можно за фиксированное число шагов, вне зависимости от размера библиотеки. Побочным эффектом такого подхода будет то, что в библиотеке не будет дубликатов книг, ведь все копии Декамерона будут попадать в ячейку с одним и тем же адресом.

Функция, которая ставит объекту в соответствие число, называется хеш-функцией. Любая хеш-функция должна удовлетворять требованию:

Если два объекта идентичны, то их хеши равны.

Идеальная хеш-функция должна удовлетворять еще одному требованию:

Если у двух объектов одинаковый хеш, то это одинаковые объекты.

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

«Облачный атлас» -> 10
«Москва-Петушки» -> 10

то это значит, что мы должны поставить две разные книги на одну полку. И когда мы приходим за книгой с номером 10, становится непонятно, какую из книг выбрать.
источник
Python in Depth
Как устроены словари в Python, часть 2.

Какое отношение эта проблема имеет к Python? Дело в том, что множества и словари в Python реализованы как хеш-таблицы, — то есть, ровно такие библиотеки с книгами на полках. Когда вы помещаете пару ключ-значение в словарь, интерпретатор вычисляет хеш ключа и кладет значение в ячейку памяти с адресом, совпадающим с результатом работы хеш-функции.

И если вдруг оказывается, что «Облачный атлас» и «Москва-Петушки» нужно положить в одну и ту же ячейку, то такую ситуацию называют коллизией.

Способов бороться с хеш-коллизиями концептуально два.

🐙 Метод цепочки. В этом методе каждая ячейка массива — это указатель на связный список пар ключ-значение, соответствующих одному и тому же хеш-значению ключа. Коллизии просто приводят к тому, что появляются цепочки длиной больше одного элемента.

Это как если положить все книги с одинаковым номером на одну полку. Тогда при поиске книги придется найти нужную полку, взять первую книгу и прочитать название. Если не та — проверить следующую, и так далее. В худшем случае все n книг попадут на одну полку и сложность получится O(n).

🐙 Открытая адресация. В этом случае в ячейки помещаются не указатели на списки, а сами пары ключ-значение. Алгоритм такой: вычисляем хеш-функцию, проверяем нужную ячейку. Если искомого элемента нет, то ищем в следующей ячейке. Следующую ячейку выбирают разными методами: это может быть просто фиксированный интервал до следующей ячейки, повторное хеширование вспомогательной хеш-функцией или другие методы.

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

В Python множествах и словарях используется метод открытой адресации.

Какие из всего этого выводы?

🐙 Ключи словаря должны быть хешируемыми.
🐙 Словари неэффективны по памяти. Если экономите память, используйте кортежи.
🐙 Поиск по ключу в итоге не O(1), но все равно очень быстрый.
🐙 Модифицировать словарь, по которому итерируешься — плохая идея. Интерпретатор может решить, что пора ресайзить хеш-таблицу и тогда старые данные переедут в новую табличку. Не стоит.

И все то же самое применимо к множествам, потому что они реализованы похожим образом.

🐙 А ещё становится понятно, что множества работают сильно быстрее списоков.

Кстати, пост не содержит совсем никаких рекомендаций, что почитать на досуге. 🐠

#словари #множества #реализация #алгоритмы #хеширование
источник
2020 July 13
Python in Depth
Дублирование объектов в множестве

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

Создадим класс Client и добавим его в множество (сразу определила метод repr, чтобы были красивые принты):

class Client:
   def __init__(self, user_name):
       self.user_name = user_name

   def __repr__(self):
       return self.user_name

fish1 = Client(user_name="catfish")

clients = set()
clients.add(fish1)

print(clients)  # {catfish}

Круто, все работает из коробки! Попробуем теперь добавить второй клиент, чтобы увидеть, что дублирования нет:

fish2 = Client(user_name="catfish")
clients.add(fish2)

print(clients)  # {catfish, catfish}

Как же так? Мы добавили в множество два совершенно одинаковых экземпляра и ждали, что останется только один, но сохранились оба.

Интерпретатор при добавлении объекта в множество следует правилу:

если a == b, то обязательно hash(a) == hash(b)

То есть, сравнивает между собой как сами объекты, так и их хеши.

Для того, чтобы сравнивать объекты, множества и словари используют магический метод eq. Для пользовательских объектов он определен по умолчанию. И по умолчанию два разных объекта не равны, даже если они абсолютно идентичны:

print(fish1 == fish2)  # False

По умолчанию все объекты в Python также хешируемы. Когда мы пытаемся добавить объект в множество, мы используем магический метод этого объекта hash, который тоже определен по умолчанию. Часто люди думают, что хеш объекта совпадает с его адресом в памяти, но это не всегда так:

🦑 в версиях Python 2.6 и ниже да, было просто hash(x) = id()
🦑
с версии 2.6 и выше: hash(x)==id(x)/16

То есть, рассчитывать на то, что hash(x) = id(), нельзя. Но можно рассчитывать на то, что Python объектах по умолчанию есть hash, который зависит от id() и на то, что хеш объекта в течение его жизни не меняется.

Так как в нашем примере объекты разные и располагаются в разных ячейках памяти, то

print(hash(fish1), hash(fish2))  # 8786876890805 8786876904409

Так что делать, чтобы объекты, которые мы хотим считать одинаковыми, схлопывались во множестве? Перегрузить методы hash и eq, чтобы в явном виде дать интерпретатору инструкцию, как сравнивать объекты и как рассчитывать хеш.  

Для этого исправим определение класса:

class Client:
   def __init__(self, user_name):
       self.user_name = user_name

   def __repr__(self):
       return self.user_name
       
   def __hash__(self):
       return hash(self.user_name)

   def __eq__(self, other):
       if self.user_name == other.user_name:
           return True
       else:
           return False

Теперь при добавлении объектов в множество все работает как ожидали:

clients = set()

fish1 = Client(user_name="catfish")
clients.add(fish1)
fish2 = Client(user_name="catfish")
clients.add(fish2)

print(clients)  # {catfish}

Помните, для того, чтобы объект класса, который вы определили сами, можно было добавить во множество или словарь, нужно:

🦑 Перегрузить методы hash и eq, причем оба, иначе не заработает
🦑 Если объекты идентичны, то и их хеши должны быть равны
🦑 В хэш-функцию должно попадать то, что однозначно идентифицирует объект
🦑 Хеш объекта не должен меняться в течение его жизни (иначе будете ловить чудеса в run time) 🐠

#словари #множества #реализация #хеширование
источник
2020 July 16
Python in Depth
Магические методы и пустые списки

Методы с двойным подчеркиванием в начале и в конце, например, __eq__, __hash__, __init__, в Python называют магическими методами. Их назвали так, потому что они добавляют магию✨ в поведение класса.

Например, метод __init__ неявно вызывается при инициализации объекта:

Foo:
 def __init__(self, a, b):
   self.a = a
   self.b = b

foo = Foo(7, 9) # вызывается __init__

print(foo.a, foo.b) # 7, 9

Но сегодня я хочу поговорить о магичесом методе __bool__. В тех классах, где этот метод определен, он сообщает интерпретатору, как оценить булево значение произвольного объекта.

Зачем нам это? Это нестареющая классика: чтобы проверить, словарь или список на непустоту, начинающие часто используют стиль как в C++:  

if len(a) != 0:
   pass

В то время как PEP8 рекомендует делать просто:

if not a:
   pass

И это работает ровно потому, что в момент if a: вызывается метод __bool__ класса список.

Для пользовательских объектов, где __bool__ не перегружен, по умолчанию возвращается True.

if foo:
   print(True)
else:
   print(False)

#  True

Но если добавить

Foo:
 def __init__(self, a, b):
   self.a = a
   self.b = b

 def __bool__(self):
     return False

То поведение изменится:

if foo:
   print(True)
else:
   print(False)

#  False

Кстати, в случае, если метода __bool__ в классе нет, то интерпретатор будет искать метод __len__ (длина). Поведение такое же: если __len__ возвращает 0, то логическое значение оценивается как False.

Что с этим делать? Помнить, что falsy (значения, которые оцениваются как False) в Python это:

🐙 пустые словари {},
🐙 списки [],
🐙 кортежи (),
🐙 множества set(),
🐙 строки "",
🐙 пустые range(0),
🐙 нули любого численного типа: 0, 0.0, 0j
🐙 константы None и собственно False

А truthy значения это:

🐙 любые пользовательские объекты по умолчанию
🐙 непустые словари, множества, строки, списки, ...
🐙 константа True

И писать проверку на непустоту красиво:

not a:
   pass

#magic #dunder #bool #len #truthy #falsy #начинающим
источник