Самый полезный модуль стандартной библиотеки Python, о котором все постоянно забывают +39





В Python много отличных доступных «из коробки» модулей. Один из самых полезных — collections. Он содержит «специализированные типы для создания контейнеров», являющихся альтернативами универсальным dict, list, set и tuple. Ниже мы рассмотрим три содержащихся в модуле класса, с которыми большинство питонистов сталкивались, но постоянно забывают применять на практике.


NamedTuple


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


Всего пара строк может привести скрипт в порядок. Смотрите:


from collections import namedtuple

Features = namedtuple('Features', ['age', 'gender', 'name'])
row = Features(age=22, gender='male', name='Alex')
print(row.age)

Теперь для доступа к элементам строки вместо указания индексов можно использовать имена, что делает код значительно чище и проще.


Counter


Counter, как следует из названия, считает. Звучит несложно, но дата-саентистам нужно вести подсчёты постоянно, поэтому инструмент крайне полезен на практике.


Есть несколько способов создать счётчик, но самый простой — инициализировать его списком значений:


from collections import Counter

ages = [22, 22, 25, 25, 30, 24, 26, 24, 35, 45, 52, 22, 22, 22, 25, 16, 11, 15, 40, 30]
value_counts = Counter(ages)
print(value_counts.most_common())

Запустив этот код (что, кстати, можно сделать передав соответствующий сниппет в pythonanywhere.com/gists/), вы увидите:


[(22, 5), (25, 3), (24, 2), (30, 2), (35, 1), (40, 1), (11, 1), (45, 1), (15, 1), (16, 1), (52, 1), (26, 1)]

Список кортежей в порядке убывания распространённости значений, где первый элемент кортежа — значение, а второй — как часто оно встречается в изначальном списке. Пары строк кода оказалось достаточно, чтобы узнать, что «22» — самый распространённый возраст, и встречается он 5 раз.


DefaultDict


Один из моих самых любимых инструментов стандартной библиотеки. DefaultDict — словарь с дефолтным значением для любого нового ключа. Пример:


from collections import defaultdict

my_default_dict = defaultdict(int)
for letter in 'the red fox ran as fast as it could':
	my_default_dict[letter] += 1
print(my_default_dict)

Возвращает:


defaultdict(<type 'int'>, {'a': 4, ' ': 8, 'c': 1, 'e': 2, 'd': 2, 'f': 2, 'i': 1, 'h': 1, 'l': 1, 'o': 2, 'n': 1, 's': 3, 'r': 2, 'u': 1, 't': 3, 'x': 1})

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


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


Вперёд — к чистому коду!


Попробуйте вспомнить, можно ли применить классы из collections к задачам, которые вы недавно решали. Иногда «переоткрытие» старых модулей стандартной библиотеки приносит больше пользы, чем освоение новых инструментов.

Вы можете помочь и перевести немного средств на развитие сайта

Теги:



