如何在Python中打破一行链接的方法?

185
我有一行以下的代码(不要责怪命名习惯,那不是我的):
subkeyword = Session.query(
    Subkeyword.subkeyword_id, Subkeyword.subkeyword_word
).filter_by(
    subkeyword_company_id=self.e_company_id
).filter_by(
    subkeyword_word=subkeyword_word
).filter_by(
    subkeyword_active=True
).one()

我不喜欢它的外观(不太易读),但我没有更好的想法来限制这种情况下的79个字符长度。有没有更好的方法来分割它(最好不用反斜杠)?

9个回答

323

您可以使用额外的括号:

subkeyword = (
        Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word)
        .filter_by(subkeyword_company_id=self.e_company_id)
        .filter_by(subkeyword_word=subkeyword_word)
        .filter_by(subkeyword_active=True)
        .one()
    )

1
我也喜欢它最好。不需要添加更多的代码,也没有反斜杠。 - Juliusz Gonera
26
不确定这里需要额外的缩进是出于何种原因;我认为只将悬挂行缩进一次,而不对尾括号进行缩进,这个解决方案读起来也很好。 - Carl Meyer
5
我认为在这里使用双重缩进会很有用,因为它在视觉上与普通的缩进块有所不同。当被其他代码包围时,这使得它更加明显,表明它是一行被换行包裹的单独代码。 - sth
1
在使用括号方面,最佳答案。正如Shanimal在另一个答案中的评论所提到的那样,通过括号使用隐含的行连续性实际上是PEP 8首选,而不是连续字符\ - kevlarr
1
到了2022年末,我仍然认为这是最好的解决方案,似乎这也是Black将我的原始问题中的代码转换成的方式(减去@CarlMeyer提到的额外缩进)。一般来说,现在值得使用首选编程格式化工具来处理你所选择的语言,并停止担心手动格式化。 - Juliusz Gonera
显示剩余2条评论

72

这是一种情况,其中使用行继续字符优于使用开括号。随着方法名称变得更长以及方法开始接受参数,需要此样式的原因变得更加明显:

subkeyword = Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word) \
                    .filter_by(subkeyword_company_id=self.e_company_id)          \
                    .filter_by(subkeyword_word=subkeyword_word)                  \
                    .filter_by(subkeyword_active=True)                           \
                    .one()

PEP 8旨在以常识和对实用性和美观的关注来进行解释。如果任何PEP 8指南导致代码难看或难以阅读,可以自由地违反它。

话虽如此,如果您经常发现与PEP 8不符,这可能是超越空格选择的可读性问题的信号 :-)


2
在这种特定情况下,在反斜杠上加1并对链接的过滤器进行对齐。这种情况在Django中也会出现,并且以这种方式最易读 - 但在任何其他情况下,我觉得括号短语更优秀(不会遭受“我的反斜杠后面有空格吗?”问题)。话虽如此,用括号括起来的短语可以用于实现相同的效果 - 但它会使你在阅读Python时处于Lisp阅读模式中,这让我感到不适。 - zxq9
16
我不认为这种解决方案在“方法名变得更长且方法开始接收参数”方面比“外部括号包裹”或“在每个开括号之后和每个闭括号之前进行换行”这两种解决方案更好。实际上,它在处理这种情况时比较差,因为(至少在此处所示)对于每一行悬挂需要更深的缩进。 - Carl Meyer
2
过滤器调用的缩进太多了。在这里,一个制表符或4个空格就足够了。此外,\的对齐方式也需要调整...你按住空格键多少秒了?通常我反对所有需要你像没有明天一样敲击空格键的方法。 - Zelphir Kaltstahl
5
值得一提的是,PEP8中写道:“将长行进行换行的首选方式是在括号、方括号和花括号内使用Python的隐式行续。可以通过在表达式周围加上括号来将长行分成多行。这应该优先使用,而不是使用反斜杠进行行续。”——Python.org PEP8还讨论了在何时适用反斜杠进行行续的情况。 - Shanimal
2
PEP8是一个很好的参考!在这里,要对齐所有的.filter调用,如果你将subkeyword更改为sub_keyword,那么你现在必须修复每一行的缩进,只因为你更改了变量名。当风格实际上妨碍可维护性时,这并不好... - kevlarr
显示剩余2条评论

21

我的个人选择会是:

subkeyword = Session.query(
    Subkeyword.subkeyword_id,
    Subkeyword.subkeyword_word,
).filter_by(
    subkeyword_company_id=self.e_company_id,
    subkeyword_word=subkeyword_word,
    subkeyword_active=True,
).one()
(注:该代码涉及到查询数据库中的数据)

2
如果有多个参数被传递进来,我同意这种写法。但是当0或1个参数是常见的时候,这看起来很丑陋。例如:https://gist.github.com/andybak/b23b6ad9a68c7e1b794d - Andy Baker
2
是的,那种风格有退化情况(像任何风格一样)。我不会在所有开放括号上中断。这些都让我不满意,但以下是一些情况:https://gist.github.com/pkoch/8098c76614765750f769 - pkoch

12

只需存储中间结果/对象并在其上调用下一个方法,例如:

q = Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word)
q = q.filter_by(subkeyword_company_id=self.e_company_id)
q = q.filter_by(subkeyword_word=subkeyword_word)
q = q.filter_by(subkeyword_active=True)
subkeyword = q.one()

13
对于像查询这样的操作,这个方法很有效,但作为一种通用模式,我不太确定。例如,在Beautiful Soup中进行链式查找时,如team_members = soup.find(class_='section team').find_all('ul').find_all('li'),每个.find(...)调用的返回值并不符合team_members的含义。 - Taylor D. Edmiston
2
@TaylorEdmiston 当然可以为部分结果取不同的名称。例如 section = soup.find(class_='section team')team_members = section.find_all('ul').find_all('li') - Jeyekomon

9

这个解决方案与其他人提供的有些不同,但是我喜欢它,因为它有时会导致巧妙的元编程。

base = [Subkeyword.subkeyword_id, Subkeyword_word]
search = {
    'subkeyword_company_id':self.e_company_id,
    'subkeyword_word':subkeyword_word,
    'subkeyword_active':True,
    }
subkeyword = Session.query(*base).filter_by(**search).one()

这是一种不错的用于构建搜索的技巧。通过条件列表来从复杂查询表单(或基于字符串的推断)中挖掘,然后只需将字典转换成过滤器即可。


2
很棒的解决方案。️虽然没有明确回答问题,但它揭示了程序员有时会尝试解决他们不必拥有的“痛苦”和问题(这里是不可读的过滤器链)。这种“痛苦”是更深层次原因的症状信号(通常是概念上、结构上、设计问题,甚至命名问题)。例如,您的字典search具有predicatematcher的有意义目的,类似于QBE:它匹配所有属性,就像在SQL中的WHERE .. AND .. - hc_dev

4
根据Python语言参考,您可以使用反斜杠或者直接换行。如果括号未成对出现,Python不会将其视为一行。在这种情况下,后续行的缩进不重要。

1

你好像在使用SQLAlchemy,如果是的话,sqlalchemy.orm.query.Query.filter_by() 方法可以接受多个关键字参数,所以你可以这样写:

subkeyword = Session.query(Subkeyword.subkeyword_id,
                           Subkeyword.subkeyword_word) \
                    .filter_by(subkeyword_company_id=self.e_company_id,
                               subkeyword_word=subkeyword_word,
                               subkeyword_active=True) \
                    .one()

但是更好的做法是:
subkeyword = Session.query(Subkeyword.subkeyword_id,
                           Subkeyword.subkeyword_word)
subkeyword = subkeyword.filter_by(subkeyword_company_id=self.e_company_id,
                                  subkeyword_word=subkeyword_word,
                                  subkeyword_active=True)
subkeuword = subkeyword.one()

+1 对于SQLAlchemy的filter_by()提示。它对于这个例子很好,但我经常使用filter(),因为它只接受一个条件。 - Juliusz Gonera

1

以下是与顶部答案略有不同的方法:将主要对象(Session)保留在第一行并使用单个缩进。这样可以快速识别主要对象和所有随后的链接方法调用。

subkeyword = (Session
    .query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word)
    .filter_by(subkeyword_company_id=self.e_company_id)
    .filter_by(subkeyword_word=subkeyword_word)
    .filter_by(subkeyword_active=True)
    .one()
)

1

我喜欢将参数缩进两个块,并将语句缩进一个块,就像这样:

for image_pathname in image_directory.iterdir():
    image = cv2.imread(str(image_pathname))
    input_image = np.resize(
            image, (height, width, 3)
        ).transpose((2,0,1)).reshape(1, 3, height, width)
    net.forward_all(data=input_image)
    segmentation_index = net.blobs[
            'argmax'
        ].data.squeeze().transpose(1,2,0).astype(np.uint8)
    segmentation = np.empty(segmentation_index.shape, dtype=np.uint8)
    cv2.LUT(segmentation_index, label_colours, segmentation)
    prediction_pathname = prediction_directory / image_pathname.name
    cv2.imwrite(str(prediction_pathname), segmentation)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接