将参数传递给__enter__

Ram*_*amy 37 python with-statement

只需从本文中了解语句即可

问题是,我可以通过论证__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
Run Code Online (Sandbox Code Playgroud)

我想将filename和param_dict作为参数传递给__enter__.那可能吗?

S.L*_*ott 44

不,你不能.你传递参数__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
Run Code Online (Sandbox Code Playgroud)

  • 我糊涂了.因为你只是在`__init__`中传递`你是否建议在`__enter__`函数中传递给`__init__`的'args`? (7认同)
  • 为什么这是公认的答案?我似乎不是唯一一个认为这是最糟糕答案的人。“不,你不能,你将参数传递给 __init__..” 根本不是真的,当然通过 init 传递参数是一种方法,但绝不是唯一的方法,而且也不总是这样最有意义..如果您想像OP想要的那样将args传递给with .. as ..行的上下文管理器,那么您绝对可以,这通常是最有意义的,具体取决于应用程序。(查看所有其他答案) (2认同)

小智 25

是的,您可以通过添加更多代码来获得效果.


    #!/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
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是我的正确答案,因为您不需要在with语句中创建变量,但可以使用容易创建的对象(例如锁)并将变量传递给with语句.很棒的答案! (4认同)
  • 很好的答案!这应该是可以接受的,因为它可以在“for”循环中使用,而无需每次迭代都实例化一个新对象 (2认同)

Vin*_*ent 11

接受的答案(我认为这是不正确的)指出您不能,而应该这样做;

class Comedian:
    def __init__(self, *jokes):
        self.jokes = jokes
    def __enter__(self):
        jokes = self.jokes
        #say some funny jokes
        return self
Run Code Online (Sandbox Code Playgroud)

..虽然这通常是您会做的事情,但它并不总是最好的解决方案,甚至不是解决方案,而且绝对不是唯一的解决方案!..

我假设你想要的是能够做类似的事情;

funny_object = Comedian()
with funny_object('this is a joke') as humor:
    humor.say_something_funny()
Run Code Online (Sandbox Code Playgroud)

如果是这种情况,并且没有比这更复杂的情况,那么您可以这样做;

class Comedian:
    def __enter__(self):
        jokes = self.jokes
        #say some funny jokes
        return self
    def __call__(self, *jokes):
        self.jokes = jokes
Run Code Online (Sandbox Code Playgroud)

..那样你仍然可以用你想要的任何参数初始化对象,并像往常一样对对象做任何其他的事情,但是当你去使用对象作为上下文管理器时,你首先调用它的调用函数并设置为上下文管理器提供一些参数。

这里重要的是准确理解上下文管理器在 Python 中的工作方式。

在 Python 中,上下文管理器是定义enter方法的任何对象。执行此操作时会自动调用此方法;

with object as alias:
    alias.do_stuff()
    ..
Run Code Online (Sandbox Code Playgroud)

..注意 object 后面没有几个“()”,它是一个隐式函数调用,它不带任何参数。

你可能已经得到了传递参数给的念头进入的;

with open(filename) as file:
    "do stuff with file..
Run Code Online (Sandbox Code Playgroud)

但这与覆盖enter不同,因为“open”不是一个对象,而是一个函数。

一个很好的练习是打开一个交互式 python 控制台并输入“open”+[ENTER]

>>> open
<built-in function open>
Run Code Online (Sandbox Code Playgroud)

“open”不是上下文管理器对象,而是函数。它根本没有enter方法,而是通过以下方式定义;

@contextmanager
def open(..):
    ...
Run Code Online (Sandbox Code Playgroud)

..你可以用同样的方式定义你自己的上下文管理器函数,你甚至可以覆盖“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')
Run Code Online (Sandbox Code Playgroud)

本例中调用链的顺序是;喜剧演员。init () -> Comedian.context(args) -> Roaster。进入()

我觉得很多人都缺少这个答案,所以我添加了它。

  • 对 Python 基本部分的精彩解释使所有这些都清晰可见。 (2认同)

Jos*_*uis 7

您可以使用 contextmanager 装饰器来传递参数:

https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager

from contextlib import contextmanager

@contextmanager
def clippy_runner(*args):
    yield
Run Code Online (Sandbox Code Playgroud)

恕我直言,我发现使用contextmanager你可以提供参数,但你不能将它们提供给__enter__

  • 我支持这个。我有一些仅在上下文中相关的设置。将它们传递给 `__init__` 是愚蠢的。 (3认同)