Python 将长变量重新指定为本地缩写是否不好?

Python 将长变量重新指定为本地缩写是否不好?,python,readability,naming-conventions,Python,Readability,Naming Conventions,我更喜欢使用长标识符来保持代码的语义清晰,但是如果重复引用同一标识符,我希望它在当前范围内“让开”。以Python为例: def define_many_mappings_1(self): self.define_bidirectional_parameter_mapping("status", "current_status") self.define_bidirectional_parameter_mapping("id", "unique_id") self.def

我更喜欢使用长标识符来保持代码的语义清晰,但是如果重复引用同一标识符,我希望它在当前范围内“让开”。以Python为例:

def define_many_mappings_1(self):
    self.define_bidirectional_parameter_mapping("status", "current_status")
    self.define_bidirectional_parameter_mapping("id", "unique_id")
    self.define_bidirectional_parameter_mapping("location", "coordinates")
    #etc...
让我们假设我真的想继续使用这个长方法名,并且这些参数总是硬编码的。 实现1感觉是错误的,因为每行的大部分都被重复的字符占据。这些行通常也相当长,当嵌套在类定义和/或try/except块中时,很容易超过80个字符,从而导致难看的换行。让我们尝试使用for循环:

def define_many_mappings_2(self):
    mappings = [("status", "current_status"),
                ("id", "unique_id"),
                ("location", "coordinates")]
    for mapping in mappings:
        self.define_parameter_mapping(*mapping)
我将把所有类似的迭代技术放在实现2的框架下,实现2改进了将“唯一”参数与“重复”方法名称分开。然而,我不喜欢这样的效果,即将参数放在它们被传递到的方法之前,这很混乱。我更愿意保留“动词后跟直接宾语”的语法

我发现自己使用以下方法作为折衷:

def define_many_mappings_3(self):
    d = self.define_bidirectional_parameter_mapping
    d("status", "current_status")
    d("id", "unique_id")
    d("location", "coordinates")
在实现3中,long方法被一个极短的“缩写”变量作为别名。我喜欢这种方法,因为乍一看,它可以立即识别为一组重复的方法调用,同时具有更少的冗余字符和更短的行。缺点是使用极短且语义不清楚的标识符“d”


最具可读性的解决方案是什么?如果“缩写变量”是从本地作用域中的未修订版本显式分配的,那么使用它是否可以接受?

我将使用实现2,但这是一个完结的决定

我认为#2和#3同样可读。想象一下如果你有100个映射。。。无论哪种方式,如果不滚动到顶部,我都无法判断底部的代码在做什么。在#2中,您为数据命名;在#3中,您为函数命名。基本上是洗衣服

更改数据也是一种清洗,因为无论采用哪种方式,您都只需以与已有数据相同的模式添加一行

如果要更改对数据所做的操作,则会出现差异。例如,假设您决定为定义的每个映射添加调试消息。使用#2,您可以向循环中添加一条语句,它仍然很容易阅读。使用#3,您必须创建一个
lambda
或其他东西。lambdas没什么问题——我和其他人一样喜欢Lisp——但我想我还是会发现#2更容易阅读和修改


但这是一个千钧一发的时刻,你的品味可能会有所不同。

我再次求助于救援!尝试使用星图-下面是一个简单的演示:

list(itertools.starmap(min,[(1,2),(2,2),(3,2)]))
印刷品

[1,2,2]
星图是一个生成器,所以要真正调用这些方法,必须使用带有列表的生成器

import itertools

def define_many_mappings_4(self):    
    list(itertools.starmap(
        self.define_parameter_mapping,
        [
            ("status", "current_status"),
            ("id", "unique_id"),
            ("location", "coordinates"),
        ] ))
通常,我不喜欢使用伪列表构造来调用一系列函数,但这种安排似乎解决了大多数问题


如果
define\u parameter\u mapping
返回None,那么您可以用
any
替换
list
,然后所有的函数调用都会被执行,您就不必构建虚拟列表。

我认为#3不错,尽管我可能会选择比
d
稍长的标识符,但是这种类型的东西通常是数据驱动的,所以你会发现你自己使用了#2的一个变体,在这里你在循环一个数据库查询的结果或者一个配置文件中的某个东西,没有正确的答案,所以你会在这里得到各方面的意见,但我更愿意在我负责维护的任何代码中看到#2

#1冗长、重复且难以更改(例如,假设您需要对每一对调用两个方法或添加日志记录,那么您必须更改每一行)。但这通常是代码演变的方式,这是一种相当熟悉且无害的模式

#3与#1有着同样的问题,但更为简洁,其代价是需要基本上是宏观的、因而是新的、稍不熟悉的术语

#2是简单明了的。它以数据形式列出映射,然后使用基本语言构造对它们进行迭代。要添加新映射,只需向数组中添加一行即可。最后可能会从外部文件或URL加载映射,这将是一个简单的更改。要更改对它们所做的操作,您只需要更改for循环的主体(如果需要的话,它本身可以变成一个单独的函数)


你对“动词前宾语”的抱怨一点也不困扰我。在扫描该函数时,我基本上首先假设动词做了它应该做的事情,并将注意力集中在对象上,该对象现在清晰、立即可见且可维护。只有当出现问题时,我才会看这个动词,它的作用会立即显现出来。

看到重复调用
self.X.Y.Z
使用本地缩写,如
xyz=self.X.Y.Z
,然后调用
xyz()
,这一点也不奇怪——这里的基本原理是,假设X,Y,和Z在这些调用上是不变的,分配给局部xyz只执行一次属性解析,而不是在每次函数调用时执行。不过,这与您的具体案例没有直接关系,因为您只是缩写了一个特别长的名称,而不是一系列的属性引用。这是一个非常主观的问题,没有单一的答案,但就它的价值而言,我发现#3是最具可读性的,并且在任何地方都可以亲自使用。在Python的情况下,它的效率也稍微高一些。虽然这是itertools的一个有趣的用法,但很少会编写需要这种级别的解释和分析的代码。我确实喜欢这样做,它解决了动词-对象排序问题,尽管使用生成器非常混乱,我们不可避免地会遇到这种情况