【问题标题】:Python: Accessing static class variables from inside the classPython:从类内部访问静态类变量
【发布时间】:2012-07-25 17:02:33
【问题描述】:

所以我正在尝试创建一个扩展列表的类,具有将某些特殊属性映射到引用列表的某些部分的额外能力。使用this Py3k doc page,我创建了以下代码。这个想法是(假设我有一个此类的sequence 实例)sequence.seq 的行为应该与sequence[0] 完全相同,sequence.index 的行为应该与sequence[2] 完全相同,等等。

它似乎工作得很好,除了我似乎无法访问到列表的类变量映射属性。

我找到了this SO question,但要么那里的答案有误,要么方法中的某些东西有所不同。我也可以使用self.__class__.__map__,但是因为我需要__getattribute__ 中的类变量,所以我进入了一个无限递归循环。

>>> class Sequence(list):
...      __map__ = {'seq': 0,
...                 'size': 1,
...                 'index': 2,
...                 'fdbid': 3,
...                 'guide': 4,
...                 'factors': 5,
...                 'clas': 6,
...                 'sorttime': 7,
...                 'time': 8,
...                 'res': 9,
...                 'driver': 10 }
...      
...      def __setattr__(self, name, value): # "Black magic" meta programming to make certain attributes access the list
...           print('Setting atr', name, 'with val', value)
...           try:
...                self[__map__[name]] = value
...           except KeyError:
...                object.__setattr__(self, name, value)
...      
...      def __getattribute__(self, name):
...           print('Getting atr', name)
...           try:
...                return self[__map__[name]]
...           except KeyError:
...                return object.__getattribute__(self, name)
...      
...      def __init__(self, seq=0, size=0, index=0, fdbid=0, guide=None, factors=None, 
...           sorttime=None, time=None):
...                super().__init__([None for i in range(11)]) # Be sure the list has the necessary length
...                self.seq = seq
...                self.index = index
...                self.size = size
...                self.fdbid = fdbid
...                self.guide = ''
...                self.time = time
...                self.sorttime = sorttime
...                self.factors = factors
...                self.res = ''
...                self.driver = ''
... 
>>> a = Sequence()
Setting atr seq with val 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 31, in __init__
  File "<stdin>", line 17, in __setattr__
NameError: global name '__map__' is not defined

【问题讨论】:

    标签: python python-3.x metaprogramming static-variables class-variables


    【解决方案1】:

    由于在完全定义Sequence 之前不会调用任何方法,因此您可以毫无问题地参考Sequence.__map__。例如:

    def __setattr(self, name, value):
        print('Setting atr', name, 'with val', value)
        try:
            self[Sequence.__map__[name]] = value
        except KeyError:
            object.__setattr__(self, name, value)
    

    顺便说一句,只要不存在同名的实例属性,就可以通过对象访问类属性:

    class Foo:
        i = 3
        def __init__(self, overwrite):
            if overwrite:
                self.i = 4
    
    f = Foo(False)
    id(f.i) == id(Foo.i)     # Should be True
    f = Foo(True)
    id(f.i) == id(Foo.i)     # Should be False
    

    【讨论】:

    • 我试图在代码中不使用文字类名的情况下这样做......但至少这是一个解决方案。我忘记了方法-not-call-until-after-definition,谢谢提醒;)
    • 所以总结是在方法定义外使用var,但在方法定义内使用__class__.var。酷。
    • 嗯...我刚刚尝试在“现场”使用self._map,但没有成功。它让我陷入__getattribute__ 中的无限递归,试图获得self._map。我恢复到Sequence._map,效果很好。
    • 哦,对了。抱歉,我忘了您正在重新定义 getattribute,当您访问对象的属性(而不是类)时会调用它。
    【解决方案2】:

    您使用点 (.) 访问属性,而不是使用 []。 Python 不允许您省略self 引用,因此您需要使用self.__map__ 访问类变量。所以如果你想访问那个位置的元素,你需要self[self.__map__[name]]

    请注意,将双下划线夹在中间的名称用于您自己的目的并不是一个好主意。即使是两个前导下划线(会进行名称修改)通常也超出您的需要。如果您只想向用户表明 __map__ 属性不是公共 API 的一部分,请将其命名为 _map

    【讨论】:

    • 由于Sequence 扩展了list 类型,self 也是list 的一个实例,并且可以这样被索引。我相信self.__map__Sequence.__map__ 之间没有区别,这是我在回答中使用的风格。关于更喜欢 _map 而不是 __map__ 的好处。
    • self.__map__Sequence.__map__ 不同。实例没有类变量,请参阅here。感谢您提供有关隐藏变量的提示,我一定会改变它。 (而且 chepner 是对的,既然 Sequence 是子类列表,只要我运行 super().__init__(),self 实际上就是一个列表。)
    • @Dubslow: self.__map__Sequence.__map__ 指的是同一个对象,直到您分配给 self.__map__。然后将self.__map__ 创建为与类属性分开的实例属性。
    • @chepner 那么访问self.__map__(不分配它)不会导致它被添加到实例属性字典中吗?所以我真的可以使用self.__map__ 而不是Sequence.__map__ 而不必担心向实例添加属性?整洁的!抱歉弄错了,我认为我发布的链接可能需要一些编辑...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-05
    相关资源
    最近更新 更多