被接受的答案(我认为是不正确的)表明你不能这样做,而应该使用以下方法;
class Comedian:
def __init__(self, *jokes):
self.jokes = jokes
def __enter__(self):
jokes = self.jokes
return self
虽然通常情况下您可能会这样做,但这并不总是最佳解决方案,甚至不是一个解决方案,而且绝对不是唯一的解决方案!
我假设您想要做的是与以下类似的事情;
funny_object = Comedian()
with funny_object('this is a joke') as humor:
humor.say_something_funny()
如果情况确实如此,并且事情并不比这更复杂,那么您只需要执行以下操作:
class Comedian:
def __enter__(self):
jokes = self.jokes
return self
def __call__(self, *jokes):
self.jokes = jokes
return self
那样您仍然可以用任何想要的参数初始化对象,并像通常一样对对象执行任何其他操作,但当您将对象用作上下文管理器时,您首先调用它的 call 函数并为上下文管理器设置一些参数。
这里重要的是要准确理解Python中上下文管理器的工作原理。
在Python中,上下文管理器是定义了一个 enter 方法的任何对象。当您执行以下操作时,将自动调用此方法;
with object as alias:
alias.do_stuff()
..
注意对象后面没有一对“()”,这是一个隐式函数调用,并且它不需要任何参数。
你可能已经从enter传递参数的想法中获得了灵感;
with open(filename) as file:
"do stuff with file..
但这与覆盖 enter 不同,因为 "open" 不是一个对象,而是一个函数。
一个好的练习是打开交互式Python控制台并输入 "open" + [ENTER]
>>> open
<built-in function open>
"
“open”不是上下文管理对象,而是函数。它根本没有enter方法,而是按以下方式定义的;
"
@contextmanager
def open(..):
...
你可以以相同的方式定义自己的上下文管理器函数,甚至可以覆盖“open”的定义。
不过,在我看来,如果您需要创建一个对象,然后稍后使用它作为具有参数的上下文管理器(..我所做的),最好的方法是给对象添加一个返回定义了enter方法的临时对象的方法。示例如下:
class Comedian:
def context(audience):
class Roaster:
context = audience
def __enter__(self):
audience = self.__class__.context
return Roaster(audience)
funny_thing = Comedian()
with funny_thing.context('young people') as roaster:
roaster.roast('old people')
该示例中调用链的顺序是:
Comedian.__init__()
->
Comedian.context(args)
->
Roaster.__enter__()
。我觉得这个答案在众多答案中缺失,所以我添加了它。
编辑:根据 @MarkLoyman 的指示,将
Comedian.__call__
中的"
return self
"添加进去。
for
循环中使用,例如,每次迭代都不需要实例化一个新对象。 - Maor Refaeli