Python и подчёркивание (_)

от автора

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

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

Когда используется подчёркивание

Именование

Пожалуй, самое распространенное и важное использование подчеркивания — это именование. Согласно PEP 8:

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

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

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

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

  • Camel case (myVariableName): Первое слово пишется в нижнем регистре, а первая буква последующих слов — в верхнем. Применяется в таких языках, как JavaScript, Java, C# и Swift.

  • Pascal case (MyVariableName): Первая буква каждого слова в верхнем регистре. Используется в Python (для имён классов), C#, Pascal, Java и C++.

  • Snake case (my_variable_name): Все слова пишутся в нижнем регистре с подчёркиванием между ними. Применяется в Python (для переменных и имен функций), а также в Ruby.

  • Screaming snake case (MY_VARIABLE_NAME): Все буквы пишутся в верхнем регистре, а слова разделяются подчёркиваниями. Используется в Python для констант, а также в C, C++ и Java.

  • Kebab case (my-variable-name): Слова в нижнем регистре, разделенные дефисами. Применяется в URL и именах классов CSS.

  • Hungarian notation (iCount, strName): Имена переменных содержат префиксы, указывающие на типы или области видимости. Раньше использовалась в C и C++.

Ниже приведены несколько примеров имен переменных на Python, в которых используется подчёркивание:

write_to_database() read_data()  df_history df_actual

Символ подчеркивания играет ещё одну роль в именовании в Python. Согласно PEP 8, если нужно создать объект с именем, которое конфликтует с зарезервированным, то можно добавить подчеркивание в конце:

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

Например, часто используются имена class_ и type_.

Подчеркивание также применяется в именовании констант. Как указано в PEP 8:

Константы обычно определяются на уровне модуля и записываются прописными буквами, а слова разделяются подчеркиванием.

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

NO_OF_DAYS SIGNIF_LEVEL RUN_DEBUGGER

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

Однако, стоит отметить, что классы в Python обычно не используют подчеркивание в именах. Например, вместо того чтобы назвать класс book_publisher, стоит назвать его BookPublisher. Существуют, конечно, хорошо известные исключения, такие как list и dict, но это не значит, что следует создавать подобные исключения самостоятельно.

Dunder- (Double UNDERscore) или магические методы

Эта роль также связана с именованием, но здесь речь идет об внутренних именах в языке Python. Вы часто будете замечать подчёркивания в именах так называемых магических методов. Это специальные методы, которые начинаются и заканчиваются двойными подчеркиваниями (__). Из-за этого двойного подчеркивания их иногда называют dunder-методами, что является сокращением от double underscore.

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

Вот несколько примеров dunder-методов в Python:

  • __init__: отвечает за создание экземпляров класса.

  • __str__: определяет поведение функций str() и print(), применяемых к объекту.

  • __len__: возвращает длину составного объекта.

  • __getitem__: обеспечивает поддержку индексации.

  • __add__, __mul__ и т.д.: позволяют объектам поддерживать арифметические операции.

Обратите внимание, что нам не стоит использовать dunder-методы напрямую; вместо этого они вызываются интерпретатором Python во время выполнения различных операций. Например, когда мы вызываем len(x), Python внутренне вызывает x.__len__(). Хотя нам не стоит использовать последний метод напрямую, но он будет работать без каких-либо проблем:

x = [1, 2, 3]  print(len(x)) # 3  print(x.__len__()) # 3

Хорошей практикой является не определять новые пользовательские dunder-методы. Однако, вполне допустимо переопределять существующие магические методы в пользовательских классах.

Специальные атрибуты

Хотя методы, начинающиеся и заканчивающиеся двойными подчеркиваниями, называются магическими или dunder-методами, атрибуты, следующие этой конвенции именования, обычно называются специальными атрибутами. Эти атрибуты автоматически создаются и управляются Python и предоставляют информацию об объектах. Вот несколько примеров:

  • __name__: используется модулями, классами, методами классов и функциями для хранения имени объекта.

  • __doc__: хранит строку документации модуля, класса, метода или функции.

  • __file__: используется модулями для хранения пути к файлу, из которого был загружен модуль.

Переменные-заглушки (dummy-переменные)

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

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

Используем переменную, инициализированную в цикле:

for i in range(1, 4):     print(f"number {i}")      # 1 # 2 # 3

Не используем переменную, инициализированную в цикле:

for _ in range(3):     print("Hello, World!")      # Hello, World! # Hello, World! # Hello, World!

Также хорошей практикой является использование подчеркивания для «захвата» объекта, возвращаемого функцией или методом, в тех случаях, когда этот объект не будет использоваться, как показано ниже:

def save(obj: Any, path: pathlib.Path) -> bool:     # объект успешно или неуспешно сохраняется     if not success:         return False     return True  _ = save(obj, pathlib.Path("file.csv"))

В этом примере мы присвоили возвращаемое значение функции save() переменной _. Это было сделано, потому что нам не нужно использовать этот объект в коде. Если бы нам понадобилось его использовать, мы бы поступили так:

