【问题标题】:__enter__() takes exactly 3 arguments (1 given)__enter__() 正好接受 3 个参数(1 个给定)
【发布时间】:2015-07-03 02:59:30
【问题描述】:

我写了一个这样的类:

class FooBar(object):
    # some methods
    # ...
    def __enter__(self, param1, param2):
        # do something here ...
        pass

我尝试像这样使用我的类(从模块 mymod 导入):

with (mymod.FooBar("hello", 123)) as x:
    # do something here with instance of mymod.FooBar called x ...
    pass

当上面的代码块被执行时,我得到了错误:

__enter__() takes exactly 3 arguments (1 given)

我做错了什么?

【问题讨论】:

    标签: python


    【解决方案1】:

    __enter__ 方法从不提供任何参数,因此在 self 之外,您的签名不应该有任何其他参数。

    您应该将这些参数移至 __init__ 方法:

    class FooBar(object):
        def __init__(self, param1, param2):
            # do something here ...
    
        def __enter__(self):
            # something else, perhaps return self
    

    创建FooBar() 的实例是一个单独的步骤withmymod.FooBar("hello", 123) 表达式的结果上调用__enter__,表达式本身不会转换为__enter__ 调用。

    如果是这样,你也不能这样使用它,但你可以:

    cm = mymod.FooBar("hello", 123)
    with cm as x:
        # do something here with x, which is the return value of cm.__enter__()
    

    请注意,x 被分配了任何 cm.__enter__() 返回的值;你可以从__enter__返回self,或者你可以返回完全不同的东西。

    预期的方法 __enter____exit__ 记录在 Python 数据模型文档的 With Statement Context Managers section 中:

    object.__enter__(self)

    输入与该对象相关的运行时上下文。 with 语句会将此方法的返回值绑定到该语句的 as 子句中指定的目标(如果有)。

    以及内置类型文档的Content Manager Types section

    contextmanager.__enter__()

    进入运行时上下文并返回此对象或与运行时上下文相关的另一个对象。此方法返回的值绑定到使用此上下文管理器的with 语句的as 子句中的标识符。

    返回自身的上下文管理器的一个示例是文件对象。文件对象从__enter__() 返回自身,以允许open() 用作with 语句中的上下文表达式。

    如果您对确切的交互感兴趣,请参阅原始提案:PEP 343 -- The "with" Statement;从规范部分你可以看到 with EXPR as VAR: BLOCK 语句在幕后做了什么:

    mgr = (EXPR)
    exit = type(mgr).__exit__  # Not calling it yet
    value = type(mgr).__enter__(mgr)
    exc = True
    try:
        try:
            VAR = value  # Only if "as VAR" is present
            BLOCK
        except:
            # The exceptional case is handled here
            exc = False
            if not exit(mgr, *sys.exc_info()):
                raise
            # The exception is swallowed if exit() returns true
    finally:
        # The normal and non-local-goto cases are handled here
        if exc:
            exit(mgr, None, None, None)
    

    注意mgr = (EXPR) 部分;在你的情况下,mymod.FooBar("hello", 123) 就是那个部分。另请注意,(EXPR)__enter____exit__ 在此处不受 try..except 的“保护”,表达式中或进入或退出时引发的异常由上下文管理器处理!

    【讨论】:

    • 但是我需要将参数传递给ctor(构造函数),以便创建一个有效的对象
    • @HomunculusReticulli:是的,但__enter__不是构造函数
    • 鉴于你的分数很高,我可以大胆地问你是否确定是这种情况。我一直认为(在某处读过?) with 语句 ** 实例化 ** 对象(换句话说,它只是 ctor 的语法糖),同样 exit 类似于 dtor。如果是这种情况,那么我的大部分代码似乎都是错误的。
    • 你仍然可以写with mymod.FooBar("hello", 123),但你必须在__init__方法中使用参数,而不是在__enter__方法中。
    • @HomunculusReticulli:我绝对,肯定,100% 肯定,是的。
    【解决方案2】:

    考虑这段代码:

    f = open("myfile.txt")
    with f:
        x = f.read()
    

    这个基本一样

    with open("myfile.txt") as f:
        x = f.read()
    

    注意对象初始化和上下文是如何分开的。 with 上下文负责在适当的时候调用对象的 enter() 和 exit() 方法。

    【讨论】:

    • +1 啊,不知何故,我似乎明白了,现在你展示了这两个块在语义上是如何等效的......然而我的信心被严重动摇了:(似乎我不知道和我想的一样多 Python ...
    • @HomunculusReticulli:它们不是相当等价的,但对于文件对象来说并不重要。在第一个示例中,您可以在两者之间执行f.close(),然后在使用with f: 时会出现异常。但是f.__enter__()返回f所以除了这两个sn-ps基本上是等价的。
    【解决方案3】:

    mymod.FooBar() 正在调用FooBar 类的__init__ 方法并将该类的实例作为对象返回。

    您的__enter__ 方法应该只接受self

    【讨论】:

      猜你喜欢
      • 2015-06-17
      • 2018-04-12
      • 1970-01-01
      • 1970-01-01
      • 2018-09-12
      • 2014-11-24
      • 2012-06-20
      • 2019-04-05
      • 1970-01-01
      相关资源
      最近更新 更多