Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 443 сообщений
Cообщения с меткой

питон - Самое интересное в блогах

Следующие 30  »
Кейси_Кармен

Смертельная схватка хищников: питон против крокодила! Кто кого?

Вторник, 09 Августа 2016 г. 08:43 (ссылка)


Не каждый день можно увидеть столь эпическую схватку двух хищников - питона и крокодила. Голодные звери готовы напасть даже на столь грозного противника, чтобы набить свое брюхо. Как вы думаете, кто победил в схватке: питон против крокодила?

1 (589x700, 246Kb)



крок2 (156x130, 20Kb)змея (175x85, 8Kb)
Метки:   Комментарии (8)КомментироватьВ цитатник или сообщество
ЛОИНА

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

Воскресенье, 24 Июля 2016 г. 12:48 (ссылка)


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

1-39-730x518 (700x496, 283Kb)

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

1 (700x415, 226Kb) Девушка обожала своего питона и спала с ним каждую ночь. Но вдруг змея начала худеть

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

3 (700x531, 320Kb)

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

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

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

4 (700x525, 432Kb)

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

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



Источник - http://www.factroom.ru/life/python-losing-weight

Метки:   Комментарии (9)КомментироватьВ цитатник или сообщество
Рыжая_красивая

Эта девушка обожала спать с питоном, но змея вдруг стала худеть... Узнав, в чём дело, я содрогнулся!

Суббота, 23 Июля 2016 г. 22:07 (ссылка)


Эта девушка обожала спать с питоном, но змея вдруг стала худеть... Узнав, в чём дело, я содрогнулся!




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



Читать далее...
Метки:   Комментарии (1)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Библиотека f для функционального программирования в Питоне

Пятница, 15 Июля 2016 г. 16:31 (ссылка)

Привет, коллеги!



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



— Что, еще одна функциональная либа для Питона? Автор, ты в курсе, что есть fn.py и вообще этих функциональных поделок миллион?



— Да, в курсе.



Причины появления библиотеки



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



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



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



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



Общие сведения



Библиотека написана на чистом Питоне и работает на любой ОС, в т.ч. на Виндузе. Поддерживаются обе ветки Питона. Конкретно я проверял на версиях 2.6, 2.7 и 3.5. Если возникнут трудности с другими версиями, дайте знать. Единственная зависимость — пакет six для гибкой разработки сразу под обе ветки.



Библиотека ставится стандартным образом через pip:



pip install f


Все функции и классы доступны в головном модуле. Это значит, не нужно запоминать

пути к сущностям:



import f

f.pcall(...)
f.maybe(...)
f.io_wraps(...)
f.L[1, 2, 3]


Пакет несет на борту следующие подсистемы:




  • набор различных функций для удобной работы с данными

  • модуль предикатов для быстрой проверки на какие-либо условия

  • улучшенные версии коллекций — списка, кортежа, словаря и множества

  • реализация дженерика

  • монады Maybe, Either, IO, Error



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



Функции



Первой функцией, которую я перенес в Питон из другой экосистемы, стала pcall из языка Луа. Я программировал на ней несколько лет назад, и хотя язык не функциональный, был от него в восторге.



Функция pcall (protected call, защищенный вызов) принимает другую функцию и возвращает пару (err, result), где либо err — ошибка и result пуст, либо наоборот. Этот подход знаком нам по другим языкам, например, Джаваскрипту или Гоу.



import f

f.pcall(lambda a, b: a / b, 4, 2)
>>> (None, 2)

f.pcall(lambda a, b: a / b, 4, 0)
>>> (ZeroDivisionError('integer division or modulo by zero'), None)


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




@f.pcall_wraps
def func(a, b):
return a / b

func(4, 2)
>>> (None, 2)

func(4, 0)
>>> (ZeroDivisionError('integer division or modulo by zero'), None)


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




def process((err, result)):
if err:
logger.exception(err)
return 0

return result + 42

process(func(4, 2))


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



Интерсно, что использование пары (err, result) есть ни что иное, как монада Either, о которой мы еще поговорим.



Вот более реалистичный пример pcall. Часто приходится делать ХТТП-запросы и получать структуры данных из джейсона. Во время запроса может произойти масса ошибок:




  • кривые хосты, ошибка резолва

  • таймаут соединения

  • сервер вернул 500

  • сервер вернул 200, но парсинг джейсона упал

  • сервер вернул 200, но в ответе ошибка



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



