【问题标题】:Python: Constant ClassPython:常量类
【发布时间】:2014-11-21 22:59:14
【问题描述】:

我正在寻找这个问题的答案: Is it possible to define a class constant inside an Enum?

我最感兴趣的是 Ethan Furman 回答中的 Constant 类。

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)

问题是关于 Python 3.4,但我使用的是 2.7。在答案中,Ethan 将引力常数设置为 Planet 类的实例变量,如下所示:

G = Constant(6.67300E-11)

我在 2.7 中对这个类的测试表明,只输入 G 会给我这个:

Out[49]: Constant(3)

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

该值可通过 G.value 获得。但是,在 Ethan 的回答中,他使用了

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

这显然只有在返回值与__repr__ 输出时才有效。现在,如果我将class Constant: 更改为class Constant(int):,然后输入G,我仍然会得到__repr__ 输出,但如果我输入G * 4,我会得到12,而不是我得到的错误。 (TypeError: *: 'instance' 和 'int' 不支持的操作数类型 )

很明显,像 int 对象这样的东西在调用时可以输出一个数字。是否有我缺少的魔术方法可以让我为 Constant 类执行此操作?由于常量可以是字符串、整数或浮点数,我希望有 1 个类来处理它们,而不是 3 个单独的类来扩展这些对象。

该值也可以通过 G.value 设置。我可以将其锁定以使 Constant 类的行为类似于实际常量吗? (我怀疑答案是否定的。)

【问题讨论】:

  • 为什么要保持不变?
  • @Veedrac 为什么我不想要常量?还是只读类中的值?从我对 Python 的阅读中,我了解到这与 Python 的“我们都是同意的成年人”哲学背道而驰。到目前为止,我真的很喜欢 Python,但我认为这种哲学是一种疏忽。我为我自己、我自己和我自己编程——没有其他人会阅读或使用它。在过去 15 年中使用多种语言进行编码后,我学到了一些东西。其中一件事是,我可以通过简单地设计一个类来避免数天的调试,其中某些事情不能在该类的内部结构之外进行更改。
  • 我更想知道你要求它在什么水平上保持不变;我的意思是检查您是否对“通过公共接口的常量”感到满意,这是正确的做法,或者您实际上希望它完全是常量,这是不可能的。
  • 也不必关心他们是否可以设置value,只需将其命名为_value 将其设为私有即可。那么你就不用担心 p̶e̶o̶p̶l̶e̶ 自己会改变它。
  • @Vedra 对不起,我关于 Python 的前几个问题得到了礼貌的回应,你为什么想做这么愚蠢的事情?就常量而言,我可以忍受它们只能通过公共接口读取。我希望它们是真正的常量,并且我可以编写一个在定义它的类之外只读的类变量,但我知道这些在 Python 中可能是不可能的。

标签: python class enums


【解决方案1】:

好吧,value 是您的class Constant 的成员;所以你可以试试making it private:

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)

另一种方法可能是关注this recipe

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

【讨论】:

  • 我还没有尝试过这个食谱,但你提供的代码对我不起作用,因为它是自己的实例。我再也无法直接获得价值了。我怀疑这是因为@baxeico 所说的它必须成为类中的描述符。谢谢
  • 配方似乎有效。但是,它必须驻留在文件中并被导入。它似乎也是一个单例。在类中导入它(就在类定义下方)不会将其限制在该类的命名空间中。也不会将导入行放在__init__ 中。如果你能忍受这些限制,这个食谱就会很好地工作。
【解决方案2】:

你的类Constant应该继承自object,成为一个新风格的Python类。

这样,Constant 将成为一个所谓的描述符。简单来说,descriptor 是一个 Python 构造,用于自定义获取和设置类属性的行为。当描述符的实例被设置为另一个类的属性时,它们很有用。

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

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

所以,像这样定义类:

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)

然后这个小例子:

c = Constant(3.14)
print c

class Test:
    c = Constant(3.14)

t = Test()
print t.c

将打印:

Constant(3.14)
3.14

看到当Constant实例直接打印时,会调用__repr__方法,但是当作为另一个类属性打印时,会使用__get__。

您可以在this great article 上阅读有关描述符的更多信息。

【讨论】:

  • 根据我的测试,它仅适用于类变量而不是实例变量。如果我通过self.c = Constant(...)c = Constant 移动到__init__,那么我得到__repr__ 输出。而且它仍然是可写的,这对于常量来说很好,因为我可以用所有大写字母命名。理想情况下,我希望找到一种方法使实例变量可在类内写入,但不能在__main__ 脚本之外写入。我在 PHP 中有一些东西可以很好地做到这一点,但从我目前所读到的内容来看,这在 Python 中是不可能的。类属性似乎总是​​可用于覆盖。谢谢
猜你喜欢
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 2020-05-11
  • 2014-06-07
  • 2018-01-24
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
相关资源
最近更新 更多