【问题标题】:Pythonic code style [closed]Pythonic 代码风格
【发布时间】:2014-06-06 18:27:38
【问题描述】:

我是一名业余程序员,正在为我大学的一个研究项目编写一些 Python。对于可能在我之后从事此项目的任何人,我需要我的代码非常易读,因此我正在尝试遵循 PEP 8。但是,我遇到了规则冲突。有问题的行是在一长串 for 循环和 if 语句之后的字典定义。问题是约定的行不应该超过 79 个字符,但是续行应该从它们开始的地方缩进。我看到了逻辑上可以完成的三个选项,但不确定什么是最好的。

选项 1:将违规行留得太长

def getIndexedData(directory):                                                 |
    ...                                                                        |
    ...                                                                        |
    ...                                                                        |
                        # construct dictionary of images with peak locations   |
                        peaks[image] = {                                       |
                            'Xpixel': [float(x) for x in step[17][10][0].text.s|plit(' ')],
                            'Ypixel': [float(x) for x in step[17][10][1].text.s|plit(' ')]}
    return peaks                                                               |

选项 2:取消缩进续行

def getIndexedData(directory):                                                 |
    ...                                                                        |
    ...                                                                        |
    ...                                                                        |
                        # construct dictionary of images with peak locations   |
                        peaks[image] = {                                       |
                'Xpixel': [float(x) for x in step[17][10][0].text.split(' ')], |
                'Ypixel': [float(x) for x in step[17][10][1].text.split(' ')]} |
    return peaks                                                               |

选项 3:在某处拆分定义(不确定在哪里)

def getIndexedData(directory):                                                 |
    ...                                                                        |
    ...                                                                        |
    ...                                                                        |
                        # construct dictionary of images with peak locations   |
                        peaks[image] = {                                       |
                            'Xpixel':                                          |
                           [float(x) for x in step[17][10][0].text.split(' ')],|
                            'Ypixel':                                          |
                           [float(x) for x in step[17][10][1].text.split(' ')]}|
    return peaks                                                               |

我也愿意接受任何其他建议:)

谢谢,

~亚伦

【问题讨论】:

  • 有一个代码审查堆栈交换,您可能希望将其发布在此处而不是此处。
  • 最好的选择很可能是“4:重构,所以你没有那么多缩进级别”。
  • 如果我没记错的话,PEP 8 建议 120 个字符。 80非常非常严格,有人会说过时了。至于你的问题——我会选择第二种选择——但我的意见和任何意见一样好。只做你觉得最好的。这绝对不会使您的代码不可读。其次,请记住 PEP8 只是指导方针 - 有时你有例外......
  • @alfasin:上面写着 79,还有一个附加条件是 100 是可以的,如果你能让整个团队在代码上工作都同意的话。 120 就出来了。
  • 您可以标记为版主并请求迁移;对于非模组来说,codereview 还不是一个有效的迁移目标。 (尽管 codereview 可能希望查看更多您的代码,但可能会对其进行审查。)

标签: python coding-style readability pep8


【解决方案1】:

来自PEP 8 的一些相关的sn-ps,添加了我的斜体。

风格指南是关于一致性的。与本样式指南的一致性 很重要。项目内部的一致性更为重要。 一个模块或功能内的一致性是最重要的。

但最重要的是:知道什么时候不一致——有时 样式指南不适用。如有疑问,请使用您的最佳判断。 查看其他示例并决定什么看起来最好。不要犹豫 问!

...可以将标称线长度从 80 增加到 100 字符(有效地将最大长度增加到 99 字符),前提是 cmets 和 docstrings 仍然被包裹 72 个字符。

【讨论】:

  • 感谢大家的cmets和建议。我肯定会尝试您的一些建议,以尝试使 dict 生成更简洁,但就目前而言,代码可能会成为我描述的第一个选项。
【解决方案2】:

函数在简短而甜美时是最好的。我的意思是他们应该尽可能有一个非常明确和简单的目的。如果一个函数有许多级别的缩进,这表明该函数试图做太多事情。它的某些部分可能会被重构为自身的函数。

拥有“小”函数的一个优点是它使代码更易于理解。它还使代码更容易进行单元测试。最后,它有助于将代码保持在 79 列 PEP8 样式限制之下。

但是,您可以用来将代码保持在 79 列限制内的另一个技巧是使用临时变量:

def getIndexedData(directory):                                                 
    ...                                                                        
    ...                                                                        
    ...                                                                        
                        # construct dictionary of images with peak locations
                        texts = [text.split(' ') for text in step[17][10][:2]]
                        x, y = [map(float, text) for text in texts]
                        peaks[image] = {'Xpixel': x, 'Ypixel': y}
    return peaks   

【讨论】:

  • texts 作为生成器是否更有意义,因此您只迭代一次? (+1 都一样)
  • @JoranBeasley:我怀疑这会有很大的不同。生成器在降低内存消耗方面做得很好,但我认为它们对性能的影响不大(甚至可能会减慢速度)。
  • 我认为取决于列表大小......但你在这里几乎肯定不会有帮助......我没有注意到它只有 2 个元素长......
  • @JoranBeasley:我做了一点时间测试;在这种情况下,似乎需要在空间和速度之间进行权衡。列表理解似乎更快(对于中等大小的列表 - 可能只要您不点击交换空间),而生成器可以节省内存。
  • @unutbu 我绝对同意较小的函数看起来更好并且更易于阅读。我一定会保存您的代码以供以后试用。
【解决方案3】:

尽量减少嵌套的 for 循环和 if 语句的数量。使用函数、生成器或过滤器来实现这一点。如果您显示更多代码,我们可以提供帮助。 您还可以将列表生成拆分为单独的行:

xpixel = step[17][10][0].text.split(' ')
ypixel = step[17][10][1].text.split(' ')
peaks[image] = {
    'Xpixel': map(float, xpixel),
    'Ypixel': map(float, ypixel),
}

你也可以写一个转换函数:

def str_to_floats(text):
  return [float(x) for x in text.split()]

...

peaks[image] = {
    'Xpixel': str_to_floats(step[17][10][0].text),
    'Ypixel': str_to_floats(step[17][10][1].text),
}

顺便说一句。 step[17][10][0].text 对我来说看起来很奇怪。在您的示例中,只有选项 1 对我来说是可读的。

【讨论】:

  • step[...][..] 来自数百兆字节的已解析 .xml 文件中的 eTree 对象。可能有一种更有效的方式来爬取数据结构,但这似乎工作得相对较快,并且可以完成工作。