Hello World – Telegram
Hello World
1.68K subscribers
71 photos
6 videos
3 files
161 links
Be so good that you cannot be ignored. And then, go one step beyond.
Download Telegram
timeit

Модуль timeit позволяет измерить время выполнения небольших кусочков кода:
>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))',
number=10000)
0.3412662749997253


>>> timeit.timeit('"-".join([str(n) for n in range(100)])',
number=10000)
0.2996307989997149


>>> timeit.timeit('"-".join(map(str, range(100)))',
number=10000)
0.24581470699922647


#timeit #tips
Использование dict() вместо цепочек if-elif-else

В некоторых случаях удобнее и лучше использовать dict() вместо цепочек условных операторов.
def dispatch_if(operator, x, y):
if operator == 'add':
return x + y
elif operator == 'sub':
return x - y
elif operator == 'mul':
return x * y
elif operator == 'div':
return x / y
else:
return None


Данный код, если можно так выразиться, более “pythonic”:
def dispatch_dict(operator, x, y):
return {
'add': lambda: x + y,
'sub': lambda: x - y,
'mul': lambda: x * y,
'div': lambda: x / y,
}.get(operator, lambda: None)()


Вывод:
>>> dispatch_if('mul', 2, 8)
16

>>> dispatch_dict('mul', 2, 8)
16

>>> dispatch_if('unknown', 2, 8)
None

>>> dispatch_dict('unknown', 2, 8)
None


#tips #dict
🐍 Немного новостей за прошедшую неделю

🔸Apple опубликовала финальный релиз языка программирования Swift 5 для iOS, macOS, watchOS и tvOS. Главное улучшение — стабильность ABI, что обеспечивает бинарную совместимость приложений и библиотек, скомпилированных разными версиями языка.

🔸TypeScript и Kotlin получили статус самых быстрорастущих языков программирования в рейтинге RedMonk. Учитывались такие параметры, как используемость и интерес со стороны разработчиков.

🔸В MIT создали новый алгоритм, который в 200 раз ускоряет разработку нейросетей.

🔸Microsoft открыла исходный код проекта Pyright, в рамках которого развивается система для применения статической типизации в приложениях на языке Python.

#news
​​Какой самый лучший способ понять как работает git?

Правильно, написать его. А писать его на Python вдвойне приятнее!
Автор статьи по пунктам разбирает написание собственной системы контроля версий.
Как по мне, это отличный способ изучить git и к тому же потренировать свои навыки программирования на Python.

#git
geopy

Программистам может быть сложно ориентироваться в географии. Однако, модуль geopy всё упрощает:
$ pip install geopy


Он работает путём абстрагирования API разных сервисов геокодирования. Этот модуль даёт возможность узнать полный адрес места, его долготу и широту и даже высоту.
Также в нём есть полезный класс Distance. Он высчитывает расстояние между двумя местами в удобной единице измерения.
from geopy import GoogleV3

place = "221b Baker Street, London"
location = GoogleV3().geocode(place)

print(location.address)
print(location.location)


#tips #geopy
Python HTTP server

Python позволяет поднять простой http сервер с помощью одной строчки в терминале:
# Python 3.x
$ python3 -m http.server

# Python 2.x
$ python -m SimpleHTTPServer 8000


По умолчанию он использует 8000й порт и предоставляет доступ к текущей директории терминала.
http://localhost:8000

#tips #http
Поиск палиндромов в слове

Небольшой, но действенный код для поиска всех палиндромов в слове:
def get_palindrom(text):
text = text.lower()
results = []

for i in range(len(text)):
for j in range(0, i):
chunk = text[j:i + 1]

if chunk == chunk[::-1]:
results.append(chunk)

return results


И на закуску несколько способов проверки на палиндром.

📎 Так как любое слово это итерируемый объект, можно просто проверить его циклом:
for index in range(len(word)):
if word[index] == word[-(index + 1)]:
results.append(word)


📎 Перейти к списку, и воспользоваться функцией reversed:
if list(word) == list(reversed(word)):
results.append(word)


📎 И самый удобный способ, со срезом списка:
if word == word[::-1]:
results.append(word)


#tips #palindrome
Generators & iterators (1/3)

Рано или поздно всякий Python-разработчик сталкивается с осознанием того, что он не до конца понимает смысл и различия следующих понятий:

🔸контейнер (container)
🔸итерируемый объект (iterable)
🔸итератор (iterator)
🔸генератор (generator)
🔸генераторное выражение (generator expression)
🔸списковое включение (list comprehension)

Контейнеры
Контейнер — это тип данных, предназначенный для хранения элементов и предоставляющий набор операций для работы с ними. Сами контейнеры и, как правило, их элементы хранятся в памяти. В Python существует масса разнообразных контейнеров, среди которых всем хорошо знакомые:

🔸list, deque, …
🔸set, frozensets, …
🔸dict, defaultdict, OrderedDict, Counter, …
🔸tuple, namedtuple, …
🔸str

📎Технически, объект является контейнером тогда, когда он предоставляет возможность определить наличие или отсутствие в нём конкретного элемента.
📎Обратите внимание, что несмотря на то, что большинство контейнеров предоставляют возможность извлекать из них любой элемент, само по себе наличие этой возможности не делает объект контейнером, а лишь итерируемым объектом.

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

Итерируемым объектом является любой объект, который может предоставить итератор, который, в свою очередь, и возвращает отдельные элементы. На первый взгляд это звучит немного странновато, но тем не менее очень важно понимать разницу между интерируемым объектом и итератором. Рассмотрим пример:
>>> x = [1, 2, 3]
>>> y = iter(x)
>>> z = iter(x)
>>> next(y)
1
>>> next(y)
2
>>> next(z)
1


Здесь х — это итерируемый объект, в то время как y и z два отдельных экземпляра итератора, производящего значения из итерируемого объекта x. Как мы видим, y и z сохраняют состояние между вызовами next(). В данном примере в качестве источника данных для итератора используется список, но это не является обязательным условием.

📎Часто, чтобы сократить объем кода, классы итерируемых объектов имплементируют сразу оба метода: __iter()__ и __next()__, при этом __iter()__ возвращает self. Таким образом класс одновременно является и итерируемым и итератором самого себя. Однако, лучшей практикой всё же считается в качестве итератора возвращать отдельный объект.

#tips #generators
Итерируемые объекты

Итак, когда выполняется следующий код:
x = [1, 2, 3]
for item in x:
pass


Вот что происходит на самом деле:
>>> import dis
>>> x = [1, 2, 3]
>>> dis.dis('for _ in x: pass')
1 0 SETUP_LOOP 14 (to 17)
3 LOAD_NAME 0 (x)
6 GET_ITER
>> 7 FOR_ITER 6 (to 16)
10 STORE_NAME 1 (_)
13 JUMP_ABSOLUTE 7
>> 16 POP_BLOCK
>> 17 LOAD_CONST 0 (None)
20 RETURN_VALUE


📌Если диассемблировать код, представленный выше, мы обнаружим вызов GET_ITER, который по сути является следствием вызова iter(x). Инструкция FOR_ITER является эквивалентом многократного вызова next() до тех пор, пока не будет возвращён последний элемент. Этого, правда, не видно в байт-коде из-за оптимизаций, вносимых интерпретатором.

#tips #cycles
я читаю посты на hello world
Anonymous Poll
17%
с компьютера
83%
с телефона
Generators & iterators (2/3)

Итераторы
Итак, чем же является итератор? Итератор — это вспомогательный объект, возвращающий следующий элемент всякий раз, когда выполняется вызов функции __next__() с передачей этого объекта в качестве аргумента. Таким образом, любой объект, предоставляющий метод __next__(), является итератором, возвращающим следующий элемент при вызове этого метода, при этом совершенно неважно как именно это происходит.

Итак, итератор — это некая фабрика по производству значений. Всякий раз, когда вы обращаетесь к ней с просьбой "давай следующее значение", она знает как сделать это, поскольку сохраняет своё внутреннее состояние между обращениями к ней.

Существует бесчисленное множество примеров использования итераторов. Например, все функции пакета itertools возвращают итераторы.

📌Некоторые из них генерируют бесконечные последовательности:
>>> from itertools import count
>>> counter = count(start=1)
>>> next(counter)
1
>>> next(counter)
2


