只需了解陈述especially from this article
问题是,我可以将参数传递给__enter__
吗?
我有这样的代码:
class clippy_runner:
def __enter__(self):
self.engine = ExcelConnection(filename = "clippytest\Test.xlsx")
self.db = SQLConnection(param_dict = DATASOURCES[STAGE_RELATIONAL])
self.engine.connect()
self.db.connect()
return self
我想将filename和param_dict作为参数传递给__enter__
。这可能吗?
答案 0 :(得分:42)
没有。你不能。您将参数传递给__init__()
。
class ClippyRunner:
def __init__(self, *args):
self._args = args
def __enter__(self):
# Do something with args
print(self._args)
with ClippyRunner(args) as something:
# work with "something"
pass
答案 1 :(得分:22)
是的,您可以通过添加更多代码来获得效果。
#!/usr/bin/env python
class Clippy_Runner( dict ):
def __init__( self ):
pass
def __call__( self, **kwargs ):
self.update( kwargs )
return self
def __enter__( self ):
return self
def __exit__( self, exc_type, exc_val, exc_tb ):
self.clear()
clippy_runner = Clippy_Runner()
print clippy_runner.get('verbose') # Outputs None
with clippy_runner(verbose=True):
print clippy_runner.get('verbose') # Outputs True
print clippy_runner.get('verbose') # Outputs None
答案 2 :(得分:5)
被接受的答案(我认为这是不正确的)指出您不能,而应该这样做;
class Comedian:
def __init__(self, *jokes):
self.jokes = jokes
def __enter__(self):
jokes = self.jokes
#say some funny 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
#say some funny jokes
return self
def __call__(self, *jokes):
self.jokes = jokes
..那样,您仍然可以使用所需的任何参数来初始化对象,并像平常一样对对象进行任何其他处理,但是当您将对象用作上下文管理器时,请首先调用其< strong> call 函数并为上下文管理器设置一些参数。
这里重要的是准确了解上下文管理器在Python中的工作方式。
在Python中,上下文管理器是定义 enter 方法的任何对象。完成时会自动调用此方法;
with object as alias:
alias.do_stuff()
..
..注意对象后面没有几个“()”,它是一个隐式函数调用,并且不带任何参数。
您可能已经有了将参数传递给输入的想法;
with open(filename) as file:
"do stuff with file..
但这不同于覆盖 enter ,因为“ open”不是对象,而是函数。
一个好的练习是打开一个交互式python控制台,然后键入“ open” + [ENTER]
>>> open
<built-in function open>
“打开”不是上下文管理器对象,而是功能。它根本没有 enter 方法,而是通过以下方式定义的;
@contextmanager
def open(..):
...
..您可以用相同的方式定义自己的上下文管理器功能,甚至可以覆盖“打开”的定义。
不过,IMO,如果需要创建一个对象,然后在以后将其用作带有参数的上下文管理器(..我该怎么做),最好的做法是为该对象提供一个返回定义了临时对象的方法像这样的 enter 方法;
class Comedian:
def context(audience):
class Roaster:
context = audience
def __enter__(self):
audience = self.__class__.context
# a comedian needs to know his/her audience.
return Roaster(audience)
funny_thing = Comedian()
with funny_thing.context('young people') as roaster:
roaster.roast('old people')
在此示例中,调用链的顺序为: 喜剧演员。 init ()-> Comedian.context(args)-> Roaster。输入()
我觉得很多人都找不到这个答案,所以我添加了它。
答案 3 :(得分:3)
您是不是只通过类构造函数将值传递给__init__
?
答案 4 :(得分:2)
您可以使用contextmanager装饰器传递参数:
https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager
from contextlib import contextmanager
@contextmanager
def clippy_runner(*args):
yield
恕我直言,我发现使用contextmanager
您可以提供参数,但您无法将其提供给__enter__
答案 5 :(得分:1)
我认为使用contextlib.contextmanager
(本地软件包)是个好主意。
更多详细信息,如下所示。
from contextlib import contextmanager
class Person:
def __init__(self, name):
self.name = name
def say_something(self, msg):
print(f'{self.name}: {msg}')
@staticmethod
@contextmanager
def enter(name, # <-- members of construct
para_1, options: dict # <-- Other parameter that you wanted.
):
with Person(name) as instance_person:
try:
print(para_1)
print(options)
yield instance_person
finally:
...
def __enter__(self):
print(self.name)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__')
with Person.enter('Carson', para_1=1, options=dict(key='item_1')) as carson:
carson.say_something('age=28')
print('inside')
print('outside')
输出
Carson
1
{'key': 'item_1'}
Carson: age=28
inside
__exit__
outside
from typing import Union
from contextlib import contextmanager
def main():
with ClippyRunner.enter(filename="clippytest/Test.xlsx",
param_dict='DATASOURCES[STAGE_RELATIONAL]') as clippy_runner:
clippy_runner.do_something()
class ConnectBase:
def connect(self):
print(f'{type(self).__name__} connect')
def disconnect(self):
print(f'{type(self).__name__} disconnect')
class ExcelConnection(ConnectBase):
def __init__(self, filename):
self.filename = filename
class SQLConnection(ConnectBase):
def __init__(self, param_dict):
self.param_dict = param_dict
class ClippyRunner:
def __init__(self, engine: Union[ExcelConnection], db: Union[SQLConnection]):
self.engine = engine
self.db = db
def do_something(self):
print('do something...')
@staticmethod
@contextmanager
def enter(filename, param_dict):
with ClippyRunner(ExcelConnection(filename),
SQLConnection(param_dict)) as cr:
try:
cr.engine.connect()
cr.db.connect()
yield cr
except:
cr.release() # disconnect
finally:
...
def __enter__(self):
return self
def release(self):
self.engine.disconnect()
self.db.disconnect()
def __exit__(self, exc_type, exc_val, exc_tb):
self.release()
if __name__ == '__main__':
main()
输出
ExcelConnection connect
SQLConnection connect
do something...
ExcelConnection disconnect
SQLConnection disconnect
contextmanager
上下文管理器(基本上)执行三件事: