Это возможно сделать, используя возможности самоанализа Python, но вы должны знать, что это не то, для чего был создан блок контекста with
.
Я согласен, что это полезная конструкция синтаксиса, которая может быть «отклонена», чтобы делать то, что вы хотите: аннотировать объекты, созданные внутри блока кода, в «реестре».
Прежде чем показать, как это сделать с помощью диспетчера контекста, подумайте, не будет ли вам достаточно тела класса. Использование тела класса таким способом также отличается от его основного предназначения, но у вас есть «реестр» бесплатно:
from somewhere import Test, MyContext
class ctx:
mytest = Test()
vars = ctx.__dict__.values()
Чтобы сделать это с помощью диспетчера контекста, вы должны проверить локальные переменные в начало и конец блока with
. Хотя это не сложно сделать, оно не будет охватывать все экземпляры Test
, созданные - потому что если код подобен этому:
mytests = []
with Mycontext as ctx:
mytests.append(Test())
Никакая новая переменная не создается - поэтому код, отслеживающий локальные переменные, будет ничего не найти. Код может быть написан для рекурсивного просмотра переменных с контейнерами, таких как словари и списки, но затем mytest()
экземпляры могут быть добавлены в контейнер, на который ссылаются как на глобальную переменную, или переменную в другом модуле.
It Оказывается, что надежным способом отслеживания экземпляров Test
было бы использование самого класса Test
для аннотирования новых экземпляров в реестре. Это гораздо проще и менее зависит от уловок «локальной переменной».
Код для этого выглядит примерно так:
class Test(object):
pass
class MyContext(object):
def __init(self, *args):
self.vars = []
self.track = args
self.original_new = {}
def patch(self, cls_to_patch):
cls_new = getattr(cls_to_patch, "__new__")
if "__new__" in cls.__dict__:
self.original_new[cls_to_patch] = cls_new
def patched_new(cls, *args, **kwargs):
instance = cls_new(*args, **kwags)
self.vars.append(instance)
return instance
cls_to_patch.__new__ = patched_new
def restore(self, cls):
if cls in self.original_new:
# class had a very own __new_ prior to patching
cls.__new__ = self.original_new[cls]
else:
# just remove the wrapped new method, restores access to superclass `__new__`
del cls.__new__
def __enter__(self):
for cls in self.track:
self.patch(cls)
return self
def __exit(self, ....):
for cls in self.track:
self.restore(cls)
...
from somewhere import Test, MyContext
with MyContext(Test) as ctx:
mytest = Test()