可以与内联语句一起使用吗?

时间:2019-08-01 18:19:11

标签: python python-3.x inline

例如,下面的代码不是内联的:

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) )

2 个答案:

答案 0 :(得分:5)

好吧,既然您可以解决问题,那么more-itertools.with_iter可能是您最好的选择。

注意这是一个第三方软件包,因此如果没有,则需要安装它

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

如果您不想使用第三方套餐,那么with_iter所做的就是:

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

所以这很容易实现。

答案 1 :(得分:3)

特别是对于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中新添加的Assignment Operator,又利用了从前到后按顺序评估元组的事实。从理论上讲,您可以使用此范例在元组中实现整个功能,尽管该代码会变得非常复杂。这是非Python的。

免责声明:此方法确实需要手动打开和关闭文件(或者,就您而言,我想是手动调用__enter__()__exit__()),并且结果它没有完全封装with的功能。我不知道您将如何对此进行错误处理。

我使用python 3.8 beta进行了测试,直到at least October 21 2019之前3.8才能正确发布。这在3.8之前的任何当前版本的python中均不起作用。我不知道这是否符合预期的功能或是否有错误,因此如果您出于任何原因选择尝试使用此功能,请小心。