长时间保持 80 个字符的边距?

Keeping 80 chars margin for long with statement?

PEP-8-ify 的 pythonic 方式是什么,例如 with 语句:

with tempfile.NamedTemporaryFile(prefix='malt_input.conll.', dir=self.working_dir, mode='w', delete=False) as input_file, tempfile.NamedTemporaryFile(prefix='malt_output.conll.', dir=self.working_dir, mode='w', delete=False) as output_file:
    pass

我可以这样做,但是由于临时文件 i/o 没有使用 with 语句,它会在 with 之后自动关闭吗?那是 pythonic 吗?:

intemp = tempfile.NamedTemporaryFile(prefix='malt_input.conll.', dir=self.working_dir, mode='w', delete=False)

outtemp = tempfile.NamedTemporaryFile(prefix='malt_output.conll.', dir=self.working_dir, mode='w', delete=False)

with intemp as input_file,  outtemp as output_file:
    pass

或者我可以使用斜线:

with tempfile.NamedTemporaryFile(prefix='malt_input.conll.',
dir=self.working_dir, mode='w', delete=False) as input_file, \
tempfile.NamedTemporaryFile(prefix='malt_output.conll.', 
dir=self.working_dir, mode='w', delete=False) as output_file:
    pass

但是 PEP8 合规吗?那是 pythonic 吗?

PEP 0008 does say it's okwith 行使用反斜杠。

Backslashes may still be appropriate at times. For example, long, multiple with -statements cannot use implicit continuation, so backslashes are acceptable.

虽然它建议缩进,所以你的行应该是这样的:

with tempfile.NamedTemporaryFile(prefix='malt_input.conll.',
      dir=self.working_dir, mode='w', delete=False) as input_file, \
      tempfile.NamedTemporaryFile(prefix='malt_output.conll.', 
      dir=self.working_dir, mode='w', delete=False) as output_file:
    pass

建议您将其缩进明显不同的 space 到以下代码块,这样可以更清楚地看到 with 行结束和代码块开始的位置。

然而,如果您将参数括在方括号中,您实际上不需要使用斜线

with (tempfile.NamedTemporaryFile(prefix='malt_input.conll.',
      dir=self.working_dir, mode='w', delete=False)) as input_file, (
      tempfile.NamedTemporaryFile(prefix='malt_output.conll.', 
      dir=self.working_dir, mode='w', delete=False)) as output_file:
    pass

当然,这确实取决于您的确切句子安排,并且您的里程数可能会因行尾有括号是否比反斜杠更好而有所不同。

PEP-8其实举了两个类似情况的例子:

示例 1(看起来最适用,因为它使用了 with 语句):

with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())

示例 2:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):

看起来斜杠是一种允许的处理方式(但最终是不必要的,如果您将文件参数括在括号中)。

PEP 8 不是很清楚。当反斜杠延续没问题时,它只提到一次 with 语句作为异常:

Backslashes may still be appropriate at times. For example, long, multiple with -statements cannot use implicit continuation, so backslashes are acceptable:

with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())

解决这个问题的一种方法确实是首先调用 return 上下文管理器的函数,然后直接使用它,正如您在问题中已经建议的那样:

file_1 = open('/path/to/some/file/you/want/to/read')
file_2 = open('/path/to/some/file/being/written', 'w')

with file_1, file_2:
    file_2.write(file_1.read())

这非常有效(因为 with 语句将只调用上下文管理器的方法,而不管它来自哪里),并且还能正确关闭句柄。

然而,这在 PEP 8 中被明确禁止:

Context managers should be invoked through separate functions or methods whenever they do something other than acquire and release resources. For example:

Yes:

with conn.begin_transaction():
   do_stuff_in_transaction(conn)

No:

with conn:
   do_stuff_in_transaction(conn)

The latter example doesn't provide any information to indicate that the __enter__ and __exit__ methods are doing something other than closing the connection after a transaction. Being explicit is important in this case.

所以最后,似乎没有 PEP 8 允许的真正解决方案。在这一点上,我认为,“违反它”并反对它是完全没问题的:只是一种风格指南.

虽然它提出了许多好的规则,但在很多情况下,严格遵守这些规则并没有多大意义。我认为你的例子使用斜杠很难读,如果你允许更长的行并且每个上下文管理器只断行一次,你可能会读得更好:

with tempfile.NamedTemporaryFile(prefix='malt_input.conll.', dir=self.working_dir, mode='w', delete=False) as input_file, \
     tempfile.NamedTemporaryFile(prefix='malt_output.conll.', dir=self.working_dir, mode='w', delete=False) as output_file:
    pass

是的,您可能需要为此滚动,但至少您可以非常清楚地看到发生了什么。

另一种选择是在 with:

之前直接初始化对象
malt_input = tempfile.NamedTemporaryFile(prefix='malt_input.conll.', dir=self.working_dir, mode='w', delete=False)
malt_output = tempfile.NamedTemporaryFile(prefix='malt_output.conll.', dir=self.working_dir, mode='w', delete=False)
with malt_input as input_file, malt_output as output_file:
    pass

由于您直接在 with 之前执行此操作,因此此处应该是 PEP 8 规则可接受的例外。毕竟,它提高了可读性,这才是最重要的。

顺便说一句。请注意,上下文管理器可能不会 return self on __enter__,因此您仍应使用 as 语法将上下文管理器的 return 值分配给一个变量。


最后,另一种适用于您的特定情况的选择是将您的调用包装到一个单独的函数中:

def namedTemp(prefix):
    return tempfile.NamedTemporaryFile(prefix=prefix,
        dir=self.working_dir, mode='w', delete=False)

with namedTemp('malt_input.conll.') as input_file, \
     namedTemp('malt_output.conll.') as output_file:
    pass

所以基本上,抽象一切,所以 with 语句再次变得可读。