Значение выражения "с" без слова "как"

Я знаком с использованием python with инструкцией в качестве средства обеспечения финализации объекта в случае генерирования исключения. Обычно это выглядит

with file.open('myfile.txt') as f:
    do stuff...

которая является короткой для

f = file.open('myfile.txt'):
try:
    do stuff...
finally:
    f.close()

или что-то другое, что может предложить класс.

Недавно я натолкнулся на часть кода, касающуюся OpenGL, который представил это:

with self.shader:
    (Many OpenGL commands)

Следует отметить, что отсутствие какого - либо в as ключевого слова. Означает ли это, что методы класса __enter__ и __exit__ еще должны быть вызваны, но что объект никогда явно не используется в блоке (т.е. Он работает через глобальные или неявные ссылки)? Или есть какой-то другой смысл, который ускользает от меня?

Ответ 1

Диспетчер контекста может необязательно возвращать объект, который должен быть назначен идентификатору, названному as. И это объект, возвращаемый методом __enter__ который присваивается as, не обязательно сам менеджер контекста.

Использование в as <identifier> помогает при создании нового объекта, например, вызов open(), но не все контекстные менеджеры создаются только для контекста. Они могут быть повторно использованы и уже созданы, например.

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

with db_connection:
    # do something to the database

Здесь не нужно создавать новые объекты, контекст вводится с помощью db_connection.__enter__() и снова db_connection.__exit__() с db_connection.__exit__(), но мы уже имеем ссылку на объект соединения.

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

with db_connection as cursor:
    # use cursor to make changes to the database

db_connection все еще не db_connection здесь, оно уже существовало раньше, и у нас уже есть ссылка на него. Но независимо от db_connection.__enter__() что db_connection.__enter__() теперь назначается cursor и может использоваться оттуда.

Это то, что происходит с файловыми объектами; open() возвращает объект файла и fileobject.__enter__() возвращает сам файл объект, так что вы можете использовать open() вызов в with выражением и присвоить ссылку на вновь созданный объект в одном шаге, а не два. Без этого небольшого трюка вам придется использовать:

f = open('myfile.txt')
with f:
    # use 'f' in the block

Применение всего этого к вашему примеру шейдера; у вас уже есть ссылка на self.shader. Вполне вероятно, что self.shader.__enter__() снова возвращает ссылку на self.shader, но поскольку у вас уже есть идеальная справочная система, зачем создавать для self.shader новую локальную?