Python的常数类

Python: Constant Class

我在看这个问题的答案:是否可以在枚举内定义类常量?

最让我感兴趣的是伊桑·弗曼的回答中不断出现的问题。

1
2
3
4
5
6
7
class Constant:
    def __init__(self, value):
        self.value = value
    def __get__(self, *args):
        return self.value
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)

问题是关于Python3.4,但我使用的是2.7。在答案中,Ethan将重力常数设置为类行星的一个实例变量,如下所示:

1
G = Constant(6.67300E-11)

我在2.7中对这门课的测试表明,只要输入g就可以得到:

1
Out[49]: Constant(3)

(我将它设置为3,以便在测试时使用。这看起来像是我的__repr__输出,如果我错了请纠正我。)

该值可通过g.value获得。然而,在伊桑的回答中,他使用了

1
return self.G * self.mass / (self.radius * self.radius)

这显然只在返回值与__repr__输出的情况下有效。现在,如果我把class Constant:改成class Constant(int):,那么G型仍然可以得到__repr__的输出,但是如果我把G * 4改成12,我得到的不是我得到的错误。(类型错误:不支持*:"instance"和"int"的操作数类型)

所以很明显,像int对象这样的对象在调用时可以输出一个数字。有没有一个神奇的方法可以让我为常量类做这个?因为常量可以是字符串、整数或浮点数,所以我宁愿用一个类来处理它们,而不是用三个独立的类来扩展这些对象。

该值也可以通过g.value设置。我可以把这个锁起来,这样常量类的行为就像一个实际常量吗?(我怀疑答案是否定的。)


类常量应该从对象继承,成为一个新样式的Python类。

这样,常量就是所谓的描述符。简单来说,描述符是一个python构造,用于定制获取和设置类属性的行为。当描述符的一个实例被设置为另一个类的属性时,它们是有用的。

在您的示例中,常量是描述符,行星有一个属性,它是常量的一个实例。当你得到行星类的属性g(例如self.g)时,你真正得到的是描述符的get方法返回的值。

注意,只有当描述符实例被另一个类属性访问时,才会调用get。

所以,像这样定义类:

1
2
3
4
5
6
7
class Constant(object):
    def __init__(self, value):
        self.value = value
    def __get__(self, *args):
        return self.value
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)

然后这个小例子:

1
2
3
4
5
6
7
8
c = Constant(3.14)
print c

class Test:
    c = Constant(3.14)

t = Test()
print t.c

将打印:

1
2
Constant(3.14)
3.14

请注意,当直接打印常量实例时,将调用方法repr,但当作为另一个类属性打印时,将使用get。

您可以在这篇伟大的文章中阅读更多关于描述符的内容。


好吧,value是你的class Constant的一个成员,所以你可以试着让它私有化:

1
2
3
4
5
6
7
8
9
10
11
class Constant:
  def __init__(self, value):
    # This actually transforms the variable to _Constant__value,
    # but also hides it from outer scope
    self.__value = value
  def __get__(self, *args):
    # Altough the member is theorically renamed as _Constant__value,
    # it is completely accesible from inside the class as __value
    reurn self.__value
  def __repr__(self):
    return '%s(%r)' % (self.__class__.__name__, self.__value)

另一种方法是遵循这个配方。

试一试,现在就让我来。希望我能帮助你!