Python - 使用 class 启用点符号

Python - Using a class to enable dot notation

正在处理具有子属性的 class。下面的代码是一个好的实践/Pythonic 吗?如果没有,有什么建议吗?

目标

exp = ExCls()
prop1 = exp.subprop.prop1
exp.print_prop2()
## prop1 = 1
## prints 2

示例代码

class ExCls():
   class subprop:
      prop1 = 1
      prop2 = 2
   def print_prop2(self):
      print self.subprop.prop2

在大多数常见的高级语言中,点符号用于指示命名空间。 Python也是如此。以下面无用的class为例:

>>> class UselessClass(object):
...     @property
...     def and_on(self):
...         return self
...     def forever(self):
...         return "I can see into forever"
>>> this_goes_on = UselessClass()
>>> this_goes_on.and_on.and_on.and_on.and_on.and_on.forever()
'I can see into forever'

它所做的只是返回自身(一个 UselessClass 类型的实例化对象),它可以访问它自己的所有属性。

您的代码的唯一问题是,正如 GingerPlusPlus 指出的那样,您正在 subpropExCls 的所有实例之间共享。这可能是需要的,但(根据问题)也可能不是。这是为什么这很糟糕的一个例子:

>>> test1 = ExCls()
>>> test2 = ExCls()
>>> test1.subprop.prop1
1
>>> test2.subprop.prop1 = 2
>>> test1.subprop.prop1
2

如您所见,这通常不是您所期望的行为。相反,您可能想要做的是:

class ExCls(object):
    def __init__(self):
        class subprop:
            prop1 = 1
            prop2 = 2
        self.subprop = subprop()

    def print_prop2(self):
        print(self.subprop.prop2)

总的来说,我强烈建议您回去阅读 Python 的 类 以及它们的工作原理。

我会建议使用正确的工具来完成工作——如果我希望我的 subprop 只包含一些数据,我会使用 types.SimpleNamespace:

from types import SimpleNamespace

class Useless:
    def __init__(self):
        self.subprop = SimpleNamespace(prop1=1, prop2=2)

或者,它是足够接近的不可变兄弟,collections.namedtuple:

from collections import namedtuple

class Useless:
    Subprop = namedtuple('Subprop', 'prop1, prop2')
    def __init__(self):
        # two lines below does the same, choose more readable one
        self.subprop = Subprop(1, 2)
        self.subprop = Subprop(prop1=1, prop=2)

如果我希望它也包含一些方法,我会使用简单的自定义对象:

class Useless:
    class Subprop:
        def __init__(self, prop1, prop2):
            self.prop1 = prop1
            self.prop2 = prop2
        def sum(self):
            return self.prop1 + self.prop2
    def __init__(self):
        # two lines below does the same, choose more readable one
        self.subprop = Subprop(1, 2)
        self.subprop = Subprop(prop1=1, prop2=2)