将参数传递给__enter__

时间:2011-02-24 19:34:54

标签: python with-statement

只需了解陈述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__。这可能吗?

6 个答案:

答案 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

上下文管理器(基本上)执行三件事:

  1. 它在代码块之前运行一些代码。
  2. 它在代码块后运行一些代码。
  3. (可选)它可抑制代码块内引发的异常。