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

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

И ни единого слова о датаклассах...

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

На самом деле с датаклассами огрооомные проблемы, что с наследованием, что с представлением их как обычных словарей. Я некоторое время был влюблен в typing.TypedDict, но и у них проблемы с тем, что update требует кастов. В итоге, пришлось велосипедить свой класс, который очень похож на dataclass, но на самом деле UserDict


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

Дык для наследования можно и обычные классы написать, раз с дженериками проблема?

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

А если серьёзно – это же два совершенно разных случая (нужно упасть при отсутствии значения или нужно вернуть дефолтное значение). Вернуть дефолт там, где надо упасть – как бы не хуже обратной ошибки: при этом ошибка «заметается под ковёр» и всплывёт в труднопредсказуемом месте.
Естественно, они и вызываются по разному.
when others then null
Джаваскриптеры учат питонистов, остальной мир наблюдает.

Блин, статья в блоге про питон, переведенная со статьи автора, который тоже пишет про питон, и… опять джаваскрипт виноват :D

Если вы хотите typesafety в этом месте, то надо использовать нормальный Option. В Питон его не завезли, правда, так что страдайте. set_default — замечательный метод словить "молча работает неправильно" там, где этого не ожидают. exception нам обещает либо веселье при тестировании, либо непротестированный код в except-ветке.


Короче, питон и typesafety не совместимы. Насколько безопасными являются утки? А если они при этом выглядят как утки, крякают как утки и плавают как утки? Наверное, это утки, а не ремни безопасности.

и Option должен быть монадой и монады должны быть поддержаны сахаром в виде do-блоков.

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


Option в Rust прекрасно реализует всё, что полезно, и не грузит мозги необходимостью думать про монады (мозги пригодятся для раздумий о lifetimes),

Монады нужны тем, кто делает Option, List и тому подобное (включая кучу нужных для чистого ФП) – чтобы они были унифицированы.
<сарказм>
Рекомендую так же не использовать обращение к атрибутам через точку (some_object.some_attr). Ведь если атрибут отсутствует, мы получим AttributeError. Используйте функцию getattr или метод __getattribute__().

Вообще рекомендую воздержаться от использования потенциально опасных методов структур данных, которые выбрасывают ошибку, например, remove() или index()

По этим же причинам не стоит использовать срезы. Используйте вместо них циклы.

Ибо ошибки это плохо, а мы хотим быть хорошими программистами
</сарказм>

Надо еще добавить locals() и globals() и код станет абсолютно безопасным, можно деплоить.

Какие-то «вредные советы» получаются.
А что делать, если дефолта нет?
А если бизнес-логика должна обрабатывать отсутсвие ключа в словаре?
В статье категорично предлагается get(), хотя он, очевидно, не решает вышеназванные вопросы, а просто переносит «проблему» в другое место, где точно так же нужно будет писать if, но сравнивать уже возвращенное значение со значением по-умолчанию. Как-то не видно преимуществ, если честно.

Я думаю, изначальный посыл автора был мог бы быть в том, чтобы не писать обработку KeyError там где можно использовать .get / .setdefault. Но никто, включая автора, этого не понял )

Раньше тоже при обращении к словарю использовал кв. скобки и try...except. Думал, какая разница, обращение через метод get() или квадратные скобки. Последний способ мне больше нравился. А потом присмотрелся к get() и обнаружил для себя полезную штуку — в случае отсутствие ключа, вернуть стандартное значение. А об этом пишут в каждой книге и в каждой статье(про словари).
get() с дефолтом — очень хорошо, когда у нас дальше используется одинаковый алгоритм для значения из словаря и для дефолта. Но часто бывает, что get() используется чтобы не писать try/except. типа:

x = d.get('x', None)
if x is None:
    ....