saved = save(obj, pathlib.Path("file.csv")

Указание приватных методов и атрибутов

В Python нет возможности создать полностью приватные методы или атрибуты.

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

Рассмотрим следующий класс:

class Me:     def __init__(self, name, smile=":-D"):         self.name = name         self.smile = smile         self._thoughts = []      def say(self, what):         return str(what)      def _think(self, what):         self._thoughts += what

У нас есть класс Me, который представляет… вас. Можно создать себя, используя следующие атрибуты:

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

  • .smile, публичный атрибут → ваша улыбка видна всем, так что она определенно публична.

  • ._thoughts, приватный атрибут → ваши мысли принадлежат только вам, поэтому они приватны.

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

Рассмотрим два метода:

  • .say(), публичный метод → когда вы что-то говорите, люди могут вас услышать.

  • ._think(), приватный метод → ваши мысли остаются в тайне, и только вы можете их знать. Если вам хочется поделиться ими, используйте публичный метод .say(), но если вы предпочитаете хранить их при себе, применяйте приватный метод ._think().

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

def say_thought(self, which):         return self._thoughts[which]

Последняя операция в интерактивной сессии

В Python 3 подчеркивание также используется для хранения результата последней операции в сессии интерпретатора. Это может быть полезно для быстрого использования результата предыдущей операции в новой операции, когда результат не был присвоен имени. Например:

>>> 1 + 2 3 >>> _ * 3 9 >>> y = 10 >>> _ 9 >>> 100 >>> _ 100

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

Форматирование числовых значений

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

x = 1_000_000 print(x) # 1000000  print(1.009_232_112) # 1.009232112  print(1_021_232.198_231_111)   # 1021232.198231111

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

Использование functools.singledispatch

В functools.singledispatch подчеркивание (_) часто используется в качестве имени функции, чтобы указать, что диспетчеризированные функции являются анонимными реализациями, предназначенными для обработки определенных типов. Этот стилистический выбор предполагает, что имя функции не имеет значения; важно то, какой тип обрабатывает функция. Такое использование помогает сохранить пространство имен чистым и подчеркивает, что логика напрямую связана с механизмом singledispatch, а не предназначена для прямых вызовов. Вот пример из PEP 443:

from functools import singledispatch  @singledispatch def fun(arg, verbose=False):     if verbose:         print("Let me just say,", end=" ")     print(arg)  @fun.register(int) def _(arg, verbose=False):     if verbose:         print("Strength in numbers, eh?", end=" ")     print(arg)  @fun.register(list) def _(arg, verbose=False):     if verbose:         print("Enumerate this:")     for i, elem in enumerate(arg):         print(i, elem)   print(i, elem)

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

Если вы хотите использовать статические проверки на вышеуказанный код, такие как MyPy, будьте готовы к тому, что это может вызвать ошибку из-за того, что вы определяете _ более одного раза. Самое простое решение этой проблемы — добавить комментарий # type: ignore в конце строк, где определяется _. Альтернативно, можно дать обычные имена этим функция, но это будет нетипичным подходом для functools.singledispatch.

Интернационализация и локализация

Интернационализация (часто сокращаемая как i18n) и локализация (сокращаемая как l10n) делают приложения адаптируемыми к различным языкам и регионам без необходимости вносить изменения в код.

В Python эти два процесса могут быть реализованы с помощью модуля gettext, который позволяет приложениям поддерживать несколько языков. В gettext общепринято использовать символ подчеркивания (_) в качестве псевдонима для функции gettext, которая отмечает строки для перевода.

import gettext import locale  # Устанавливаем русскую локаль: locale.setlocale(locale.LC_ALL, "ru_RU.UTF-8")  # Устанавливаем путь к файлам перевода .mo # и выбираем текстовый домен: gettext.bindtextdomain(     "myapp",     "/path/to/my/locale/directory" ) gettext.textdomain("myapp")  # Подчеркивание обычно используется в качестве псевдонима для gettext.gettext: _ = gettext.gettext  # _() оборачивает текст для перевода.  # gettext.gettext() _() — извлекает переведенную строку. # Использование подчеркивания проще; сравните: print(_("Hello, World!"))  # Привет, мир! print(gettext.gettext("Hello, World!"))  # Привет, мир!

Итого: когда в приложении используется gettext, подчеркивание довольно полезно.

Игнорирование значений при распаковке

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

Присвоить ненужные значения подчеркиванию (_), которое действует как переменная-заглушка, можно так:

a, _, b = (1, 2, 3)  print(a) # 1  print(b) # 3

В этом примере подчеркивание используется для игнорирования центрального значения (2).

Если же нужно проигнорировать несколько значений, что может быть особенно полезно при работе с длинными последовательностями, то можно воспользоваться звёздочкой с подчеркиванием (*_):

a, *_, b = [1, 2, 3, 4, 5]  print(a) # 1  print(b) # 5

Мы присвоили первое и последнее значения списка переменным a и b соответственно. Остальные значения — 2, 3 и 4 — присвоены *_, что означает, что они игнорируются и больше не будут использоваться.

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

Заключение

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

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

👉Если тебе интересны и другие полезные материалы по IT и Python, то можешь подписаться на мой канал PythonTalk или посетить сайт OlegTalks👈


ссылка на оригинал статьи https://habr.com/ru/articles/871434/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *