Как стать автором
Обновить

Комментарии 53

«Джаваскрипту или Гоу» — первый раз встречаю не латиницей. Красивее будет если написать в более привычном стиле, imho.
При слове «Виндуз» у меня возникло ощущение, будто открыл «Навигатор игрового мира» за 1999 год.

Значит, в 1999 году в журнале работал толковый редактор.

а как же «джейсону»?

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

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

Большинство научится, а своё «упрямство» я неоднократно объяснял со всех возможных сторон.

Читайте больше на русском, дальше не читал.

Как вы изящно над собой же и пошутили в контексте нашего разговора!


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

Смешно. Никогда не понимал людей, которые вместо того чтобы использовать терминологию и характер написания принятые в интернациональной среде, обязательно пытаются «локализовать». Я открою вам небольшой секрет, в современном мире, если вы хотите быть на переднем крае в любой области необходимо бОльшую часть времени уделять именно международным источникам. Зачем это искусственное усложнение? Есть и без этого огромное количество вещей, которые затрудняют чтение профессиональных статей.
Если же вы используете источники только на русском языке… это наверное очень круто, быть всегда на шаг позади? Зачем это искусственное огораживание от свежей информации? Зачем все эти усложнения с локализацей? Лень? Нежелание менять привычки? Ненависть ко всему нерусскому?
К автору поста у меня претензий нет, мне все равно как он написал, хотя и читается так себе. Но вот к таким как вы, лучше не раздавать людям советов.

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


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


Ненависти ко всему нерусскому у меня нет, разумеется, как и ко всему русскому. На английском я буду писать по-английски, на русском — по-русски.


Если же вы используете источники только на русском языке… это наверное очень круто, быть всегда на шаг позади?

Вы что-то перепутали. Я вам посоветовал читать побольше по-русски. Не пользовать источниками только на русском, а просто больше читать по-русски.


Но вот к таким как вы, лучше не раздавать людям советов.

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

Я написал именно в привычном стиле.

Привет! Добавь сюда свою либу https://github.com/xgrommx/awesome-functional-programming#python
Очень много «левых сдвигов», в итоге вкусные вещи лежит в той же библиотеке, что и примитивнейшие предикаты или коллекции, для которых есть collection. Ну то есть тебя не устроила стандартная либа, и ты написал свою, чтобы не ковыряться в collection? В таком случае, кто будет использовать твои коллекции?
Кроме того, заимствования из Go лежат рядом с заимствованиями из Haskell. pcall — интересно, но в одной библиотеке с дженериками? А вообще знающие люди пишут свой контекст «with as», если надо избежать множественных «try, except».

Интересно, как они это пишут? Примеры?

примерно так:

class SocketContext:
    def __init__(self, addr, port):
        self.sock = socket.socket()
        self.sock.connect((addr, port))

    def __enter__(self):
        return self.sock

    def __exit__(self, type, value, tb):
        print_tb(tb)
        sock.close()
        return True

Это менеджер контекста, если далее
with SocketContext(«example.com», 80) as sock:
    # Do smth

Срабатывает __init__(«example.com», 80), затем __enter__ передаёт значение в sock (разумеется можно несколько, я в последний раз передавал функцию, чтобы получить замыкание), при ЛЮБОМ выходе (исключение, корректное завершение, return внутри блока) из контекста вызывается __exit__, если было исключение, то его можно обработать как tb, если __exit__ вернёт False, то вроде бросится исключение наружу.

Вспоминаем известный with open(name) as fin: — суть в том, что что бы мы не делали, файл будет закрыт после выхода из контекста.

Спасибо, я знаю, как работает контекстный менеджер. Мне не ясно, как он позволит, цитирую, "избежать множественных «try, except»".

pcall_wraps как я понимаю обёртывает функцию в
try:
    return (func(*args, **kwargs), None)
except Exception, e:
    return (e, None)

Для единообразной арифметики, это, пожалуй, сэкономит код, но если ты хочешь «деструктор», который бы освобождал ресурсы/закрывал дескрипторы и по-разному обрабатывал разные ошибки, то всё равно надо каждый писать что-то кастомное. В сетях и парсинге всякого текста приходится как раз таки «закрывать дескрипторы». И опять таки это 7 строчный декоратор, который не лень и самому написать.
но если ты хочешь «деструктор»
Я не хочу никакого деструктора, и ничего не хочу закрывать. О чем вы?

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

Я, конечно, не гуру Python, но мне кажется, что это несколько не python-way, и мне казалось, что обычно стараются наоборот писать декларативно.


Сам я больше использую C# и стараюсь, где возможно, использовать лямбды и LINQ (не перебарщивая, конечно). ИМХО, декларативный стиль куда нагляднее

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

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

Whoops


Написано же:


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

Тогда мне тем более не понятно, для чего давать заведомо нерабочий пример.

Это рабочий пример. Вы запускаете его не в том окружении. В тексте это оговорено.

Вы пишите «питоном занимаюсь довольно давно», а давно это сколько?
Я вот лично считаю, что тоже давольно давно работаю с питоном, лет семь, это давно по-вашему? Давнее вашего?

Тоже 7 лет.

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

Не уловил связь между "давно" и "Тогда вы должны понимать", объясните?

Если вы работаете давно, значит у вас есть опыт разработки, вы понимаете язык, не пытаетесь писать на питоне в стиле С++, знаете библиотеку и уже прошли этап экспериментов и наивного новаторства.

Знатная солянка, узнал много нового, спасибо. В особенности monadic pipeline — ваще огонь )
А вот такое легаси я бы переписал на "длинно и некрасиво":


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

foo = [ord(x) for x in 'abc']
'-'.join(str(x) for x in foo[::-1])
'99-98-97'

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


def test_f():
    f.L("abc").map(ord).map(str).reversed().join("-")

def test_p():
    foo = [ord(x) for x in 'abc']
    '-'.join(str(x) for x in foo[::-1])

print(timeit.timeit(test_f, number=1000), timeit.timeit(test_p, number=1000))
0.0127332210541, 0.00602507591248

И еще я думал так тоже работать будет:


f.p_num(Decimal(10)), f.p_num('1')
False, False

Decimal(10) + 4, '1'.isdigit()
Decimal('14'), True

Хотя, конечно, вы об этом не писали.

Да, и спасибо за вклад в Open Source. Это важно и нужно.

Спасибо за полезный отзыв.


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


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


Насчет предикатов — да, Decimal тоже нужно учесть. А строка не долна проходить проверку на число.

Чисто ради интереса, ваш код:
foo = [ord(x) for x in 'abc']
'-'.join(str(x) for x in foo[::-1])
'99-98-97'

, можете сказать, чем он лучше, например, такого:
'-'.join(map(str, [ord(x) for x in 'abc'[::-1]]))
'99-98-97'

или даже такого:
'-'.join(map(str, map(ord, 'abc'[::-1])))

Читаемость? Или есть ещё какой-то смысл разделить более сложное выражение с мапами на два с генераторами?

По скорости map выигрывает генераторы (на хабре статья была с измерениями, не найду уже), но если мой код в test_p2 и test_p3, то как-то так:

print(timeit.timeit(test_p, number=1000), timeit.timeit(test_p2, number=1000), timeit.timeit(test_p3, number=1000))
0.0027002159040421247 0.0017352891154587269 0.001657433109357953

Вы просто оптимизировали, а я небольшой фанат матрешек, вот без мап:


def test_map(): '-'.join(map(str, [ord(x) for x in 'abc'[::-1]]))
def test_comp(): '-'.join(str(ord(x)) for x in 'abc'[::-1])
print(timeit.timeit(test_map, number=1000), timeit.timeit(test_comp, number=1000))
(0.003298044204711914, 0.0029828548431396484)

У меня comprehension быстрее.

Если в test_map второй генератор тоже в map засунуть, то на моём железе отличие лишь в четвёртом знаке после запятой. В общем, на мой взгляд, ваш последний вариант наиболее ясен и элегантен.

Проморгал, что не тот вариант засунул.


2.7.11:


In [1]: import timeit
In [2]: def test_map(): '-'.join(map(str, map(ord, 'abc'[::-1])))
In [3]: def test_comp(): '-'.join(str(ord(x)) for x in 'abc'[::-1])
In [4]: print(timeit.timeit(test_map, number=1000), timeit.timeit(test_comp, number=1000))
(0.0022649765014648438, 0.0031321048736572266)

3.5.1:


