Python 中的常量分组

问题描述 投票:0回答:6

这主要是一个“良好的Python风格”问题。

我有一个模块,它使用了许多感觉应该分组的常量。

假设我们有狗和猫,它们每只都有许多条腿和最喜欢的食物。

请注意

  1. 我们只想对狗和猫的常数进行建模
  2. 未来我们很可能会有更多的动物。
  3. 这些常量不会在当前模块之外使用。

我想到了以下解决方案:


模块级别的常量

DOG_NUMBER_OF_LEGS = 4
DOG_FAVOURITE_FOOD = ["Socks", "Meat"]
CAT_NUMBER_OF_LEGS = 4
CAT_FAVOURITE_FOOD = ["Lasagna", "Fish"]

他们看起来并没有真正分组,但我认为这是我更喜欢的解决方案。


类作为命名空间

class Dog(object):
  NUMBER_OF_LEGS = 4
  DOG_FAVOURITE_FOOD = ["Socks", "Meat"]
class Cat(object):
  NUMBER_OF_LEGS = 4
  FAVOURITE_FOOD = ["Lasagna", "Fish"]

我不喜欢这个解决方案,因为我们将拥有不会使用的类,并且它们可以实际实例化。


常量字典

ANIMALS_CONFIG = { 
   "DOG" : { 
     "NUMBER_OF_LEGS" : 4,
     "FAVOURITE_FOOD" : ["Socks", "Meat"]
   },
   "CAT" : { 
     "NUMBER_OF_LEGS" : 4,
     "FAVOURITE_FOOD" : ["Lasagna", "Fish"]
   }
}

我也考虑过添加子模块,但我真的不想公开那些内部常量


最Pythonic的方法是什么/你会怎么做?

python python-2.7 namespaces coding-style constants
6个回答
12
投票

我会选择第四种选择,更喜欢

collections.namedtuple

Animal = namedtuple('Animal', 'number_of_legs favourite_food')

然后您创建如下实例:

DOG = Animal(4, ['Socks', 'Meat'])
CAT = Animal(4, ['Lasagna', 'Fish'])

并从外部访问值:

from animals import CAT

print CAT.number_of_legs

如果您不需要创建任何方法,那么拥有类确实没有意义,而且我认为上面的访问形式比例如更简洁:

from animals import animals

print animals['CAT']['number_of_legs']

namedtuple
,就像普通的
tuple
一样,也是不可变,所以你不能意外地重新分配,例如
CAT.number_of_legs = 2
某处。

最后,

namedtuple
是一种轻量级数据结构,如果您要创建大量动物,这可能很重要:

>>> import sys
>>> sys.getsizeof({'number_of_legs': 4, 'favourite_food': ['Lasagna', 'Fish']})
140
>>> from collections import namedtuple
>>> Animal = namedtuple('Animal', 'number_of_legs favourite_food')
>>> sys.getsizeof(Animal(4, ['Lasagna', 'Fish']))
36

4
投票

没有一刀切的答案,这实际上取决于您必须处理多少个常量、如何使用它们、多态调度是否有意义以及月相。

现在在您的示例中,由于您有两组或更多组具有相似结构和含义的常量,所以我会选择“类作为名称空间”解决方案。

FWIW 防止类被实例化并不困难:

>>> class Foo(object):
...    def __new__(cls):
...       return cls
... 
>>> Foo()
<class '__main__.Foo'>

但是文档应该足够了 - 请记住,无论如何,你的“常量”只是约定俗成的常量。


2
投票

在较新的版本(>= 3.7)中,我建议您使用

dataclass
并使其成为
frozen
。这不仅可以防止任何成员被更改,而且还允许将常量用作字典键(因为它们变得可哈希)。

并且在 python >= 3.10 中,您甚至可能想要设置

slots=True
来阻止添加新成员。

这样你也能正确打字:

from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class Animal:
    number_of_legs: int
    favourite_foods: tuple[str, ...]

DOG = Animal(number_of_legs=4, favourite_foods=('Socks', 'Meat'))
CAT = Animal(number_of_legs=4, favourite_foods=('Lasagna', 'Fish'))

print(CAT)                # Animal(number_of_legs=4, favourite_foods=('Lasagna', 'Fish'))
print(DOG.number_of_legs) # 4

0
投票

我会选择字典方法,因为它感觉更具可读性和更好的组织性,但我相信这是个人喜好的问题。

如果您使用仅包含静态变量和像 this 这样的方法的类并且从不实例化它们,那么使用类似乎也不是一个坏主意。


0
投票

使用NamedTuples的另一种方式:

如果有一些常量集,将它们分组总是好的。如果我们可以在键入它们时在 IDE 上访问它们,那就更好了。字典不是一个解决方案,因为 IDE 无法在您键入时显示建议。

from collections import namedtuple
    
_coverage_comments = namedtuple('COVERAGE_COMMENTS',['no_progress', 'coverage_improved'])
    
COVERAGE_COMMENTS = _coverage_comments(no_progress="No Progress", coverage_improved="Improved")
    
print(COVERAGE_COMMENTS.coverage_improved)

0
投票

您可以使用

enum
对常量集合进行分组。与常规 class
dataclass
namedtuple
相比,它具有
优势

  1. 枚举的值不能在运行时重新赋值,这使得它真正成为常量
  2. 您无法实例化枚举类的对象,这意味着它的行为类似于单例。

此外,

enum
项是可迭代的(
list
)和可散列的(
in
)。您可以通过
.value
属性访问实际的常量值,但这有点冗长。

from enum import Enum

class DogConstant(Enum):
  NUMBER_OF_LEGS = 4
  FAVOURITE_FOOD = ["Socks", "Meat"]

class CatConstant(Enum):
  NUMBER_OF_LEGS = 4
  FAVOURITE_FOOD = ["Lasagna", "Fish"]

# List all constants
print(f"{list(DogConstant)}")

# Check if an enum item exists in the enum class
print(f"{'NUMBER_OF_LEGS' in DogConstant.__members__ = }")

# Get value of an enum item
print(f"{DogConstant.NUMBER_OF_LEGS.value = }")
print(f"{DogConstant.FAVOURITE_FOOD.value = }")
print(f"{CatConstant.FAVOURITE_FOOD.value = }")

打印

[<DogConstant.NUMBER_OF_LEGS: 4>, <DogConstant.FAVOURITE_FOOD: ['Socks', 'Meat']>]
'NUMBER_OF_LEGS' in DogConstant.__members__ = True
DogConstant.NUMBER_OF_LEGS.value = 4
DogConstant.FAVOURITE_FOOD.value = ['Socks', 'Meat']
CatConstant.FAVOURITE_FOOD.value = ['Lasagna', 'Fish']
© www.soinside.com 2019 - 2024. All rights reserved.