Комментарии (26):

  1. 1755
    /#20980904

    С появлением python3.7 вместо named tuples лучше использовать dataclasses :)

    • ZuOverture
      /#20981646

      Мм, named tuple c регулируемой мутабельностью и дефолтными значениями без кровотечения из глаз… Спасибо.

      • ardraeiss
        /#20981808

        dataclasses вообще вкусная конфигурируемая штука.

    • Murtagy
      /#20981718

      А что насчет производтельности? Туплы очень легкие структуры

      • eugals
        /#20982528

        Насчёт потребления памяти к dataclasses в их нынешнем виде есть много претензий. NamedTuple они в этом по-прежнему уступают.
        Проблему можно обойти, если использовать дополнительную обёртку вроде этой. Но какой смысл было так торопиться, чтобы непременно зарелизить эти dataclasses во чтобы то не стало в 3.7 без предварительного решения такого ключевого вопроса, как встроенная поддержка slots — от меня ускользает.

        • ADR
          /#20984276

          At least for the initial release, slots will not be supported. slots needs to be added at class creation time. The Data Class decorator is called after the class is created, so in order to add slots the decorator would have to create a new class, set slots, and return it. Because this behavior is somewhat surprising, the initial version of Data Classes will not support automatically setting slots.

          https://www.python.org/dev/peps/pep-0557/#support-for-automatically-setting-slots

    • worldmind
      /#20981934

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

    • TrueMaker
      /#20983420

      C появлением 3.7 не мало вещей так же и сломалось, что держит не мало компаний от миграции с 3.6. А также не так уже и много разработчиков будут уделять внимание новым плюшкам ради обратной совместимости. Пруфа ради чего только стоят попытки делать код обратно совместимым с Python 2.7. Если люди идут даже на такое, то о dataclasses на ближайшие лет 5 можно забыть.

  2. Ormgair
    /#20981176

    При работе с обычным словарём пришлось бы постоянно проверять, существует ли ключ, и инициализировать несуществующие ключи вручную.

    Не обязательно, у словарей есть метод get
    my_default_dict = dict()
    for letter in 'the red fox ran as fast as it could':
        my_default_dict[letter] = my_default_dict.get(letter,0) + 1
    print(my_default_dict)


    Вывод:
    {'a': 4, ' ': 8, 'c': 1, 'e': 2, 'd': 2, 'f': 2, 'i': 1, 'h': 1, 'l': 1, 'o': 2, 'n': 1, 's': 3, 'r': 2, 'u': 1, 't': 3, 'x': 1}

    • evil_homer
      /#20981722

      А еще есть setdefault

      new = {}
      for key, value in data:
          group = new.setdefault(key, []) # key might exist already
          group.append(value)

  3. stalker1984
    /#20981196

    Статью надо было назвать — "три полезных для датасайнтистов <\b> функции из библиотеки ..."

    • ardraeiss
      /#20981818

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

    • deadmoroz14
      /#20982536

      Мне кажется, дата сайнтисты ванильным питоном не пользуются. Pandas в руки и вперёд.
      Первые 2 проблемы решаются из коробки.

      • CrazyElf
        /#20983442

        Pandas часто слишком медленный в текущей реализации, поэтому и чистый Python и Numpy массивы/матрицы очень даже в ходу у дата-сатанистов. Можно убедиться, посмотрев кернелы на Kaggle.

  4. arthuriantech
    /#20981878

    У встроенных словарей есть метод __missing__ который может обрабатывать промахи в словаре:


    class MyDict(dict):
        def __missing__(self, key):
            print("Oh no")
            return 123

    https://docs.python.org/3/library/stdtypes.html?#index-51

    • MikiRobot
      /#20982962

      Так будет короче

      dictionary.get('not_found_key', 123)

      • ardraeiss
        /#20983026

        Разное ведь — missing для личной обработки отсутствия элемента в своём классе, get со вторым параметром для стандартных.

      • arthuriantech
        /#20983068

        Этот метод работает только с оператором [] и предназначен для других вещей — создания собственных классов словарей с видоизмененной логикой d[key] без оверхеда:


        >>> class Counter(dict):
        ...     def __missing__(self, key):
        ...         return 0
        >>> c = Counter()
        >>> c['red']
        0
        >>> c['red'] += 1
        >>> c['red']
        1

        >>> class Default(dict):
        ...     def __missing__(self, key):
        ...         return key
        ...
        >>> '{name} was born in {country}'.format_map(Default(name='Guido'))
        'Guido was born in country'

  5. MikiRobot
    /#20982990

    Спасибо! Информации по стандартной библиотеке очень не хватает, особенно на русском очень сложно что то найти. #sarcasm

    • CrazyElf
      /#20983454

      Да ну ладно, вещи реально полезные, сам через их «открытие» проходил когда-то как начинающий дата-сайентист :)

  6. alexyr
    /#20983460

    from collections import OrderedDict

    особенно удобен для работы с JSON файлами, чтобы сохранить внутренний порядок элементов

    • wildraid
      /#20984356

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

      • alexyr
        /#20984362

        Завидую тем, кто может писать только для третьего питона

        • netch80
          /#20985222

          Причём для >=3.6 :)
          для 2.7 и для 3.0-3.5 порядок ключей различный даже при одинаковом seed?е.