📌Некоторые создают бесконечные последовательности из конечных:
>>> from itertools import cycle
>>> colors = cycle(['red', 'white', 'blue'])
>>> next(colors)
'red'
>>> next(colors)
'white'
>>> next(colors)
'blue'
>>> next(colors)
'red’


📌Или конечные последовательности из бесконечных:
>>> from itertools import islice
>>> colors = cycle(['red', 'white', 'blue'])
>>> limited = islice(colors, 0, 4)
>>> for x in limited:
... print(x)
red
white
blue
red


Генераторы
Итак, наконец-то мы добрались до самого интересного! Генераторы являются безумно интересной и полезной штукой в Python. Генератор — это особый, более изящный случай итератора.

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

Давайте внесём немного ясности:

любой генератор является итератором (но не наоборот!);
следовательно, любой генератор является "ленивой фабрикой", возвращающей значения последовательности по требованию.
Вот пример итератора последовательности чисел Фибоначчи в исполнении генератора:
>>> def fib():
... a, b = 0, 1
... while True:
... yield b
... a, b = b, a + b
...
>>> f = fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]


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

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

Теперь, когда происходит вызов функции fib
f = fib()

будет создан и возвращён экземпляр генератора. К данному моменту ещё никакого кода внутри функции не выполняется и генератор ожидает вызова.

Дальше созданный экземпляр генератора передаётся в качестве аргумента функции islice, которая также возвращает итератор, следовательно также никакого кода функции fib пока что не выполняется.

И, наконец, происходит вызов list() с передачей в качестве аргумента итератора, возвращённого функцией islice(). Чтобы list() смогла построить объект списка на основе полученного аргумента, ей необходимо получить все значения из этого аргумента. Для этого list() выполняет последовательные вызовы метода next() итератора, возвращённого вызовом islice(), который, в свою очередь, выполняет последовательные вызовы next() в экземпляре итератора f.

#tips #generators
Python's list comprehensions

vals = [expression 
for value in collection
if condition]


Это эквивалентно следующему коду:
vals = []
for value in collection:
if condition:
vals.append(expression)


Пример:
>>> even_squares = [x * x for x in range(10) if not x % 2]
>>> even_squares
[0, 4, 16, 36, 64]


#tips
Generators & iterators (3/3)

Типы генераторов
В Python существует два типа генераторов: генераторные функции и генераторные выражения. Генератором является любая функция, содержащая yield в любом месте её кода. Пример такого генератора мы только что рассмотрели. Другой разновидностью генераторов в Python являются генераторные выражения, своим видом напоминающие списковые выражения. Использование генераторных выражений бывает очень хорошим решением в ряде случаев.

Предположим, вы используете следующую конструкцию, чтобы создать список квадратов чисел:
>>> numbers = [1, 2, 3, 4, 5, 6]
>>> [x * x for x in numbers]
[1, 4, 9, 16, 25, 36]


Или, то же самое, но в виде множества:
>>> {x * x for x in numbers}
{1, 4, 36, 9, 16, 25}


Или в виде словаря:
>>> {x: x * x for x in numbers}
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36}


Или, наконец, используя генераторное выражение (обратите внимание, это НЕ кортеж!):
>>> lazy_squares = (x * x for x in numbers)
>>> lazy_squares
at 0x10d1f5510>


Заключение
Генераторы являются потрясающей языковой конструкцией. Они позволяют писать код, используя меньше промежуточных переменных, снизить потребление памяти и ресурсов процессора, а также уменьшить объём самого кода.

📌Если вы всё ещё не используете генераторы и хотели бы начать делать это, попробуйте начать с того, что обратите внимание на все участки вашего кода, имеющие вид:
def something():
result = []
for ... in ...:
result.append(x)
return result


И замените их генераторами:
def iter_something():
for ... in ...:
yield x


#tips #generators
uuid

Стандартный модуль uuid — быстрый и простой способ сгенерировать UUID (universally unique identifier, глобально уникальный идентификатор).
import uuid

user_id = uuid.uuid4()
print(user_id)


Так мы создаём случайное 128-битное число, которое почти наверняка будет уникальным.

Существует более 2¹²² возможных UUID. Это более 5 ундециллионов или 5,000,000,000,000,000,000,000,000,000,000,000,000.

Вероятность нахождения дубликатов в заданном наборе крайне мала. Даже при наличии триллиона UUID вероятность того, что среди них есть дубликат, гораздо меньше, чем один к миллиарду.

Вполне недурно для двух строк кода.

#uuid
Global Variables

В Python любая переменная, задекларированная вне функции, по-умолчанию определена как глобальная.
c = 1

def add():
print(c)

add()


#выведет
1


Но если попытаться изменить эту переменную внутри функции, мы наткнемся на ошибку:
c = 1

def add():
c = c + 1
print(c)

add()


#выведет
UnboundLocalError: local variable ‘c’ referenced before assignment


Поэтому существует специальное слово global:
c = 1

def add()
global c
c = c + 1
print(‘Inside:’, c)

add()
print(‘Outside:’, c)


#выведет
Inside: 2
Outside: 2


📌Любая переменная внутри функции по-умолчанию определена как локальная
📌Любая переменная снаружи функции по-умолчанию определена как глобальная
📌Мы используем ключевое слово global для того, чтобы модифицировать глобальную переменную внутри функции
📌Использование global снаружи функции не даст никакого эффекта

#tips #global
type annotations (1/2)

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

Сохраняя идею динамической утиной типизации в современных версиях Python (3.6+) поддерживает аннотации типов переменных, полей класса, аргументов и возвращаемых значений функций.

Пример:
def my_add(a: int, b: int) -> int:
return a + b


def greet(name: str) -> str:
return ‘Hello ‘ + name


Стрелочка после аргументов функции показывает возвращаемый тип.

#typing
Python’s slices

Срезы в листах Python могут быть использованы и без индексов
Вот несколько забавных и полезных вещей:

# Очистка всех элементов листа:
>>> lst = [1, 2, 3, 4, 5]
>>> del lst[:]
>>> lst
[]


# Замена всех элементов листа
# без создания нового объекта:
>>> a = lst
>>> lst[:] = [7, 8, 9]
>>> lst
[7, 8, 9]
>>> a
[7, 8, 9]
>>> a is lst
True


# Создание копии листа:
>>> b = lst[:]
>>> b
[7, 8, 9]
>>> b is lst
False


#tips #list
Argument unpacking

Небольшой пример того, почему пайтон так хорош:
def myfunc(x, y, z):
print(x, y, z)


tuple_vec = (1, 0, 1)
dict_vec = {'x': 1, 'y': 0, 'z': 1}


Теперь мы можем распаковать кортеж (да и любой другой итерируемый объект) с помощью оператора *
>>> myfunc(*tuple_vec)
1, 0, 1


Распаковка словарей происходит с помощью **
>>> myfunc(**dict_vec)
1, 0, 1


#tips
Collections

Находим наиболее часто встречающийся элемент в контейнере:

>>> import collections
>>> c = collections.Counter('helloworld’)


>>> c
Counter({'l': 3, 'o': 2, 'e': 1, 'd': 1, 'h': 1, 'r': 1, 'w': 1})


>>> c.most_common(3)
[('l', 3), ('o', 2), ('e', 1)]


#tips #collections
Permutations

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

>>> import itertools
>>> for p in itertools.permutations('ABCD'):
... print(p)

('A', 'B', 'C', 'D')
('A', 'B', 'D', 'C')
('A', 'C', 'B', 'D')
('A', 'C', 'D', 'B')
('A', 'D', 'B', 'C')
('A', 'D', 'C', 'B')
('B', 'A', 'C', 'D')
('B', 'A', 'D', 'C')
('B', 'C', 'A', 'D')
('B', 'C', 'D', 'A')
('B', 'D', 'A', 'C')
('B', 'D', 'C', 'A')
('C', 'A', 'B', 'D')
('C', 'A', 'D', 'B')
('C', 'B', 'A', 'D')
('C', 'B', 'D', 'A')
('C', 'D', 'A', 'B')
('C', 'D', 'B', 'A')
('D', 'A', 'B', 'C')
('D', 'A', 'C', 'B')
('D', 'B', 'A', 'C')
('D', 'B', 'C', 'A')
('D', 'C', 'A', 'B')
('D', 'C', 'B', ‘A’)


4! = 24

#tips #permutations