То есть, для обработки дефолта — потом все равно делается особая ветка кода. По сути, оба варианта (c get() и с try/except) работают одинаково, только выглядят иначе. Но они по разному выглядят в коде. И тут у подхода через исключения есть огромный плюс — по нему нам проще читать код. Если мы пытаемся понять саму логику кода — то читаем обычный код (и он короткий, простой), и «по диагонали» проглядываем обработку exception, которая прописана в другом месте.

И, как всегда, ни единого слова про defaultdict

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

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

Эта статья уже устарела. В Python 3.6 порядок ключей по добавлению уже был как побочный эффект реализации, в Python 3.7+ расположение ключей в словаре в порядке добавления закреплено на уровне языка. В Python 3.8 появилась поддержка reversed для словарей.

НЛО прилетело и опубликовало эту надпись здесь

OrderedDict довольно часто бывает полезным. Почему бы не сделать словарь упорядоченным by design, более того если это достигается бесплатно. В Py36 взяли более эффективную реализацию словаря из PyPy, которая имела такое свойство, затем просто закрепили гарантию упорядоченности ключей на уровне языка. Теперь любая реализация Python должна обеспечивать упорядоченность ключей по добавлению. Не вижу ничего плохого.

Дело в том, что не бывает в природе «более эффективной реализации» «вообще». Если у вас хорошая хеш функция, то хеш мап обычно при поиске лучше, чем почти все деревья, а упорядоченный мап — это вероятно дерево. Так что вряд ли…
Да я верю, что вы это не придумали, просто мотивация немного странная… впрочем, у нее есть непротиворечивая интерпретация — если у вас из коробки есть ровно одна реализация словаря, то да, можно сказать, что «мы выбрали самую быструю» (при некоторых условиях, которые забыли озвучить).
Так OrderedDict не упорядочен в обычном понимании (т.е. не сортирует ключи), он просто сохраняет порядок вставки. Назвали б лучше стабильным, по аналогии со стабильной сортировкой.
Ну, это так или иначе ограничивает возможные варианты реализации. Я в общем не вижу тоже ничего плохого в таком выборе — ну решили и решили, врял ли кому-то нужен словарь, специально не сохраняющий порядок.
К сожалению, get и setdefault вычисляют свой второй параметр энергично, и это ограничивает область применимости обоих методов.
Странный выбор статьи для перевода, при условии что даже оригинал поста получил не очень лестные комментарии.

Такой сейчас на хабре тренд — перевод манов.
Тоже бизнес, кстати.
Как отвечал мой знакомый лет 25 назад на всякоразно вопросы на очередной фидопойке:
"Чтений манов вслух — $50/час".

Получал и буду продолжать получать значения из словаря через квадратные скобки. Если вы так беспокоитесь за содержимое внутри (а один из самых распространённых кейсов это получение JSON в RestAPI) то используйте JSON schema. Там можно досконально описать структуру словаря без всяких if и try/except

Немного не в тему, но всё-же скажу.
В pandas к колонке можно обратиться как df.column, так и df['column'].
Как они это сделали?
(Поправьте меня если я что-то не так сказал)

Очень не рекомендую этим пользоваться, но как-то так


class UglyDict(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

my_dict = UglyDict({"key": "value"})
print(my_dict["key"]) # "value"
print(my_dict.key) # "value"

my_dict.key2 = "test"
print(my_dict) # {'key': 'value', 'key2': 'test'}

Просто переопределили методы __getattr__ и __setattr__, никакой особой магии там нет. Смотрите код: https://github.com/pandas-dev/pandas/blob/6010141a83234652a9a676df38d8d13f183bad2b/pandas/core/generic.py#L5216-L5273


pandas DataFrame — это не словарь, это очень-очень большой класс с кучей барахла, который мимикрирует под NumPy массив в некоторых операциях с векторизацией и предоставляет ещё кучу всяких методов, в том числе реализацию методов __getitem__ и __setitem__ для поддержки [].


А про магические методы у классов в Python почитайте эту статью если интересно:
https://habr.com/ru/post/186608/

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории