Оператор pass: Как ничего не делать в Python

Оглавление

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

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

В этом уроке вы узнаете:

  • Что такое оператор Python pass и почему он полезен
  • Как использовать инструкцию Python pass в производственном коде
  • Как использовать инструкцию Python pass в качестве вспомогательного средства при разработке кода
  • Каковы альтернативы pass и когда их следует использовать

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

Оператор Python pass: синтаксис и семантика

В синтаксисе Python новые блоки с отступом следуют за символом двоеточия (:). Есть несколько мест, где будет отображаться новый блок с отступом. Когда вы начинаете писать код на Python, наиболее распространенными являются места после if ключевого слова и после for ключевого слова:

>>> for x in [1, 2, 3]:
...     y = x + 1
...     print(x, y)
...
1 2
2 3
3 4

После for инструкции следует тело цикла for, который состоит из двух строк с отступом, следующих непосредственно за двоеточием.

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

  1. y = x + 1
  2. print(x, y)

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

Чтобы ничего не делать внутри пакета, вы можете использовать специальную инструкцию Python pass. Эта инструкция состоит только из одного ключевого слова pass. Хотя вы можете использовать pass во многих местах Python, это не всегда полезно:

>>> if 1 + 1 == 2:
...     print("math is ok")
...     pass
...     print("but this is to be expected")
...
math is ok
but this is to be expected

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

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

>>> if 1 + 1 == 3:
...
  File "<stdin>", line 2

    ^
IndentationError: expected an indented block

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

Чтобы исправить это, вы можете использовать pass:

>>> if 1 + 1 == 3:
...     pass
...

Теперь, благодаря pass, ваш оператор if имеет допустимый синтаксис Python.

Временное использование pass

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

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

Будущий код

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

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

def get_and_save_middle(data, fname):
    middle = data[len(data)//3:2*len(data)//3]
    save_to_file(middle, fname)
    return middle

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

Можно закомментировать вызов как save_to_file(), но тогда вам придется не забыть раскомментировать вызов после подтверждения того, что get_and_save_middle() работает нормально. Вместо этого вы можете быстро реализовать save_to_file() с помощью инструкции pass:

def save_to_file(data, fname):
    pass # TODO: fill this later

Эта функция ничего не делает, но позволяет протестировать get_and_save_middle() без ошибок.

Другой вариант использования pass - это когда вы пишете сложную структуру управления потоком и вам нужен заполнитель для будущего кода. Например, при реализации задачи fizz-buzz challenge с помощью оператора по модулю полезно сначала понять структуру кода:

if idx % 15 == 0:
    pass # Fizz-Buzz
elif idx % 3 == 0:
    pass # Fizz
elif idx % 5 == 0:
    pass # Buzz
else:
    pass # Idx

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

Например, в данном случае важно понимать, что первое утверждение if должно проверять делимость на 15, поскольку любое число, которое делится на 15, также делится на 5 и 3. Это структурное понимание полезно независимо от деталей конкретного результата.

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

def fizz_buzz(idx):
    if idx % 15 == 0:
        print("fizz-buzz")
    elif idx % 3 == 0:
        print("fizz")
    elif idx % 5 == 0:
        print("buzz")
    else:
        print(idx)

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

Альтернативой было бы написать функцию, которая возвращает строку, а затем выполнить цикл в другом месте:

def fizz_buzz(idx):
    if idx % 15 == 0:
        return "fizz-buzz"
    elif idx % 3 == 0:
        return "fizz"
    elif idx % 5 == 0:
        return "buzz"
    else:
        return str(idx)

Эта функция расширяет возможности печати и упрощает тестирование.

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

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

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

class Candy:
    pass

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

Закомментированный код

Когда вы закомментируете код, можно сделать синтаксис недействительным, удалив весь код в блоке. Если у вас есть условие if ... else, то, возможно, было бы полезно закомментировать одну из ветвей:

def process(context, input_value):
    if input_value is not None:
        expensive_computation(context, input_value)
    else:
        logging.info("skipping expensive: %s", input_value)

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

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

Однако это неверный код:

def process(context, input_value):
    if input_value is not None:
        # Temporarily commented out the expensive computation
        # expensive_computation(context, input_value)
    else:
        logging.info("skipping expensive: %s", input_value)

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

В этом случае добавление инструкции pass делает код допустимым:

def process(context, input_value):
    if input_value is not None:
        # Temporarily commented out the expensive computation
        # expensive_computation(context, input_value)
        # Added pass to make code valid
        pass
    else:
        logging.info("skipping expensive: %s", input_value)

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

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

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

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

def write_to_file(fname, data):
    with open(fname, "w") as fpout:
        fpout.write(data)

get_data(source).add_callback(write_to_file, "results.dat")

Этот код вызывает get_data() и присоединяет обратный вызов к результату.

Возможно, было бы полезно провести тестовый запуск, который удалит данные, чтобы убедиться, что источник указан правильно. Однако это неверный код на Python:

def write_to_file(fname, data):
    # Discard data for now
    # with open(fname, "w") as fpout:
    #     fpout.write(data)

get_data(source).add_callback(write_to_file, "results.dat")

Поскольку в блоке функции нет инструкций, Python не может проанализировать этот код.

Еще раз, pass может помочь вам:

def write_to_file(fname, data):
    # Discard data for now
    # with open(fname, "w") as fpout:
    #     fpout.write(data)
    pass

get_data(source).add_callback(write_to_file, "results.dat")

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

Маркеры для отладчиков

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

Когда тестовый запуск часто запускает точку останова, например, в цикле, может быть много случаев, когда состояние программы неинтересно. Чтобы решить эту проблему, многие отладчики также допускают условную точку останова, которая срабатывает только при выполнении условия true. Например, вы можете установить точку останова в цикле for, который запускается только в том случае, если переменная равна None, чтобы понять, почему этот случай обрабатывается неправильно.

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

Хотя отладчик, возможно, и не способен проверять наличие палиндромов, Python может сделать это с минимальными усилиями. Вы можете воспользоваться этой функциональностью, создав инструкцию "ничего не делать" if и установив точку останова в строке pass:

for line in filep:
    if line == line[::-1]:
        pass # Set breakpoint here
    process(line)

Проверяя наличие палиндромов с помощью line == line[::-1], вы получаете строку, которая выполняется только при выполнении условия true.

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

Пустые функции

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

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

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

>>> def ignore_arguments(record, status):
...
  File "<stdin>", line 2

    ^
IndentationError: expected an indented block

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

>>> def ignore_arguments(record, status):
...     pass
...

Теперь, когда у функции есть оператор, даже тот, который ничего не делает, это допустимый синтаксис Python.

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

class DiscardingIO:
    def write(self, data):
        pass

Экземпляры этого класса поддерживают метод .write(), но немедленно удаляют все данные.

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

Пустые классы

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

>>> empty={}
>>> try:
...     empty["some key"]
... except LookupError as exc:
...     print("got exception", repr(exc))
...
got exception KeyError('some key')
>>> issubclass(KeyError, LookupError)
True

Исключение KeyError перехватывается, даже если в инструкции except указано LookupError. Это происходит потому, что KeyError является подклассом LookupError.

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

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

  • Не менее восьми символов
  • Хотя бы одно число
  • По крайней мере, один специальный символ, такой как вопросительный знак (?), восклицательный знак (!), или точка (.).

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

Для получения дополнительной информации ознакомьтесь с рекомендациями Национального института стандартов и технологий (NIST) и исследованиями, на которых они основаны.

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

# password_checker.py
class InvalidPasswordError(ValueError):
    pass

class ShortPasswordError(InvalidPasswordError):
    pass

class NoNumbersInPasswordError(InvalidPasswordError):
    pass

class NoSpecialInPasswordError(InvalidPasswordError):
    pass

def check_password(password):
    if len(password) < 8:
        raise ShortPasswordError(password)
    for n in "0123456789":
        if n in password:
            break
    else:
        raise NoNumbersInPasswordError(password)
    for s in "?!.":
        if s in password:
            break
    else:
        raise NoSpecialInPasswordError(password)

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

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

>>> from password_checker import check_password
>>> def friendly_check(password):
...     try:
...         check_password(password)
...     except InvalidPasswordError as exc:
...         print("Invalid password", repr(exc))
...
>>> friendly_check("hello")
Invalid password ShortPasswordError('hello')
>>> friendly_check("helloworld")
Invalid password NoNumbersInPasswordError('helloworld')
>>> friendly_check("helloworld1")
Invalid password NoSpecialInPasswordError('helloworld1')

В этом случае friendly_check() перехватывает только InvalidPasswordError, поскольку другие ValueError исключения, вероятно, являются ошибками в самой программе проверки. Он выводит имя и значение исключения, которые показывают правило, которое не было соблюдено.

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

def get_username_and_password(credentials):
    try:
        name, password = credentials.split(":", 1)
        check_password(password)
    except ValueError:
        return get_default_credentials()
    else:
        return name, value

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

Из-за этих различных вариантов использования для check_password() требуются все четыре исключения:

  1. InvalidPasswordError
  2. ShortPasswordError
  3. NoNumbersPasswordError
  4. NoSpecialPasswordError

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

Несмотря на необходимость использования четырех разных классов, ни один из них не обладает каким-либо поведением. Оператор pass позволяет быстро определить все четыре класса.

Методы маркирования

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

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

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

Ничто не должно создавать экземпляр класса Origin напрямую. Каждый запрос должен поступать либо из источника LoggedIn, либо из источника NotLoggedIn. Вот минималистичная реализация:

import abc

class Origin(abc.ABC):
    @abc.abstractmethod
    def description(self):
        # This method will never be called
        pass

class NotLoggedIn(Origin):
    def description(self):
        return "unauthenticated connection"

class LoggedIn(Origin):
    def description(self):
        return "authenticated connection"

Хотя реальный класс Origin был бы более сложным, в этом примере показаны некоторые основы. Origin.description() никогда не будет вызван, поскольку все подклассы должны переопределять его.

Поскольку у Origin есть abstractmethod, он не может быть создан:

>>> Origin()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Origin with abstract...
>>> logged_in.description()
'authenticated connection'
>>> not_logged_in.description()
'unauthenticated connection'

Классы с методами abstractmethod не могут быть созданы. Это означает, что любой объект, имеющий Origin в качестве суперкласса, будет экземпляром класса, который переопределяет description(). Из-за этого тело в Origin.description() не имеет значения, но метод должен существовать, чтобы указывать, что все подклассы должны создавать его экземпляры.

Поскольку тела методов не могут быть пустыми, вы должны поместить что-то в Origin.description(). Еще раз повторю, что оператор "ничего не делать" pass - это хороший вариант, позволяющий сделать очевидным, что вы включили эту строку только по синтаксическим соображениям.

Более современный способ указать, какие методы необходимы, - это использовать Protocol,, который доступен в стандартной библиотеке в Python 3.8 и выше. В более старых версиях Python он доступен с typing_extensions обратными портами.

A Protocol отличается от абстрактного базового класса тем, что он явно не связан с конкретным классом. Вместо этого он полагается на соответствие типу, чтобы связать его во время проверки типа с mypy.

Методы в Protocol никогда не вызываются. Они служат только для обозначения типов необходимых методов:

>>> from typing_extensions import Protocol
>>> class StringReader(Protocol):
...     def read(self, int) -> str:
...         pass

Демонстрация того, как использовать Protocol, подобную этой, в mypy не имеет отношения к pass инструкции. Но важно видеть, что в теле метода есть только pass оператор.

Есть и другие примеры использования таких маркеров за пределами языка Python и стандартных библиотек. Например, они используются в пакете zope.interface для указания методов интерфейса и в automat для указания входных данных для конечного автомата.

Во всех этих случаях классы должны иметь методы, но никогда не вызывать их. Из-за этого тело не имеет значения. Но поскольку тело не может быть пустым, вы можете использовать оператор pass для добавления тела.

Альтернативы pass

Оператор pass - это не единственный способ ничего не делать в вашем коде. Как вы увидите позже, он даже не самый короткий. Это даже не всегда самый лучший или наиболее питонический подход.

Любое выражение в Python является допустимым оператором, и каждая константа является допустимым выражением. Таким образом, все следующие выражения ничего не дают:

  • None
  • True
  • 0
  • "hello I do nothing"

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

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

Строки документации

Есть одно важное исключение из идиомы использования pass в качестве инструкции "ничего не делать". В классы, функции и методы, с помощью константного выражения строка вызовет выражение будет использоваться в качестве объекта .__doc__ атрибут.

Атрибут .__doc__ используется help() в интерактивном интерпретаторе и различными генераторами документации, многими IDE, и другие разработчики, читающие код. Некоторые стили кода требуют наличия этого в каждом классе, функции или методе.

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

class StringReader(Protocol):
      def read(self, length: int) -> str:
          """
          Read a string
          """

class Origin(abc.ABC):
    @abc.abstractmethod
    def description(self):
        """
        Human-readable description of origin
        """

class TooManyOpenParens(ParseError):
    """
    Not all open parentheses were closed
    """

class DiscardingIO:
    def write(self, data):
        """
        Ignore data
        """

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

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

Одно из технических преимуществ docstrings, особенно для тех функций или методов, которые никогда не выполняются, заключается в том, что они не помечаются как “незащищенные” при проверке покрытия тестами.

Многоточие

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

>>> ...
Ellipsis
>>> x = ...
>>> type(x), x
(<class 'ellipsis'>, Ellipsis)

Одноэлементный объект Ellipsis из встроенного класса ellipsis является реальным объектом, который создается выражением ....

Первоначально Ellipsis использовался для создания многомерных срезов. Однако теперь это также рекомендуемый синтаксис для заполнения набора в stub-файле:

# In a `.pyi` file:
def add(a: int, b: int)-> int:
    ...

Эта функция не только ничего не делает, но и находится в файле, который интерпретатор Python никогда не вычисляет.

Выдает ошибку

В случаях, когда функции или методы пусты, потому что они никогда не выполняются, иногда лучшим телом для них является raise NotImplementedError("this should never happen"). Хотя технически это действительно что-то делает, это все еще действительная альтернатива оператору pass.

Постоянное использование pass

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

Использование pass при перехвате исключений

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

Если вы хотите убедиться, что файл не существует, вы можете использовать os.remove(). Эта функция выдаст сообщение об ошибке, если файла там нет. Однако в данном случае отсутствие файла - это именно то, что вам нужно, поэтому ошибка не требуется.

Вот функция, которая удаляет файл и не завершается ошибкой, если файл не существует:

import os

def ensure_nonexistence(fname):
    try:
        os.remove(fname)
    except FileNotFoundError:
        pass

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

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

Обратите внимание, что оператор pass часто заменяется оператором для ведения журнала. Однако это не требуется, если ошибка ожидаема и хорошо понятна.

В этом случае вы также можете использовать контекстный менеджер contextlib.suppress() для устранения ошибки. Однако, если вам нужно обработать некоторые ошибки, игнорируя другие, то проще создать пустой класс except, в котором нет ничего, кроме инструкции pass.

Например, если вы хотите, чтобы ensure_nonexistence() имел дело как с каталогами, так и с файлами, то вы могли бы использовать этот подход:

import os
import shutil

def ensure_nonexistence(fname):
    try:
       os.remove(fname)
    except FileNotFoundError:
       pass
    except IsADirectoryError:
       shutil.rmtree(fname)

Здесь вы игнорируете FileNotFoundError при повторном запуске IsADirectoryError.

В этом примере порядок инструкций except не имеет значения, поскольку FileNotFoundError и IsADirectoryError являются родственными, и оба наследуются от OSError. Если бы существовал случай, который обрабатывал бы общий OSError, возможно, регистрируя и игнорируя его, тогда порядок имел бы значение. В этом случае FileNotFoundError и его оператор pass должны были бы предшествовать OSError.

Используя pass в цепочках if ... elif

Когда вы используете длинные цепочки if ... elif, иногда вам не нужно ничего делать в одном случае. Однако вы не можете пропустить это elif, потому что выполнение продолжилось бы до другого условия.

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

  • Если число делится на 20, то выведите "twist".
  • В противном случае, если число делится на 15, то ничего не выводите.
  • В противном случае, если число делится на 5, выведите "fizz".
  • В противном случае, если число делится на 3, выведите "buzz".
  • В противном случае выведите число.

Интервьюер считает, что этот новый поворот сделает ответы более интересными.

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

for x in range(100):
    if x % 20 == 0:
       print("twist")
    elif x % 15 == 0:
       pass
    elif x % 5 == 0:
       print("fizz")
    elif x % 3 == 0:
       print("buzz")
    else:
       print(x)

Цепочка if ... elif отражает логику перехода к следующему варианту, только если предыдущий не сработал.

В этом примере, если бы вы полностью удалили предложение if x % 15, то изменили бы поведение. Вместо того, чтобы ничего не выводить для чисел, кратных 15, вы бы напечатали "fizz". Это условие имеет важное значение, даже если в этом случае ничего нельзя сделать.

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

Заключение

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

В этом уроке вы узнали:

  • Что такое оператор Python pass и почему он полезен
  • Как использовать инструкцию Python pass в производственном коде
  • Как использовать инструкцию Python pass в качестве вспомогательного средства при разработке кода
  • Каковы альтернативы pass и когда их следует использовать

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

<статус завершения: article-slug="python-pass" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-pass/bookmark/" данные-api-статья-статус завершения-url="/api/v1/articles/python-pass/completion_status/"> <кнопка поделиться bluesky-text="Интересная статья #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей на Python:%0A%0ЭТот оператор передачи: Как ничего не делать в Python" email-subject="Статья о Python для вас" twitter-text="Интересная статья о Python от @realpython:" url="https://realpython.com/python-pass/" url-title="Инструкция pass: Как ничего не делать в Python">

Back to Top