@f.pcall_wraps
def get_user(use_id):
resp = requests.get("http://local.auth.server",
params={"id": user_id}, timeout=3)

if not resp.ok:
raise IOError("")

data = resp.json()

if "error" in data:
raise BusinesException("")

return data


Рассмотрим другие функции библиотеки. Мне бы хотелось выделить f.achain и f.ichain. Обе предназначены для безопасного извлечения данных из объектов по цепочке.



Предположим, у вас Джанго со следующими моделями:



Order => Office => Department => Chief


При этом все поля not null и вы без страха ходите по смежным полям:



order = Order.objects.get(id=42)
boss_name = order.office.department.chief.name


Да, я в курсе про select_related, но это роли не играет. Ситуация справедлива не только для ОРМ, но и для любой другой структуры класов.



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



Функция f.achain безопасно проходит по цепочке атрибутов:



f.achain(model, 'office', 'department', 'chief', 'name')
>>> John


Если цепочка нарушена (поле равно None, не существуте), результат будет None.



Функция-аналог f.ichain пробегает по цепочке индексов. Она работает со словарями, списками и кортежами. Функция удобна для работы с данными, полученными из джейсона:



data = json.loads('''{"result": [{"kids": [{"age": 7, "name": "Leo"},
{"age": 1, "name": "Ann"}], "name": "Ivan"},
{"kids": null, "name": "Juan"}]}''')

f.ichain(data, 'result', 0, 'kids', 0, 'age')
>>> 7

f.ichain(data, 'result', 0, 'kids', 42, 'dunno')
>> None


Обе функции я забрал из Кложи, где их предок называется get-in. Удобство в том, что в микросерверной архитектуре структура ответа постоянно меняется и может не соответствовать здравому смыслу.



Например, в ответе есть поле-объект "user" с вложенными полями. Однако, если пользователя по какой-то причине нет, поле будет не пустым объектом, а None. В коде начнут возникать уродливые конструкции типа:



data.get('user', {]}).get('address', {}).get('street', '')


Наш вариант читается легче:



f.ichain(data, 'user', 'address', 'street') or ''


Из Кложи в библиотеку f перешли два threading-макроса: -> и ->>. В библиотеке они называются f.arr1 и f.arr2. Оба пропускают исходное значение сквозь функиональные формы. Этот термин в Лиспе означает выражение, которе вычисляется позже.



Другими словами, форма — это либо функция func, либо кортеж вида (func, arg1, arg2, ...). Такую форму можно передать куда-то как замороженное выражение и вычислить позже с изменениями. Получается что-то вроде макросов в Лиспе, только очень убого.



f.arr1 подставляет значение (и дальнейший результат) в качестве первого

аргумента формы:



f.arr1(
-42, # начальное значение
(lambda a, b: a + b, 2), # форма
abs, # форма
str, # форма
)
>>> "40"


f.arr2 делает то же самое, но ставит значение в конец формы:



f.arr2(
-2,
abs,
(lambda a, b: a + b, 2),
str,
("000".replace, "0")
)
>>> "444"


Далее, функция f.comp возвращает композицию функций:



comp = f.comp(abs, (lambda x: x * 2), str)
comp(-42)
>>> "84"


f.every_pred строит супер-предикат. Это такой предикат, который истиннен только если все внутренние предикаты истинны.



pred1 = f.p_gt(0)        # строго положительный
pred2 = f.p_even # четный
pred3 = f.p_not_eq(666) # не равный 666

every = f.every_pred(pred1, pred2, pred3)

result = filter(every, (-1, 1, -2, 2, 3, 4, 666, -3, 1, 2))
tuple(result)
>>> (2, 4, 2)


Супер-предикат ленив: он обрывает цепочку вычислений на первом же ложном значении. В примере выше использованы предикаты из модуля predicate.py, о котором мы еще поговорим.



Функция f.transduce — наивная попытка реализовать паттерн transducer (преобразователь) из Кложи. Короткими словами, transducer — это комбинация функций map и reduce. Их суперпозиция дает преобразование по принципу "из чего угодно во что угодно без промежуточных данных":



f.transduce(
(lambda x: x + 1),
(lambda res, item: res + str(item)),
(1, 2, 3),
""
)
>>> "234"


Модуль функций замыкет f.nth и его синонимы: f.first, f.second и f.third для безопасного обращения к элементам коллекций:



f.first((1, 2, 3))
>>> 1

f.second((1, 2, 3))
>>> 2

f.third((1, 2, 3))
>>> 3

f.nth(0, [1, 2, 3])
>>> 1

f.nth(9, [1, 2, 3])
>>> None


Предикаты



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



Я добавил несколько наиболее нужных предикатов в библиотеку. Предикаты могут унарными (без параметров) и бинарными (или параметрическими), когда поведение предиката зависит от первого аргумента.



Рассмотрим примеры с унарными предикатами:



f.p_str("test")
>>> True

f.p_str(0)
>>> False

f.p_str(u"test")
>>> True

# особый предикат, который проверяет на int и float одновременно
f.p_num(1), f.p_num(1.0)
>>> True, True

f.p_list([])
>>> True

f.p_truth(1)
>>> True

f.p_truth(None)
>>> False

f.p_none(None)
>>> True


Теперь бинарные. Создадим новый предикат, который утверждает, что что-то больше нуля. Что именно? Пока неизвесто, это абстракция.



p = f.p_gt(0)


Теперь, имея предикат, проверим любое значение:



p(1), p(100), p(0), p(-1)
>>> True, True, False, False


По аналогии:



# Что-то больше или равно нуля:
p = f.p_gte(0)
p(0), p(1), p(-1)
>>> True, True, False

# Проверка на точное равенство:
p = f.p_eq(42)
p(42), p(False)
>>> True, False

# Проверка на ссылочное равенство:
ob1 = object()
p = f.p_is(ob1)

p(object())
>>> False

p(ob1)
>>> True

# Проверка на вхождение в известную коллекцию:
p = f.p_in((1, 2, 3))

p(1), p(3)
>>> True, True

p(4)
>>> False


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



Дженерики



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



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



Выглядит это примерно так. Сначала создадим экземпляр дженерика:



gen = f.Generic()


Теперь расширим его конкретными обработчиками. Декоратор .extend принимает набор предикатов для этого обработчика, по одному на аргумент.



@gen.extend(f.p_int, f.p_str)
def handler1(x, y):
return str(x) + y

@gen.extend(f.p_int, f.p_int)
def handler2(x, y):
return x + y

@gen.extend(f.p_str, f.p_str)
def handler3(x, y):
return x + y + x + y

@gen.extend(f.p_str)
def handler4(x):
return "-".join(reversed(x))

@gen.extend()
def handler5():
return 42


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




gen(1, "2")
>>> "12"

gen(1, 2)
>>> 3

gen("fiz", "baz")
>>> "fizbazfizbaz"

gen("hello")
>>> "o-l-l-e-h"

gen()
>>> 42


Что случится, если не подошла ни одна стратегия? Зависит от того, был ли задан обработчик по умолчанию. Такой обработчик должен быть готов встретить произвольное число аргументов:



gen(1, 2, 3, 4)
>>> TypeError exception goes here...

@gen.default
def default_handler(*args):
return "default"

gen(1, 2, 3, 4)
>>> "default"


После декорирования функция становится экземпляром дженерика. Интересный прием — вы можете перебрасывать исполнение одной стратегии в другую. Получаются функции с несколькими телами, почти как в Кложе, Эрланге или Хаскеле.



Обработчик ниже будет вызван, если передать None. Однако, внутри он перенаправляет нас на другой обработчик с двумя интами, это handler2. Который, в свою очередь, возвращает сумму аргументов:



@gen.extend(f.p_none)
def handler6(x):
return gen(1, 2)

gen(None)
>>> 3


Коллекции



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



Улучшенные коллекции создаются или из обычных вызовом класса, или особым синтаксисом с квадратными скобками:



f.L[1, 2, 3]            # или f.List([1, 2, 3])
>>> List[1, 2, 3]

f.T[1, 2, 3] # или f.Tuple([1, 2, 3])
>>> Tuple(1, 2, 3)

f.S[1, 2, 3] # или f.Set((1, 2, 3))
>>> Set{1, 2, 3}

f.D[1: 2, 2: 3]
>>> Dict{1: 2, 2: 3} # или f.Dict({1: 2, 2: 3})


Коллекции имеют методы .join, .foreach, .map, .filter, .reduce, .sum.



Список и кортеж дополнительно реализуют .reversed, .sorted, .group, .distinct и .apply.



Методы позволяют получить результат вызовом его из коллекции без передачи в функцию:



l1 = f.L[1, 2, 3]
l1.map(str).join("-")
>>> "1-2-3"


result = []

def collect(x, delta=0):
result.append(x + delta)

l1.foreach(collect, delta=1)
result == [2, 3, 4]
>>> True


l1.group(2)
>>> List[List[1, 2], List[3]]


Не буду утомлять листингом на каждый метод, желающие могут посмотреть исходный код с комментариями.



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



f.L[1, 2, 3].filter(f.p_even)
>>> List[2]


f.S[1, 2, 3].filter(f.p_even)
>>> Set{2}


Словарь итерируется по парам (ключ, значение), о чем я всегда мечтал:



f.D[1: 1, 2: 2, 0: 2].filter(lambda (k, v): k + v == 2)
>>> Dict{0: 2, 1: 1}


Улучшенные коллекции можно складывать с любой другой коллекцией. Результатом станет новая коллекция этого (левого) типа:



# Слияние словарей
f.D(a=1, b=2, c=3) + {"d": 4, "e": 5, "f": 5}
>>> Dict{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'f': 5}

# Множество + стандартный спосок
f.S[1, 2, 3] + ["a", 1, "b", 3, "c"]
>>> Set{'a', 1, 2, 3, 'c', 'b'}

# Список и обычный кортеж
f.L[1, 2, 3] + (4, )
List[1, 2, 3, 4]


Любую коллекцию можно переключить в другую:



f.L["a", 1, "b", 2].group(2).D()
>>> Dict{"a": 1, "b": 2}

f.L[1, 2, 3, 3, 2, 1].S().T()
>>> Tuple[1, 2, 3]


Комбо!



f.L("abc").map(ord).map(str).reversed().join("-")
>>> "99-98-97"


def pred(pair):
k, v = pair
return k == "1" and v == "2"

f.L[4, 3, 2, 1].map(str).reversed() \
.group(2).Dict().filter(pred)

>>> Dict{"1": "2"}


Монады



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




  • Проверки входных значений основаны не на типах, как в Хаскеле, а на предикатах, что делает монады гибче.




  • Оператор >>= в Хаскеле невозможно перенести в Питон, поэтому он фигурирует как >> (он же __rshift__, битовый сдвиг вправо). Проблема в том, что в Хаскеле тоже есть оператор >>, но используется он реже, чем >>=. В итоге, в Питоне под >> мы понимаем >>= из Хаскела, а оригинальный >> просто не используем.




  • Не смотря на усилия, я не смог реализовать do-нотацию Хаскелла из-за ограничений синтаксиса в Питоне. Пробовал и цикл, и генератор, и контекстные менеджеры — все мимо.



Maybe



Монада Maybe (возможно) так же известна как Option. Этот класс монад представлен двумя экземплярами: Just (или Some) — хранилище положительного результата, в которм мы заинтересованы. Nothing (в других языках — None) — пустой результат.



Простой пример. Определим монадный конструктор — объект, который будет преобразовывать скалярные (плоские) значения в монадические:



MaybeInt = f.maybe(f.p_int)


По-другому это называется unit, или монадная единица. Теперь получим монадные значения:



MaybeInt(2)
>>> Just[2]

MaybeInt("not an int")
>>> Nothing


Видим, что хорошим результатом будет только то, что проходит проверку на инт. Теперь попробуем в деле монадный конвеер (monadic pipeline):



MaybeInt(2) >> (lambda x: MaybeInt(x + 2))
>>> Just[4]

MaybeInt(2) >> (lambda x: f.Nothing()) >> (lambda x: MaybeInt(x + 2))
>>> Nothing


Из примера видно, что Nothing прерывает исполнения цепочки. Если быть совсем точным, цепочка не обрывается, а проходит до конца, только на каждом шаге возвращается Nothing.



Любую функцию можно накрыть монадным декоратором, чтобы получать из нее монадические представления скаляров. В примере ниже декоратор следит за тем, чтобы успехом считался только возрат инта — это значение пойдет в Just, все остальное — в Nothing:



@f.maybe_wraps(f.p_num)
def mdiv(a, b):
if b:
return a / b
else:
return None

mdiv(4, 2)
>>> Just[2]

mdiv(4, 0)
>>> Nothing


Оператор >> по другому называется монадным связыванием или конвеером (monadic binding) и вызывается методом .bind:



MaybeInt(2).bind(lambda x: MaybeInt(x + 1))
>>> Just[3]


Оба способа >> и .bind могут принять не только функцию, но и функциональную форму, о которой я уже писал выше:



