Python变量命名约定,用于表示单位的方法。

13

首先,当我询问单位时,我的意思是度量单位,例如英寸、英尺、像素和单元格。我并不是指int和float等数据类型。

维基百科 将此称为逻辑数据类型,而不是物理数据类型。

我想知道命名变量的最佳方法。

以下是一些示例代码,以便更好地理解我的问题:

board_length=8 #in inches
board_length=8*12 #Convert from feet to inches

注意这两个值都是整数(或浮点数,我不在意),但是单位已经被更改了。变量名没有变化。 我可以建立一个约定,这就是这个问题的目的。如果没有指导,我可能会像这样做:

board_length=8
board_length_inches=8*12

我认为这是一种临时的做事方式。或者,我可以建立一个惯例:

Fboard_length=8
Iboard_length=8*12

还有其他变量的变体,我同样不喜欢。 我如何以描述性的方式命名变量,同时尽可能接近PEP-08

只是为了尽可能清晰,变量可能具有不同的数据类型,但单位将是相同的(以英寸为例,无论它存储为整数还是浮点数,其命名都将相同)


1
根据单位对脚本的重要性,您可以考虑基于每个单位创建类型子类。每个“度量单位”(长度、重量等)都将有自己的类,继承内置类型,并具有其自己的子类用于各个单位。这些单位将具有将所有相同类型的其他单位转换为方法。这需要很多工作,因此您需要问问自己是否真的值得这样做。 - wnnmaw
或者你可以装饰内置函数,使其具有一个属性或方法来跟踪单位。 - wnnmaw
1
我不会太担心这个问题。我想不出有哪些情况下你需要在不同的单位中持续储存相同的值。我建议只需这样操作 - board_length = 24 # in inches (几行之后)print(to_feet(board_length)),或者 FEET = 12; print(board_length*FEET) - rlms
1
我在这种情况下使用 board_length_inches。保持简单。 - RemcoGerlich
1
我赞同@sweeneyrod的观点。换句话说,你的策略应该是选择一个内部用于所有计算的单位,并始终如一地使用它。只有在与人类用户或需要使用不同单位的某些模块进行接口时,你才需要考虑不同单位之间的转换,并在代码中的这些接口点使用恰当命名的转换函数或方法。 - John Y
@john y,考虑一下我有以(Xn,Yn)指定的笛卡尔坐标,并想将这些点映射到2D矩阵(Mn,Nn)的情况。通常的过程是从一个参考系统转换到另一个参考系统,进行某些操作,然后再转换回原始系统。在代码中,这个过程会在不同的变量上以不同的时间发生。实际上,我一直在使用后缀。 - Robert Lugg
4个回答

10

虽然你可以提出一种命名约定,但通过构建一个表示“距离”的对象,并具有以不同单位读写的属性,可能会更好地为你服务。例如:

class Distance(object):

    def __init__(self):
        self._inches = 0

    @property
    def inches(self):
        return self._inches

    @inches.setter
    def inches(self, value):
        self._inches = value

    @property
    def feet(self):
        return self._inches/12

    @feet.setter
    def feet(self, value):
        self._inches = value * 12
你甚至可以将其更加通用化,这样你就可以轻松地扩展新的转换。(注意:根据评论进行了编辑以进行备忘录)
from collections import defaultdict

class Distance(object):

    _conversion_map = defaultdict(lambda: {'to' : None, 'from' : None})

    def __init__(self, **kwargs):
        self._memo = {}
        if kwargs:
            unit, value = kwargs.iteritems().next()
            if unit == 'inches':
                self.inches = value
            else:
                setattr(self, unit, value)
        else:
            self.inches = 0

    def __getattr__(self, name):
        if name in self._conversion_map:
            try:
                return self._memo[name]
            except KeyError:
                converter = self._conversion_map[name]['to']
                if converter is None:
                    raise AttributeError
                converted = converter(self.inches)
                self._memo[name] = converted
                return converted
        else:
            raise AttributeError

    def __setattr__(self, name, value):
        if name == '_memo':
            super(Distance, self).__setattr__(name, value)
        else:
            # Clear memoized values if setting the value of the object
            self._memo = {}
        if name == 'inches':
            super(Distance, self).__setattr__(name, value)
        if name in self._conversion_map:
            converter = self._conversion_map[name]['from']
            if converter is None:
                raise AttributeError
            self._memo[name] = value
            self.inches = converter(value)
        else:
            raise AttributeError

    @classmethod
    def converter(cls, func):
        direction, unit = func.__name__.split('_', 1)
        cls._conversion_map[unit][direction] = func
        return func

@Distance.converter
def to_feet(value):
    return value / 12

@Distance.converter
def from_feet(value):
    return value * 12

board_1_length = Distance(feet=2)
board_2_length = Distance(inches=14)
board_1_length.inches # 24
board_2_length.feet # 1 (integer division)

对于我的应用程序,我不太倾向于使用这种技术(虽然可能)。问题在于我过于简单的示例中存在缺陷。问题是单位之间的转换很慢,而且基本上是单向的...在我的特定情况下,有时将形状表示为矢量数据,有时将其表示为灰度场(即SVG与BMP)。 - Robert Lugg
1
你可以将这种模式与缓存系统相结合,以便仅在第一次(虽然仍然是惰性的)完成完整转换,然后随后返回缓存的转换结构。 - Silas Ray

4

六年后...

在我提出问题之后很久,我偶然发现了Python类型库。它提供了一种(运行时)免费的方法来静态类型检查变量,更具体地说,不仅可以使用“int”或“float”,而且还可以使用“inches”和“meters”:

from typing import NewType

UserId = NewType('UserId', int)
some_id = UserId(524313)

这让我得到了我所追求的大部分内容。


你提到了英寸和米这样的单位,但是在你的示例或者https://docs.python.org/3/library/typing.html上哪里提到了呢? - root

3

我总体上喜欢这个建议,它似乎是实现 @BrianAgnew 建议的一个好方法。 - Robert Lugg

2

我认为应该使用不同的对象类型来提供类型安全性,而不仅仅依靠命名约定。否则,您可能会将表示英寸的变量传递给需要英里的方法。

我认为依靠命名约定长期来维护会有问题,使用类型将为您提供更多的灵活性和安全性(例如,内置于对象类型中的转换等)。


这是明智的建议。如果命名约定的目的是帮助开发人员避免混淆和错误,那么让Python类系统为您强制执行规则会更加有效。如果仅使用命名约定,则可能会使困惑的开发人员轻易地做出像12(英寸)+ 1(厘米)= 13(哎呀)等错误。 - Chris Johnson
我会接受这个答案,即使它没有提出命名约定,但它提供了一个对其他对此问题感兴趣的人有用的替代方案。谢谢。 - Robert Lugg

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