CoolPython
4.64K subscribers
20 photos
44 links
Канал об основах Python и хороших практиках разработки. Создаём системность в обрывочных знаниях.

Тем, кто хочет понимать, что пишет!
Download Telegram
Как писать резюме

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

Сделайте так, чтобы каждая строчка резюме работала на две эти цели и уберите из текста все, что вас к ним не приближает. Например, мое первое образование экономическое и если я иду на backend разработку, у меня нет причин о нем говорить. Но для финтех компаний у меня есть вторая версия:) То же с водительскими правами: в hh есть это поле, но его не обязательно заполнять, если не идете разрабатывать беспилотный автомобиль. Работа грузчиком или кассиром тоже не имеет отношения к программированию. Не мешайте читателю понять, стоит ли с вами поговорить лично.

Каждая запись в моем резюме отвечает на вопросы, что я сделала на прошлой работе (именно <<сделала>>, а не <<делала>>) и чему при этом научилась. Работали на проекте -- опишите этот опыт. В своей первой компании я задержалась на 2,5 года, но там можно было менять проекты или работать над несколькими в параллель. Эти опыты разные и о каждом из них я рассказываю отдельно.

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

Если не уверены, что убрать, а что оставить, исследуйте! Закиньте в десяток релевантных мест одну версию документа, а в остальные другую. Вряд ли рекрутеры будут очень счастливы, но отклики покажут, какая версия работает в вашем случае. Кстати, я записываю в табличку, куда я уже отсылала резюме, а куда нет, чтобы не забывать, с кем на какой стадии находятся трудовые отношения.

Не бойтесь белых пятен в резюме. Все начинают в разное время, пробуют себя в новом, заводят детей, поэтому перерывы в карьере -- частая история. Они никак не повлияют на то, как вы будете вести себя на новом месте. У меня за все время так и не спросили, где потерялись четыре года эконома.

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

В конце резюме люди часто пишут о своих коммуникационных навыках и умении работать в команде. Это штамп. Если хотите рассказать о себе, лучше напишите, что вы гордитесь своим ребенком, ходите в горы или играете на гитаре. Это сделает резюме более человечным, чем безликое <<умение работать в команде>>. Или дайте в этом поле ссылку на свой гитхаб, публичные выступления или статьи, если они есть. Но если вы все же решили эти строчки оставить, подготовьтесь к вопросам типа <<расскажите о ситуации, в которой вы проявили коммуникационные навыки>>.

И еще об оформлении: в нашей культуре люди читают сверху вниз и слева направо. Можно организовать резюме в две колонки, можно в одну. Запрограммируйте опыт человека, который будет идти глазами по тексту. Что он увидит в первую очередь? Что потом? К каким выводам он скорее всего придет? Если не уверены, как верстать лучше, верстайте проще.

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

#резюме #ищем_работу #soft_skillz #начинающим
👍1🔥1
Чем == отличается от is?

== проверяет, одинаковые ли значения у переменных.
is проверяет, указывают ли переменные на один и тот же объект.

Рассмотрим на примере. Создадим словарь и скопируем ссылку на него:

a = [1, 2, 3] 
b = a
b is a # True
b == a # True

Теперь создадим новый словарь с помощью копирования:

b = a[:]
b is a # False

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

>>> b == a    # True    

То есть, a is b по сути то же, что id(a) == id(b). Поэтому если a is b вернет True, это будет значить, что и содержимое у них одинаковое. Просто потому что это один и тот же объект.

Кстати, чтобы проверить, есть ли вообще у переменной значение, обычно пользуются оператором is, а не ==:

a = 42
a is None # False

Дело в том, что класс None реализован как синглтон. Это значит, что экземпляр этого класса существует в интерпретаторе в единственном числе и все неопределенные значения указывают на него.

a = None 
b = None
a is b # True

Для некоторых классов оператор == тоже сработает, но так лучше не делать, потому что в пользовательских классах этот оператор можно переопределить и выстрелить себе в ногу получить интересные результаты. Поэтому в PEP8 и рекомендуют проверять на значение None именно с помощью is. 🐍

#основы #identity_vs_equality #перед_собесом #типы_данных
👍4🔥1
Пять способов создать словарь

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

Я знаю пять (!) способов создать словарь в Python. С помощью литералов словаря:

fish = {
"move": "water",
"eat": "insects",
"say": None
}

Используя конструктор явно:

snail = dict(
eat=”leaves”,
move=”ground”,
say=None
)

Или инициализируя его кортежами:

cow = dict([
(“move”, “ground”),
(“eat”, “grass”),
(“say, “moo”)
])

Четвертый, с помощью генераторных выражений (версия интерпретатора 3.5 и выше):

>>> animals = [“snail”, “fish”, “cow”]
>>> {animal: it for it, animal in enumerate(animals)}
{'snail': 0, 'fish': 1, 'cow': 2}

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

>>> {v: k for k, v in animals.items()}
{1: 'snail', 2: 'fish', 3: 'cow’}

Только будьте осторожны, потому что ключи в словаре должны быть уникальны. Если какие-то значения исходного словаря совпадали, то, когда они станут ключами нового, дубликаты исчезнут.

И последний, который я использую, когда мне надо взять ключи из одного контейнера, а значения из другого:

>>> animals = ["frog", "snail", "bird"]
>>> numbers = [1, 2, 3]
>>> dict(zip(animals, numbers))
{'snail': 2, 'frog': 1, 'bird': 3}

Почему так много? Потому что каждый удобен под свой случай.🐍

#типы_данных #словари #основы #перед_собесом #comprehensions #загадка
🔥3
Грамотно ставим запятые

Допустим, у нас в коде есть список:

>>> capitals = ['Beijing', 'New Delhi']

Добавим в него ещё город и сделаем diff двух файлов перед коммитом.

>>> capitals = ['Beijing', 'New Delhi', 'Mexico City']

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

>>> capitals = [
'Beijing',
'New Delhi'
'Mexico City'
]

Так на diff'е будет видно, какой именно элемент появился, и делать ревью тоже будет проще. Кстати, вы заметили, что я забыла запятую? Это частая ошибка, и довольно неприятная, потому что при этом не только не будет брошено исключение, но и объединятся две последние строки:

>>> capitals
['Beijing', 'New DelhiMexico City']

Упс😕. Строки, не разделенные запятой, в Python конкатенируются. Это удобно для того, чтобы размещать в коде длинные тексты без переноса строки:

>>> why_so = ('Это очень очень '
'очень длинная строка или '
'документ на вашем сайте.')
>>> why_so
'Это очень очень очень длинная строка или документ на вашем сайте.'

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

>>> capitals = [
'Beijing',
'New Delhi',
'Mexico City',
]

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

Плюсы запятой в конце коллекции:
🤖один и тот же формат всех строк
🤓меньше ошибок по невнимательности
🐙удобно ставить под контроль версий

#контейнеры #синтаксис #основы
👍4
Кеширование небольших чисел в CPython

Поговорим о небольших числах в CPython. Возьмем, например, два числа, 10 и 300:
 
>>> a = 10
>>> b = 300
>>> a is 10
True
>>> b is 300
False

Что?! Почему a указыввет на свое значение, а b y нет?

Все дело в том, что интерпретатор хранит числа в диапазоне от - 5 до 256 в специальном массиве. Поэтому когда мы пишем x = 10, то вообще-то получаем ссылку на объект, который уже существует в памяти:
 
>>> c = 1
>>> id(1)
94492411548416
>>> id(c)
94492411548416

Причем это касается любых значений, которые приводятся к целым числам этого диапазона. Можно завести число хоть в двоичном виде, хоть в hex, кеширование все равно будет работать:
 
>>> id(19)
94579033949504
>>> id(0b10011)
94579033949504
>>> id(0o23)
94579033949504
>>> id(0x13)
94579033949504

Числа вне диапазона - 5, 256 интерпретатор тоже будет пытаться оптимизировать, но только если они находятся в одной строке (или одном файле). В командной строке интерпретатора так не работает:
 
>>> d = 400
>>> d is 400
False

а так будет:
 
>>> e = 500; f = 500
>>> e is f
True

В общем и целом то, что нужно запомнить -- это что небольшие числа в интерпретаторе хранятся не так, как большие и что с ними следует пользоваться оператором ==, а не is.

И это снова не фича, а деталь реализации для экономии ресурсов. А пишу я об этом, потому что на задачу с кешем наткнулась на собеседовании, и собеседующий удивился, что я в курсе. Сказал, что узнал об этой особенности, когда начал сам проводить собеседования. Теперь это знаете и вы 🐍

#кеш #детали_реализации #low_level #identity_vs_equality
🔥2👍1
Pythonic циклы

Поговорим о циклах в Python. Вот пример цикла, который мог бы написать человек с C или Java бэкграундом:

items = [‘a’, ‘b’, ‘c’]
i = 0
while i < len(items):
print(items[i])
i += 1

Что с ним не так?
🤖здесь мы вручную отслеживаем индекс, а значит, создаем себе возможность сделать ошибку на единицу. Легко заехать в элемент items[3], заменив < на <=;
🤖 если тело цикла длинное, то частая ошибка это сделать так, что условие выхода никогда не выполнится, и войти в бесконечный цикл;
🤖 в Python можно выразительнее.

А именно:

for item in items:
print(item)

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

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

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

for i, item in enumerate(items):
print(i)

В общем, если вам в Python цикле нужно делать операции не с индексами, а с самими объектами, то средства языка позволяют писать чистый и менее подверженный ошибкам код. Если индексы все же нужны, выбирайте enumerate(): пользоваться индексами напрямую — это не pythonic.

Закончу цитатой.

«В программировании есть только две сложные вещи: инвалидация кеша, выбор имени переменной, и ошибки на единицу»
(Джефф Этвуд, создатель StackOverflow).

#циклы #кодстайл #питониста #pythonic
👍21🤔1
Странная переменная в Python.

Когда человек впервые видит в программе что-то вроде

_, v in my_dict.items():
pass

он обычно теряется. Что это за переменная _?

В командной строке интерпретатора нижнее подчеркивание хранит значение последнего выражения. Пользоваться ей можно вот так:

>>> a + b
20
>>> _
20
>>> _ * 2
40

В интерпретаторе это позволяет вообще не писать названия переменных и работать быстрее:

>>> list()
[]
>>> _.append(‘чепуха’)
>>> _.append(‘небылица’)
>>> _.append(‘выдумка’)
>>> _
[‘чепуха’, ‘небылица’, ‘выдумка’]

При этом _ -- абсолютно валидное имя. Настолько, что в скриптах его принято использовать для dummy-переменных, значениями которых пользоваться не собираются. Например:

for _ in range(4):
print(“Шутка, повторенная дважды, становится в 4 раза смешнее”)

Или вот так:

_, c = ['чепуха', 'небылица', 'выдумка']
print(‘{} {}’.format(a, c))
'чепуха, выдумка'

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

for _, v in my_dict.items():
pass

можно было с тем же успехом использовать values().

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

#underscores #словари #соглашения #основы
🔥3
Чеклист для адаптации на новом месте.

Итак, вы прошли n кругов ада собеседований и вышли на новую работу. У меня в это время обычно стресс. Делюсь своим списком того, что я на новом проекте делаю в первую очередь. Это помогает мне справиться с переживаниями, почувствовать себя нужной и структурировать происходящее.

🐍 Завести keepass -- мне очень лень записывать пароли в хранилку. Мне кажется, что я все запомню, а потом сами знаете, чем все заканчивается.
🐍 Получить доступы в репозиторий, тикетницу, доку, мониторинг, ... И сразу же добавить все хозяйство в keepass и в закладки в браузере.
🐍 Познакомиться с сеньором/сеньоритой проекта.
🐍 Попользоваться корпоративным спортом. Иногда люди не знают, где находится спортзал, какое в нем расписание, подолгу не могут принести какую-нибудь справку, в итоге по полгода не доходят туда.
🐍 То же с ДМС, если он есть.
🐍 Пообедать с командой.
🐍 Развернуть проект локально.
🐍 Спросить, прочитать в доке или посмотреть видео про бизнес-часть проекта.
🐍 Познакомиться с парой людей не из проекта.
🐍 Посмотреть все этажи офиса.
🐍 Проследить в коде проекта какой-нибудь сценарий работы, например, <<пользователь купил билет на нашем сайте>>.
🐍 Решить первую задачу в трекере.
🐍 Задокументировать что-нибудь, не покрытое докой. Это и легкая ачивка, и сэкономит время следующим поколениям.

Обратите внимание, что
1) такой план включает в себя пункты не только про проект, но и про погружение в культуру, команду, процессы,
2) пункты не нумерованы (можно делать в любом порядке),
3) пункты детерминированы (понятно как выполнить) и небольшие (0,5 -- 4 часа).

Дополните их своими или уберите лишние.

#онбординг #new_job #soft_skillz #после_собеса #успешность
Одинарное подчеркивание в конце переменной

Недавно говорила об одинарном символе подчеркивания в Python. Теперь поговорим о подчеркивании в конце переменной:
 
my_argument_ = 10

Нет никаких причин так делать, кроме одной: имя, которое вы хотите дать переменной, конфликтурет с именем встроенной функции или ключевого слова в Python. В PEP8, документе, который закрепляет соглашения по кодстайлу в Python, написано следующее:

single_trailing_underscore_: используется по соглашению для избежания конфликтов с ключевыми словами языка, например:
 
Tkinter.Toplevel(master, class_='ClassName')

Поэтому в этом примере лучше просто написать my_argument, без подчеркивания. А вот если вам понадобится, например, что-то из id, class, del, ..., то можно добавить _. Правда, даже в этом случае я сама обычно выбираю более объясняющее имя, например, person_id, class_name, delimeter, чтобы не путать коллег и сделать код более читаемым.

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

#underscores #нейминг #соглашения #основы #кодстайл
🔥2
Есть вот такой словарь:
 
sports ={
'Nikolai':{
'weight':92,
'height':187,
'age':32
},
'Natasha':{
'weight':65,
'height':170,
'age':24
},
'Boris':{
'weight':87,
'height':180,
'age':28
}
}


Как получить имя самого старшего спортсмена?

Решение через полчаса.
Возможное решение:
 
max(sports, key=lambda k: sports[k]['age'])
🔥2
Спасибо за отзывы🌸 Видимо, здесь можно немного рассказать про lambda-функции. На самом деле это несложно, разберемся. Попозже напишу.
List astonishment

Допустим, мы хотим написать функцию, которая будет принимать на вход список. Естественным ходом выглядит написать что-то вроде:
 
>>> def foo(data=[]):
... data.append(5)
... return data

Ждем, что при каждом новом вызовы функции список снова будет пустым. На самом же деле:
 
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]

Вот это поворот! Причина такого поведения в том, что в Python значения дефолтных аргументов вычисляются только один раз -- при объявлении функции. То есть, после того, как инструкция def выполнена, список data уже создан. При этом пока функция существует, пересоздаваться список больше не будет. Это объясняет и вот такое поведение:
 
>>> foo(data=[1, 2, 3])
[1, 2, 3, 5]
>>> foo()
[5, 5, 5]

Видно, что, когда мы передали другой список в foo(), к нему добавилось значение 5. При этом аргумент по умолчанию не изменился.

Как с этим жить? Либо вообще не использовать изменяемые объекты в качестве дефолтных значний, либо инициализировать их как None:
 
>>> def bar(data=None):
... if data is None:
... data = []
... data.append(5)
... return data

Такое определение создает новый список в локальном неймспейсе. Теперь все чисто😏
  
>>> bar()
[5]
>>> bar()
[5]
>>> bar(data=[1, 2, 3])
[1, 2, 3, 5]

На самом деле это поведение можно обратить в свою пользу. Пусть, например, в программе есть функция, которая долго вычисляет результат. Можно кешировать параметры и результат вычисления, и, если функцию вызывают еще раз с теми же парметрами, просто возвращать заранее посчитанный ответ.
 
def baz(arg1, arg2, _cache={}):
# Если есть в кеше, выходим
if (arg1, arg2) in _cache:
return _cache[(arg1, arg2)]

# Вычисляем результат
result = ...
# Сохраняем в кеш
_cache[(arg1, arg2)] = result
return result

Сохранение результатов для предотвращения повторных вычислений называется мемоизацией.

В Python дефолтные аргументы инициализируются только при объявлении функции. Это значит, что изменения, которые делаются в мутабельных аргументах при вызове функции, сохраняются. Поэтому лучше не использовать списки, словари и экземпляры самописных классов как значения по умолчанию. Либо использовать специально, как кеш, в котором можно хранить полезную информацию🐍

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

#функции #мемоизация #кеширование #мутабельность #иммутабельность
👍1
for ... else?

Рассмотрим пример использования ключевого слова else в цикле while:
 
>>> i = 1
>>> while i < 4:
... print(i)
... i += 1
... else:
... print('Вышли из цикла!')

Если выполнить этот цикл, то мы получим:
 
1
2
3
Вышли из цикла!

Здесь блок else выполняется только тогда, когда условие выхода из цикла перестает быть верным. Особенность здесь в том, что если вывалиться из цикла через break или через исключение, то блок else выполняться не будет.

Это работает не только с циклами while, но и с циклами for:
 
>>> for value in values:
... if value == 4:
... print('Нашли!:)')
... break
... else:
... print('Не нашли:(')

И так же можно с try-except блоками. На самом деле исключения в Python это не try-except-finally, а try-except-else-finally, ниже пример:
 
>>> num = 0
>>> try:
... result = 1 / num
... except ZeroDivisionError:
... print('Деление на ноль!')
... else:
... print('Конечное число.')
... finally:
... print('Вычисления закончены.')
...
Деление на ноль!
Вычисления закончены.

А если задать num = float('Inf'), то в результате деления мы получим 0 и вывод будет:
 
Конечное число.
Вычисления закончены.

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

В общем, есть конструкции while-else и for-else, в которых else отрабатывает в случае <<штатного>> завершения цикла. Еще else можно использовать в try-except блоке, чтобы выполнить какой-то код только в случае успешного выполнения блока try. Во всех случаях использование else позволяет более гибко управлять потоком выполнения 🐍

#циклы #исключения #else #условия #основы
👍2
Timsort

Для сортировки в CPython используется алгоритм Timsort, который его создатель Тим Петерс застенчиво назвал в честь себя😏. Timsort — это комбинация сортировки вставками и сортировки слиянием, заточенная под работу с реальными данными. Дело в том, что на практике массивы, которые нужно сортировать, часто бывают частично упорядочены и Timsort пользуется этим предположением для ускорения работы.

Логика работы Timsort на самом деле довольно прозрачная:
🐠 Timsort делит входной массив на подмассивы;
🐠 Сортирует подмассивы вставками;
🐠 Объединяет их попарно сортировкой слиянием;
🐠 И возвращает отсортированный массив.

Но дьявол кроется в деталях.

Делим на подмассивы

Части, на которые алгоритм делит входной массив, в описании алгоритма автор называет run'ами. Так на какое количество run'ов следует разделить входной массив? Здесь два соображения. Во-первых, части должны быть достаточно короткими, чтобы их было выгодно сортировать вставками. А еще мы хотим, чтобы пары последовательностей, которые позже поступят в сортировку слиянием, были примерно одинаковой длины. Дальше происходит магия, которая учитывает эти соображения и выбирает длину подпоследовательности minrun между 32 и 64 элементами. При этом если входной массив короче 64 элементов, то на части он не делится.

Сортируем run'ы

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

Сливаем подмассивы

По мере того, как упорядоченные run'ы формируются, сортировкой слияния они объединяются попарно во всё более крупные отсортированные куски. При этом, если алгоритм при слиянии последовательностей встречает два участка вида

a = [1, 2, 3, 4]
b = [5, 6, 7, 8]

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

Вывод

Объем алгоритма в исходнике примерно 2к строк и, как можно догадаться по описанию, код довольно запутанный. Зато за счет использования упорядоченности в реальных данных сложность Timsort в лучшем случае линейная. В худшем случае, если входные данные случайные, timsort справится за O(n log n), а это неплохо для сортировки. Кстати, несмотря на то, что Timsort изначально был придуман в Python-сообществе, сейчас он используется еще в Java, Swift и Android platform.

Вот что нужно запомнить о Timsort:
🐠 Timsort это гибридный алгоритм сортировки, сочетающий модифицированные варианты сортировки вставками и сортировки слиянием;
🐠 Timsort пользуется тем, что реальные данные часто бывают частично упорядочены;
🐠 В лучшем случае сложность O(n), в худшем O(n log n)).

Визуализацию работы алгоритма можно посмотреть здесь. Видно, как сначала строятся run'ы и как они сливаются. Обратите внимание, что в обоих прогонах всегда объединяются примерно равные по размеру части 🐍
Хочу поделиться новостью. Меня внезапно пригласили выступить на Science Slam в Питере через неделю. Science Slam -- это битва учёных в формате стенд-ап. У каждого спикера по 10 минут на рассказ о своей разработке и ещё по 5 на вопросы. Победителя определяют, замеряя с помощью шумомера, кого аудитория поддерживает больше.

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

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

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 #списки #селяви #коллекции
👍2
Умножение контейнеров

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

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

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

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

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

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

Потому что теперь каждый из двух словарей доступен по одной и той же ссылке:

>>> d[0]['key'] = ''
>>> d
[{'key': ''}, {'key': ''}]

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

#типы_данных #funfact #контейнеры #мутабельность #иммутабельность
👍1
Как зарешивать, если нервничаешь.

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

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

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

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

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

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

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

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

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

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

А несколько вариантов, как поменять две переменные местами, я нашла тут. Если эта задача вам встретится, будете во всеоружии. Если на ум приходит ещё решение, отправьте его мне в обратную связь, а я сделаю отдельный пост с самым классным. 🐠
2😁1