MaybeInt(6) >> (mdiv, 2)
>>> Just[3]

MaybeInt(6).bind(mdiv, 2)
>>> Just[3]


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



m = MaybeInt(2) >> (lambda x: MaybeInt(x + 2))
m.get()
>>> 3


Either



Эта монада расширяет предыдущую. Проблема Maybe в том, что негативный результат отбрасывается, в то время как мы всегда хотим знать причину. Either состоит из подтипов Left и Right, левое и правое значения. Левое значение отвечает за негативный случай, а правое — за позитивный.



Правило легко запомнить по фразе "наше дело правое (то есть верное)". Слово right в английском языке так же значит "верный".



А вот и флешбек из прошлого: согласитесь, напоминает пару (err, result) из начала статьи? Коллбеки в Джаваскрипте? Результаты вызовов в Гоу (только в другом порядке)?



То-то же. Все это монады, только не оформленные в контейнеры и без математического аппарата.



Монада Either используется в основном для отлова ошибок. Ошибочное значение уходит влево и становится результатом конвеера. Корректный результат пробрысывается вправо к следующим вычислениям.



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



EitherStrNum = f.either(f.p_str, f.p_num)

EitherStrNum("error")
>>> Left[error]

EitherStrNum(42)
>>> Right[42]


Проверим конвеер:



EitherStrNum(1) >> (lambda x: EitherStrNum(x + 1))
>>> Right[2]

EitherStrNum(1) >> (lambda x: EitherStrNum("error")) \
>> (lambda x: EitherStrNum(x + 1))
>>> Left[error]


Декоратор f.either_wraps делает из функции монадный конструктор:



@f.either_wraps(f.p_str, f.p_num)
def ediv(a, b):
if b == 0:
return "Div by zero: %s / %s" % (a, b)
else:
return a / b

@f.either_wraps(f.p_str, f.p_num)
def esqrt(a):
if a < 0:
return "Negative number: %s" % a
else:
return math.sqrt(a)

EitherStrNum(16) >> (ediv, 4) >> esqrt
>>> Right[2.0]

EitherStrNum(16) >> (ediv, 0) >> esqrt
>>> Left[Div by zero: 16 / 0]


IO



Монада IO (ввод-вывод) изолирует ввод-вывод данных, например, чтение файла, ввод с клавиатуры, печать на экран. Например, нам нужно спросить имя пользователя. Без монады мы бы просто вызвали raw_input, однако это снижает абстракцию и засоряет код побочным эффектом.



Вот как можно изолировать ввод с клавиатуры:



IoPrompt = f.io(lambda prompt: raw_input(prompt))
IoPrompt("Your name: ") # Спросит имя. Я ввел "Ivan" и нажал RET
>>> IO[Ivan]


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



import sys

@f.io_wraps
def input(msg):
return raw_input(msg)

@f.io_wraps
def write(text, chan):
chan.write(text)

input("name: ") >> (write, sys.stdout)
>>> name: Ivan # ввод имени
>>> Ivan # печать имени
>>> IO[None] # результат


Error



Монада Error, она же Try (Ошибка, Попытка) крайне полезна с практической точки зрения. Она изолирует исключения, гарантируя, что результатом вычисления станет либо экземпляр Success с правильным значением внутри, либо Failture с зашитым исключением.



Как и в случае с Maybe и Either, монадный конвеер исполняется только для положительного результата.



Монадический конструктор принимает функцию, поведение которой считается небезопасным. Дальнейшие вызовы дают либо Success, либо Failture:



Error = f.error(lambda a, b: a / b)

Error(4, 2)
>>> Success[2]

Error(4, 0)
>>> Failture[integer division or modulo by zero]


Вызов метода .get у экземпляра Failture повторно вызовет исключение. Как же до него добраться? Поможет метод .recover:



Error(4, 0).get()
ZeroDivisionError: integer division or modulo by zero

# value variant
Error(4, 0).recover(ZeroDivisionError, 42)
Success[2]


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




def handler(e):
logger.exception(e)
return 0

Error(4, 0).recover((ZeroDivisionError, TypeError), handler)
>>> Success[0]


Вариант с декоратором. Функции деления и извлечения корня небезопасны:



@f.error_wraps
def tdiv(a, b):
return a / b

@f.error_wraps
def tsqrt(a):
return math.sqrt(a)

tdiv(16, 4) >> tsqrt
>>> Success[2.0]

tsqrt(16).bind(tdiv, 2)
>>> Success[2.0]


Конвеер с расширенным контекстом



Хорошо, когда функции из конвеера требуют данные только из предыдущей монады. А что делать, если нужно значение, полученное два шага назад? Где хранить контекст?



В Хаскеле это проблему решает та самая do-нотация, которую не удалось повторить в Питоне. Придется воспользоваться вложенными функциями:




def mfunc1(a):
return f.Just(a)

def mfunc2(a):
return f.Just(a + 1)

def mfunc3(a, b):
return f.Just(a + b)

mfunc1(1) >> (lambda x: mfunc2(x) >> (lambda y: mfunc3(x, y)))
# 1 2 1 2
>>> Just[3]


В примере выше затруднения в том, что функции mfunc3 нужно сразу два значения, полученных из других монад. Сохранить контекст пересенных x и y удается благодаря замыканиям. После выхода из замыкания цепочку можно продолжить дальше.



Заключение



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



Ссылка на Гитхаб. Документация и тесты — там же. Пакет в Pypi.



Я надеюсь, специалисты по ФП простят неточности в формулировках.



Буду рад замечаниям в комментариях. Спасибо за внимание.


Original source: habrahabr.ru.

https://habrahabr.ru/post/305750/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
medsioclear

Питон не может ползти по мягкому одеялу: видео

Пятница, 01 Июля 2016 г. 06:10 (ссылка)


.

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
Заболотская_Анжела

Без заголовка

Четверг, 16 Июня 2016 г. 17:33 (ссылка)

Это цитата сообщения Radmillla Оригинальное сообщение

Став Питон. Автора не знаю




5916975_M5Y6uEdSau8 (497x604, 71Kb)



Эйваз (зерк.)-лагуз (пер.)-уруз (пер.)-туризас (пер.)-туризас-иса

Питон провоцирует раздвоение личности и в целом сумасшествие, 

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

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

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
Stepan_Sikora

Этот мужчина просто пошел в туалет. То, что случилось потом, похоже на фильм ужасов…

Пятница, 27 Мая 2016 г. 14:35 (ссылка)
fenixslovo.com/ru/incidents/11035

Совершенно жуткий случай произошел на днях в таиландском городе Чаченгсау. 38-летний Аттхапорн Бунмакхай (Atthaporn Boonmakchuay) подвергся нападению...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
алла_разумикина

«Милый» домашний любимец: дружба мальчика и 5-метрового питона

Вторник, 24 Мая 2016 г. 18:56 (ссылка)




Удивительная дружба мальчика и питона. | Фото: momentsjournal.com.




Удивительная дружба мальчика и питона. | Фото: momentsjournal.com.




Для современного европейца экзотичные страны экзотичны во всем. У тамошних жителей непохожий уклад жизни, другое мировосприятие, и даже домашние животные совсем не кошки и не собаки. Так в одном камбоджийской деревне мальчик завел себе питомца – 5-метрового питона. Вот уже несколько лет это его неразлучный друг.


Читать далее...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_kulturologia

Вокруг света: «Милый» домашний любимец: дружба мальчика и 5-метрового питона

Вторник, 24 Мая 2016 г. 14:21 (ссылка)


Для современного европейца экзотичные страны экзотичны во всем. У тамошних жителей непохожий уклад жизни, другое мировосприятие, и даже домашние животные совсем не кошки и не собаки. Так в одном камбоджийской деревне мальчик завел себе питомца – 5-метрового питона. Вот уже несколько лет это его неразлучный друг.

Подробнее..

http://feedproxy.google.com/~r/kulturologia/~3/FSsbl-AcKXw/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
Миша_Лютый

Новый сайт

Суббота, 30 Апреля 2016 г. 09:25 (ссылка)


Вот выложили вчера с ребятами из интерната в интернет наш сайт по языку программирования Питон: http://python.supercharts.ru/



Ждём ваших конструктивных предложений по его усовершенствованию take_example

Метки:   Комментарии (3)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Производственный календарь на Python

Среда, 06 Апреля 2016 г. 18:25 (ссылка)

image



Предисловие





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



Чтобы не откладывать ознакомление с данным модулем просто наберите в командной строке:

pip install prod-cal




Гарантирую что проект будет работать на Python 2.7 и Windows 7, т. к. на этой конфигурации он разрабатывался.



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



Главная цель данной статьи разобрать устройство данного модуля и наметить перспективы для его развития сообществом.



Чтобы не плодить календарей в моём календаре можно использовать все методы стандартного модуля calendar.Calendar.





Состав проекта





После установки проект будет доступен в C:\Python27\Lib\site-packages\prodcal, если вы устанавливали пакет в виртуальное окружение, то ищите его в: <домашний каталог вирт. окружения>\Lib\site-packages\prodcal



Проект можно вообще не устанавливать а скачать его напрямую с сайта PyPi. После чего распаковать и использовать код непосредственно в своём проекте.



Проект состоит из следующих файлов (все с расширением *.py):


  • config — описывает информацию о поддерживаемых календарях и о календаре выбранном по умолчанию

  • service — файл со вспомогательными функциями, вроде приведения типов и т.п., некоторые функции из этого файла мы разберём ниже

  • holidays — файл содержит реализацию основного и пока единственного класса ProdCal

  • каталог prodcals — содержит наборы календарей и файл prod_dict, который содержит реализацию класса ProdDict (о нём также ниже)





Примеры использования
from procal import ProdCal

my_first_prod_cal = ProdCal()

# Проверяем праздничный день 1 мая
my_first_prod_cal.is_work_day(2016, 5, 1)

# Проверяем рабочий день
my_first_prod_cal.is_work_day(2016, 4, 1)

# Проверяем выходной день
my_first_prod_cal.is_work_day(2016, 4, 2)

# Проверяем перенос празничного дня (рабочий день)
my_first_prod_cal.is_work_day(2016, 2, 20)

# Передаём сразу объект даты
my_first_prod_cal.is_work_day(date(2016, 5, 1)

# Передаём в качестве аргумента строку (today - сегодня)
my_first_prod_cal.is_work_day('today')

# Передаём в качестве аргумента строку (yesterday - вчера)
my_first_prod_cal.is_work_day('yesterday')

# Передаём в качестве аргумента строку (tomorrow - завтра)
my_first_prod_cal.is_work_day('tomorrow')

# Проверяем количество рабочих дней в различных месяцах
my_first_prod_cal.count_work_days([2016, 4, 1], [2016, 4, 30])
my_first_prod_cal.count_work_days([2016, 5, 1], [2016, 5, 31])
my_first_prod_cal.count_work_days([2016, 6, 1], [2016, 6, 30])

# Передаём сразу в формате даты и времени
my_first_prod_cal.count_work_days(date(2016, 4, 1), date(2016, 4, 30))
my_first_prod_cal.count_work_days(date(2016, 5, 1), date(2016, 5, 31))
my_first_prod_cal.count_work_days(date(2016, 6, 1), date(2016, 6, 30))

# Передаём дату начала ввиде текста (today, yesterday, tomorrow)
my_first_prod_cal.count_work_days('today', date(2016, 4, 30))
my_first_prod_cal.count_work_days('yesterday', date(2016, 4, 30))
my_first_prod_cal.count_work_days('tomorrow', date(2016, 4, 30))

# Передаём в качестве конечной даты количество дней от даты начала (включительно)
my_first_prod_cal.count_work_days([2016, 4, 1], 30)
my_first_prod_cal.count_work_days('today', 30)

# Проверяем количество выходных дней в различных месяцах
my_first_prod_cal.count_holidays([2016, 4, 1], [2016, 4, 30])
my_first_prod_cal.count_holidays([2016, 5, 1], [2016, 5, 31])
my_first_prod_cal.count_holidays([2016, 6, 1], [2016, 6, 30])

# Передаём сразу в формате даты и времени
my_first_prod_cal.count_holidays(date(2016, 4, 1), date(2016, 4, 30))
my_first_prod_cal.count_holidays(date(2016, 5, 1), date(2016, 5, 31))
my_first_prod_cal.count_holidays(date(2016, 6, 1), date(2016, 6, 30))

# Передаём дату начала ввиде текста (today, yesterday, tomorrow)
my_first_prod_cal.count_holidays('today', date(2016, 4, 30))
my_first_prod_cal.count_holidays('yesterday', date(2016, 4, 30))
my_first_prod_cal.count_holidays('tomorrow', date(2016, 4, 30))

# Передаём в качестве конечной даты количество дней от даты начала (включительно)
my_first_prod_cal.count_holidays([2016, 4, 1], 30)
my_first_prod_cal.count_holidays('today', 30)

# Рассчитываем конечную дату по рабочим дням
my_first_prod_cal.get_date_by_work_days([2016, 4, 1], 21))
my_first_prod_cal.get_date_by_work_days('today', 21)







Реализация



Структура производственного календаря



Все производственные календари находятся в подкаталоге prodcals в виде отдельных файлов. Формат названия файла соотв. буквенному коду страны по ISO в нижнем регистре. Например, росс. производственный календарь находится в файле ru.py.



Файл содержит два словаря: NON_WORK_DAY_DICT и WORK_DAY_DICT, они имеют одинаковую структуру, первый словарь описывает нерабочие дни (праздничные), а второй описывает переносы рабочих дней на выходные. Словари не содержат указания на «стандартные» нерабочие дни субботу и воскресенье.

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

Для удобства работы с календарём был сделан отдельный класс ProdDict (унаследован от стандартного словаря) в котором реализован метод is_value, который возвращает True или False в зависимости от наличия в словаре переданного значения. На вход данный класс принимает только даты. Реализация класса ProdDict описана в файле prod_dict (расположен в подкаталоге prodcals).



Реализация класса ProdCal



Данный класс может быть создан и без указания каких-либо аргументов, в этом случае будет использован календарь по умолчанию (российский). Если требуется указать какой календарь использовать, то необходимо передать именованный аргумент locale=<значение>, где значение — это код страны по ISO в любом регистре. Пример для создания производственного календаря Украины:

from prodcal import ProdCal
my_prod_cal = ProdCal(locale='UA')


В настоящий момент поддерживаются календари следующих стран: Беларусь, Грузия, Казахстан, Россия, Украина.



Методы класса ProdCal

is_work_day



Вход: дата, список (с int), кортеж аргументов, строка (поддерживает только: 'today', tomorrow', 'yesterday')

Выход: bool



Описание: проверяет заданную дату на предмет того рабочий ли сегодня день.



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



count_work_days, count_holidays



Вход: дата начала, дата окончания (периода), формат дат описан выше.

Выход: int



Описание: подсчитывает количество рабочих дней в заданном периоде (в случае count_work_days), а в случае count_holidays количество выходных дней.



get_date_by_work_days



Вход: дата начала, int

Выход: date



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



Описание сервисных функций



Напомню, что сервисные функции находятся в файле service.py.

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



def get_date_today(day):
today = datetime.today().date()
if 'today' == day:
return today
elif 'yesterday' == day:
return today - timedelta(days=1)
elif 'tomorrow' == day:
return today + timedelta(days=1)
raise ValueError('Unknown string format', day)




Магия возможности использования дат в различных форматах (если так корректно выражаться) реализована в функции cast.

Реализация функции cast
def cast(start_date, end_date):
if isinstance(start_date, (tuple, list)) and isinstance(end_date, (tuple, list)):
start_date, end_date = date(*start_date), date(*end_date)

if isinstance(start_date, str):
start_date = get_date_today(start_date)
elif isinstance(start_date, (tuple, list)):
start_date = date(*start_date)

if isinstance(end_date, (tuple, list)):
end_date = date(*end_date)
elif isinstance(end_date, int):
end_date = calc_days_by_int(start_date, end_date)

if isinstance(start_date, date) and isinstance(end_date, date):
pass
else:
raise ValueError("Unknown format for parse")




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



Ещё интересным местом является функция get_prodcals, которая по переданному значению подгружает из подкаталога prodcals нужный календарь. Возможность этого обеспечивается с помощью функции import_module() из стандартной библиотеки importlib, которая интерпретирует переданную строку как путь к модулю. Например: import_module('prodcal.prodcals.ru') эквивалентно from prodcals import ru. Главный смысл использования этой функции в том, чтобы не указывать явно какие календари загружать, что несколько облегчает дальнейшую поддержку.



Поддержка новых календарей



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



Планы на развитие



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



Также планируется добавить ряд новых функций, например: расчёт даты и времени по переданным часам, написать тесты совместимости с Python3 и поправить некоторые ошибки.



Для русскоязычных пользователей данная статья может выступать в роли документации по модулю, а вот для остальных придётся сделать отдельную документацию.



Для всех кто хотел бы поучаствовать в развитии этого и ряда других Open Source проектов доступен этот репозиторий.



Благодарность



Помимо меня в этом проекте участвует Аркадий Аристов из Челябинска, за что ему большое спасибо!






Интересно ли Вам читать статьи на подобные темы?




























Проголосовало 4 человека. Воздержался 1 человек.




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





Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/281040/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<питон - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda