变量命名的Python约定以指示单位

时间:2014-02-11 17:26:02

标签: python naming-conventions pep8

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

Wikipedia将此称为逻辑数据类型,而不是物理数据类型

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

这里有一些代码可以解决我的问题:

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

为了尽可能清楚,变量可能有不同的数据类型,但单位将是相同的(英寸将具有相同的命名,无论它是否存储为整数或浮点数)

4 个答案:

答案 0 :(得分:9)

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

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)

答案 1 :(得分:2)

如果您想获得更强大的单位支持,请查看PyPi's Pint module。它并没有直接处理你的命名约定问题,但它可能需要花费很多工作来处理频繁的转换。您可以在此处找到有关它和其他一些单元模块的信息:

http://www.drdobbs.com/jvm/quantities-and-units-in-python/240161101

答案 2 :(得分:1)

我会更进一步,有单独的对象类型提供类型安全,而不是简单地依赖命名约定。否则,您可以将表示英寸的变量传递给需要英里数的方法。

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

答案 3 :(得分:0)

...六年后...

问了很长时间,我偶然发现了python类型库。它提供了一种(运行时)免费的静态类型方法来检查变量,而不仅仅是“ int”或“ float”,而是“ inches”和“ meters”:

from typing import NewType

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

这使我获得了大部分的收获。