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
Memoization part 3.

И напоследок, в стандартной библиотеке functools уже отлично реализован подобный декоратор, который называется lru_cache.

LRU расшифровывается как Least Recently Used.

from functools import lru_cache

@clock
@lru_cache()
def fib(n):
if n < 2:
return n
return fib(n-2) + fib(n-1)

print('fib(20) =', fib(20))


lru_cache имеет два необязательных аргумента:

📌 maxsize — это количество хранимых результатов.
📌 typed — при равном true, например, значения 1 и 1.0 будут считаться разными (поскольку это разные типы).

📎 Мемоизация довольно простая и эффективная практика. А благодаря functools.lru_cache, ей удобно пользоваться в Python.

#lru #memoization
Currying.

Теперь о каррировании. Каррирование (от англ. currying, иногда — карринг) — преобразование функции от многих аргументов в набор функций, каждая из которых является функцией от одного аргумента

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

В самом простейшем случае можно реализовать каррирование с помощью лямбда-функций.
f = lambda y: lambda x: x + y
print(f(2)(2))

Out [0]: 4


Создадим простую функцию greet, которая принимает в виде аргументов приветствие и имя.
def greet(greeting, name):
print(greeting + ', ' + name)

greet('Hello', 'Nameless')


Небольшое улучшение позволит нам создать новую функцию для любого приветствия и передать этой функции имя.
def greet_curried(greeting):
def greet(name):
print(greeting + ', ' + name)
return greet

greet_hello = greet_curried('Hello')

greet_hello('German')
greet_hello('Ivan')

# или напрямую greet_curried
greet_curried('Hi')('Roma')


Сделать это можно с любым числом аргументов.
def greet_deeply_curried(greeting):
def w_separator(separator):
def w_emphasis(emphasis):
def w_name(name):
print(greeting + separator + name + emphasis)
return w_name
return w_emphasis
return w_separator

greet = greet_deeply_curried("Hello")("...")(".")
greet('German')
greet('Ivan')


#currying
Частичное применение (partial application).

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

📎Другими словами, это функция, которая принимает функцию с несколькими параметрами и возвращает функцию с меньшим количеством параметров.

📎Частичное применение преобразует функцию от n аргументов в (n-m), а карринг, в свою очередь, создает n функций с одним аргументом.

Такая возможность есть у Python в стандартной библиотеке functools, функция называется partial.
from functools import partial


def greet(greeting, separator, emphasis, name):
print(greeting + separator + name + emphasis)

newfunc = partial(greet, greeting='Hello', separator=',', emphasis='.')
newfunc(name='German')
newfunc(name='Ivan')

newfunc2 = partial(greet, greeting='Hello', emphasis='.')
newfunc2(name='German', separator='...')
newfunc2(name='Ivan', separator='..')


#partial
Первый шаг к тому, чтобы заниматься анализом данных - изучить библиотеку Pandas для Python. Вот небольшая статья, которая поможет начать изучение. В ней рассказывается о различных способах создания датафреймов в Pandas.

Автор статьи также ведет канал @datalytx, в котором регулярно размещаются полезные ссылки по анализу данных и автоматизации обработки данных с помощью Python.

http://bit.ly/2KutV0B
Чай из itertools

Если вы читали предыдущие посты про итераторы, то примерно представляете как они работают (если нет, ищите по тегу #generators). Итератор обычно выдает значения по одному (с помощью метода __next__, например). Это означает, что получать значения из итератора может только один потребитель. Однако, это можно исправить.

📌tee принимает два аргумента: исходный итератор и количество новых итераторов, на которые разделится исходный. А возвращает он кортеж из новых итераторов.
from itertools import tee

def get_iter():
for i in range(5):
yield i

one, two, three = tee(get_iter(), 3)

print(f'next is {next(one)}')
print(f'next is {next(two)}')
for item in three:
print(f'next is {item}')


Вывод:
next is 0
next is 0
next is 0
next is 1
next is 2
next is 3
next is 4


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

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

#tee #iterators
Немного новостей 🗒

📌Сейчас мы плотно заняты разработкой обучающего курса о создании ботов в телеграме. Что там вообще будет? А будет там много информации о самой библиотеке pyTelegramBotAPI, об обработчиках, о клавиатурах, о базах данных конечно и о веб хуках. Лично от меня — базы данных, платежки, встраивание платежек в бота.

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

📌Кроме того, потихоньку осваиваю data science дальше, и изучаю нейросети. Нашел для вас отличную статью (не реклама, статья правда отличная) по машинному обучению. В планах сейчас освоение нейронок на хорошем уровне + создание своего приложения.

#news
Замыкания с поздним связыванием.

Один из распространенных источников путаницы в Python — способ связывания переменных в замыканиях (closures).

Например, вы пишите:
def create_multipliers():
return [lambda x: i * x for i in range(5)]


for multiplier in create_multipliers():
print(multiplier(2), end=" ... ")
print()


Что вы ожидаете:
Out [0]: 0 … 2 … 4 … 6 … 8 …


Что происходит на самом деле:
Out [0]: 8 … 8 … 8 … 8 … 8 …


Создаются пять функций, и все они умножают x на 4. Почему?

📌В языке Python замыкания имеют позднее связывание
📌Значения переменных, использованных в замыканиях, определяются в момент вызова внутренней функции

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

#closures
Как же тогда написать правильно? А вот об этом уже в следующий раз.

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

Мы в теме ботов уже давно, если кто-то помнит наш бот по ставкам на футбольные матчи (сейчас бот в стадии обновления, парсеры переписаны с нуля и без использования splinter, который дико тормозил и нагружал систему).

Курс по ботам включает в себя работу с базами данных, с веб хуками и платежной системой QIWI. Он не требует каких-то особенных знаний Python и рассчитан преимущественно на новичков. Для записи пишите в бота @bots_course_bot

P.S. На следующей неделе запланировано поднятие цен, так что поспешите.
For masochists only.

А как вы относитесь к математике? Знакомы ли с векторным-тензорным анализом? Удовлетворяет ли вас институтский/школьный курс, или ничего вообще непонятно? Хотелось бы вам видеть больше математики на канале, даже не применительно только к языку Python?

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

Недавно мы выяснили как работает позднее связывание в замыканиях в Python. Теперь попробуем устранить проблему, которая возникала при создании замыкания.

Наиболее общее решение, возможно, станет костылем. Из-за поведения Python при определении аргументов по умолчанию для функций, вы можете создать замыкание, которое немедленно связывается со своими аргументами про помощи аргумента по умолчанию:
def create_multipliers():
return [lambda x, i=i : i * x for i in range(5)]


Либо воспользоваться functools.partial():
from functools import partial
from operator import mul

def create_multipliers():
return [partial(mul, i) for i in range(5)]


В таком случае,
Out [0]: 0 … 2 … 4 … 6 … 8 …

#closures
5 способов форматирования строк

1.
Конкатенация.
Грубый способ форматирования, в котором мы просто склеиваем несколько строк с помощью операции сложения.

name = "Дмитрий"
age = 25
print("Меня зовут " + name + ". Мне " + str(age) + " лет.")
# Output: Меня зовут Дмитрий. Мне 25 лет.


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

name = "Дмитрий"
age = 25
print("Меня зовут %s. Мне %d лет." % (name, age))
# Output: Меня зовут Дмитрий. Мне 25 лет.

print("Меня зовут %(name)s. Мне %(age)d лет." % {"name": name, "age": age})
# Output: Меня зовут Дмитрий. Мне 25 лет.


3. Template-строки.
Этот способ появился в Python 2.4, как замена %-форматированию (PEP 292), но популярным так и не стал. Поддерживает передачу значений по имени и использует $-синтаксис как в PHP.

from string import Template

name = "Дмитрий"
age = 25
s = Template('Меня зовут $name. Мне $age лет.')
print(s.substitute(name=name, age=age))
# Output: Меня зовут Дмитрий. Мне 25 лет.


4. Форматирование с помощью метода format().
Этот способ появился в Python 3 в качестве замены %-форматированию. Он также поддерживает передачу значений по позиции и по имени.

name = "Дмитрий"
age = 25
print("Меня зовут {}. Мне {} лет.".format(name, age)
# Output: Меня зовут Дмитрий. Мне 25 лет.

print("Меня зовут {name} Мне {age} лет.".format(age=age, name=name)
# Output: Меня зовут Дмитрий. Мне 25 лет.


5. f-строки.
Форматирование, которое появилось в Python 3.6 (PEP 498). Этот способ похож на форматирование с помощью метода format(), но гибче, читабельней и быстрей.

name = "Дмитрий"
age = 25
print(f"Меня зовут {name} Мне {age} лет.")
# Output: Меня зовут Дмитрий. Мне 25 лет.


#string
Погружение в f-строки

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

name = "Олег"
age = 20
print(f"Меня зовут {name} Мне {age} лет.")
# Output: Меня зовут Олег. Мне 20 лет.


📌f-строки также поддерживают расширенное форматирование чисел:

from math import pi
print(f"Значение числа pi: {pi:.2f}")
# Output: Значение числа pi: 3.14


📌С помощью f-строк можно форматировать дату без вызова метода strftime():

from datetime import datetime as dt

now = dt.now()
print(f"Текущее время {now:%d.%m.%Y %H:%M}")
# Output: Текущее время 24.02.2017 15:51


📌Они поддерживают базовые арифметические операции. Да, прямо в строках:

x = 10
y = 5
print(f"{x} x {y} / 2 = {x * y / 2}")
# Output: 10 x 5 / 2 = 25.0


📌Позволяют обращаться к значениям списков по индексу:

planets = ["Меркурий", "Венера", "Земля", "Марс"]
print(f"Мы живем на планете {planets[2]}")
# Output: Мы живем на планете Земля


📌А также к элементам словаря по ключу:

planet = {"name": "Земля", "radius": 6378000}
print(f"Планета {planet['name']}. Радиус {planet['radius']/1000} км.")
# Output: Планета Земля. Радиус 6378.0 км.


📌Причем вы можете использовать как строковые, так и числовые ключи. Точно также как в обычном Python коде:

digits = {0: 'ноль', 'one': 'один'}
print(f"0 - {digits[0]}, 1 - {digits['one']}")
# Output: 0 - ноль, 1 - один


📌Вы можете вызывать в f-строках методы объектов:

name = "Олег"
print(f"Имя: {name.upper()}")
# Output: Имя: ОЛЕГ


📌А также вызывать функции:

print(f"13 / 3 = {round(13/3)}")
# Output: 13 / 3 = 4


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

Со всеми возможностями f-строк вы можете ознакомится в PEP498.
Не забывайте, что их можно использовать только в версиях Python >= 3.6

#fstring
Что выведет функция print?

class A:
pass

class B:
value = 1

class C:
value = 3

class D(A, B, C):
def __str__(self):
return str(self.value)

print(D())
В этом простом примере Python ищет необходимый атрибут в родительских классах, поскольку они перечислены слева направо. То есть, Python начнет с просмотра A и, если A не имеет атрибута, то будет смотреть на B.

Таким образом, правильный ответ — 1.

📌Более подробно об этом можно почитать тут (MRO).
Что такое itertools?

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

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

Ранее мы уже немного касались itertools (tee, islice, permutations) а так же когда разбирали различия между генераторами и итераторами (здесь первая часть).
Chains 🔗

itertools.chain
Возвращает по одному элементу из первого итератора, потом из второго, до тех пор, пока итераторы не кончатся. Итоговый массив содержит все элементы данных итераторов.

📌Пример использования:

from itertools import chain

print(list(chain(['a', 'b', 'c'], range(4))))
# Output: ['a', 'b', 'c', 0, 1, 2, 3]


itertools.chain.from_iterable
Работает аналогично chain. Также выполняется объединение списков. Отличие заключается в том, что аргумент только один – вложенный список со списками, которые надо объединить.

📌Пример использования:

from itertools import chain

print(list(chain.from_iterable([['a', 'b', 'c'], range(4)])))
# Output: ['a', 'b', 'c', 0, 1, 2, 3]


#iterators #chain