【问题标题】:Is it possible to use with statements inline?是否可以使用内联语句?
【发布时间】:2019-08-01 18:19:11
【问题描述】:

例如,这里的代码不是内联的:

with x:
    do(stuff)

我可以像这样在一行中做到这一点:

with x: do(stuff)

但是我想知道是否有办法做这样的事情(内联):

a = [(lambda x: with x: do(stuff))(x) for x in someList]

编辑:

更详细地说,假设我有一个对象列表,每个对象都有自己的上下文。这意味着do(stuff) 在使用with x 时会表现不同,上面的语句将遍历该列表中的每个对象,输入它们的上下文,运行do(stuff),然后继续下一个。

由于我尝试过这样做,但它不起作用,我想知道是否有任何方法可以做到这一点,以及如何做到这一点。

上述代码的扩展版本如下所示(并且有效):

a = []
for x in someList:
    with x: a.append( do(stuff) )

【问题讨论】:

  • 不清楚你想在这里做什么,但可能你需要使用map
  • @DavidZemens 添加了更多信息
  • 最好能提供xdo 的示例。
  • @DavidZemens 因为x.__enter__() 改变了环境,从而改变了do_stuff() 返回的内容。或者 OP 将其误写为 with x as y: do_stuff(y) 之类的。

标签: python python-3.x inline


【解决方案1】:

好吧,既然您对解决方法没意见,那么more-itertools.with_iter 可能是您最好的选择。

注意这是一个 3rd 方包,所以如果你还没有安装它,你需要安装它

from more_itertools import with_iter

a = [do(stuff) for _ in with_iter(x)]
#your example lacks what do and x really are/do so this comprehension is inferred

如果您不想使用第 3 方包,那么 with_iter 所做的就是:

def with_iter(context_manager):
    with context_manager as iterable:
        yield from iterable

所以这很容易自己实现。

【讨论】:

    【解决方案2】:

    特别是使用 lambda 表达式,这是不可能的(与 lambdas 的部分交易是您不能在其中分配变量)。一种解决方法是提前构造一个适当的函数,然后让 lambda 调用该适当的函数:

    def _f(x):
        with x as y: 
           return do(stuff)
    
    a = [_f(x) for x in someList]
    

    我无法让with: 在 lambda 中工作,不幸的是您也无法手动执行打开/关闭例程 - 我怀疑出于同样的原因(您无法在 lambda 中分配变量)。


    但是,使用 lambdas 和 python 3.8 中引入的赋值运算符,你可以这样做:

    >>> a = [(lambda x:(f := open(x), f.read(), f.close())[1])(i) 
             for i in ('hello.txt', 'dunno.txt', 'goodbye.txt')]
    >>> print(a)
    ['Hello', 'I dunno', 'Goodbye']
    

    它使用一个元组作为在 lambda 中放置多个语句的类比。简而言之,它按顺序执行以下操作:

    1. 在元组的第一个元素中,使用赋值运算符创建命名变量
    2. 在元组的第二个元素中,对新创建的命名变量执行一个返回值的操作。将此值存储在元组的第二个元素中。
    3. 在元组的第三个元素中,关闭我们最初在元组的第一部分打开的资源,再次使用命名变量。在这种情况下,file.close() 返回 None,我们不在乎,因为它实际上并没有引发错误。
    4. 告诉 lambda 返回元组的第二个元素(我们在其中对打开的资源执行重要操作的元素)。

    这利用了 python 3.8 中新添加的赋值运算符,以及元组按从前到后的顺序计算的事实。从理论上讲,您可以使用此范例在元组中实现整个函数,尽管该代码会变得异常复杂。这是 unpythonic 的原样。

    免责声明:此方法确实需要手动打开和关闭文件(或者,在您的情况下,我猜是手动调用 __enter__()__exit__()),因此它不需要t 完全封装with 的功能。我不知道您将如何对此进行错误处理。

    我使用 python 3.8 测试版对此进行了测试,直到at least October 21 2019 3.8 才会正确发布。这在 3.8 之前的任何当前存在的 python 版本中都不起作用。我不知道这是否符合预期的功能,或者它是否是一个错误,所以如果你出于任何原因选择尝试使用它,可能要小心。

    【讨论】:

    • 这很酷!在升级之前,我将不得不使用一种解决方法,但这非常简洁。谢谢!
    猜你喜欢
    • 2022-10-13
    • 2016-03-16
    • 2021-09-05
    • 2014-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多