In [1]: import timeit
In [2]: def test_map(): '-'.join(map(str, map(ord, 'abc'[::-1])))
In [3]: def test_comp(): '-'.join(str(ord(x)) for x in 'abc'[::-1])
In [4]: print(timeit.timeit(test_map, number=1000), timeit.timeit(test_comp, number=1000))
0.001268998021259904 0.001320684008533135

Мап быстрее в обоих случаях.

Я что-то не врубился в pcall. Можете прояснить?
Заворачивать вызов в try с отловом четырех исключений означает сделать код абсолютно нечитаемым.
А зачем заворачивать в 4? заверните в один, и будет то же что и в pcall
try:
user = get_user(use_id)
except Exception as e:
# Do something

А если исключения все же нужно обрабатывать по разномо, то такой код помоему читабельней
try:
user = get_user(use_id)
except Exception1 as e:
# Do something
except Exception2 as e:
# Do something
except Exception3 as e:
# Do something

чем

err, user = pcall(get_user(use_id))
if isinstance(err, Exception1):
# Do something
elif isinstance(err, Exception2):
# Do something
elif isinstance(err, Exception3):
# Do something

Или не так я использую pcall?
не знаю почему форматирование слетело. возможно как-то связано с моим read-only статусом

преимущество в том, что не приходится таскать за собой блок try-catch, который вы рано или поздно забудете.

Да ладно, try-catch рано или поздно забудете, а проверить кортеж на наличие в нем исключения нет?)
На мой взгляд просто внесение в язык полюбившейся фичи из другого языка, короче говоря не более чем вкусовщина.
а проверить кортеж на наличие в нем исключения нет?)

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

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

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

func(4, 2)[1]
>>> 2

Предлагаешь делать каждый раз
import f as fun


Примеры накладок
def f(x):
    return x * x + 1

f = open('lalala')
f.close()


Да и python-pylint реагирует на такие имена, говорит «нужно прорефакторить имя, слишком короткое».

Написал бы программу чисто на ФП, чтобы можно было посмотреть на читаемость (на соответствие Zen'у).

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

я этого не предлагал, где вы увидели?


линтеры pylint и flake8 гибко настраиваются.


мы уже похоронили второй питон

никому не интересно, что именно вы похоронили. Я работал в компаниях, чей бизнес крутится исключительно на втором питоне.

Я работал в компаниях, чей бизнес крутится исключительно на втором питоне.

Да всем пофиг, где ты там работал. Тебе говорят про питон вообще. Ну, сдохнет твоя библиотека вместе со вторым питоном. Да она даже не оживёт, нечему подыхать будет.

я этого не предлагал, где вы увидели?

Ты занял имя, вместо того чтобы сделать, как все делают — уникальное имя, а дальше, кому надо, тот сократит до f.

линтеры pylint и flake8 гибко настраиваются.

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

Не пойму, с чего такой грубый тон? Словно я вас к чему-то принуждаю.


Библиотека работает на диапазоне версий 2.6 — 3.5. Вы, наверное, не заметили это в тексте. Комментировать остальное я не считаю нужным.

Я ходил на PyPI, вот твоя ссылка https://pypi.python.org/pypi/f и там Python 2.7 выставлен. Не можешь заполнить информацию о пакете правильно? А зачем выкатил тогда?
Интересно посмотреть на альтернативное решение частых задач. Можно использовать как библиотеку «под рукой» — набросать скриптик в консоли, обработать пачку данных. Лаконичность конструкций в такой ситуации — большой плюс
pcall, pcall_wraps
Так принято в го, но не в питоне. В питоне принято пользоваться исключениями.
Даже если вам нравится такой стиль, это не повод тащить его в питон: функции у вас в проекте будут вести себя неконсистентно — одни будут кидать исключения, а другие — возвращать ошибку.

achain, ichain
В питоне 3.6+ будет способ это делать нативно:
None-aware operators — www.python.org/dev/peps/pep-0505

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

Список и кортеж дополнительно реализуют .reversed, .sorted, .group, .distinct и .apply.
Зачем? Это всё можно делать либо встроенными функциями, либо с помощью list/dict/set comprehensions, либо с помощью generator expressions.

pep-505 не факт, что примут, Гвидо не решил ничего)
Спасибо за библиотеку. Это набор функций, к которым всё равно так или иначе приходишь со временем, а у вас уже оформлено в пакет. Для себя утащил achain/ichain, comp и монады.

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

Публикации

Истории