Python中的类变量和继承

时间:2015-07-22 09:30:17

标签: python inheritance

我希望能够定义一个存在但不在子类或实例之间共享的类变量(在基类中以某种方式)。我最初尝试的是以下内容:

from __future__ import print_function  # For lambda print()

class CallList(list):
    def __call__(self, *args, **kwargs):
        for f in self:
            f(*args, **kwargs)

class State:    
    on_enter = CallList()
    def enter(self):
        self.on_enter()

class Opening(State): pass 
class Closing(State): pass

Opening.on_enter.append(lambda: print('Opening state entered'))
Closing.on_enter.append(lambda: print('Closing state entered'))

但是子类和子类实例的行为是引用基类类变量,它给出了以下内容:

opening = Opening()
closing = Closing()

opening.enter()
# Actual output:  Opening state entered
#                 Closing state entered
# Desired output: Opening state entered

opening.on_enter.append(lambda: print('Additional instance callback'))
opening.enter()
# Actual output:  Opening state entered
#                 Closing state entered
#                 Additional instance callback
# Desired output: Opening state entered
#                 Additional instance callback

closing.enter()
# Actual output:  Opening state entered
#                 Closing state entered
#                 Additional instance callback
# Desired output: Closing state entered

我理解为什么会发生这种情况(我期待与经验不同的是其他语言,但那很好)。

是否可以修改Base类(而不是子类)来获取子类,每个子类都有自己的类变量副本,并且子类的实例可以得到它们的classes变量的副本,然后可以独立修改也没有共享?

1 个答案:

答案 0 :(得分:1)

首先,通过继承object:

开始使用new-style classes
class State(object):    
    on_enter = CallList()
    def enter(self):
        self.on_enter()

旧式课程已经过时了,我建议不会在那里工作。

您可以使用descriptors解决您的具体问题。正如您在文档中看到的那样,描述符允许我们覆盖特定属性的属性访问,即它们应用于的属性。从类中读取属性时甚至可以这样做。

import weakref

class CallListDescriptor(object):
    def __init__(self):
        self._class_level_values = weakref.WeakKeyDictionary()

    def __get__(self, instance, type):
        if instance is None:
            # class-level access
            try:
                return self._class_level_values[type]
            except KeyError:
                default = CallList()
                self._class_level_values[type] = default
                return default
        # instance-level access
        return instance._call_lists.setdefault(self, CallList())


class State(object):
    def __init__(self):
        # for the instance-level values
        self._call_lists = {}

    on_enter = CallListDescriptor()

我们正在使用weakref作为类级属性,以确保在超类仍然在范围内时,子类可以正确地收集垃圾。

我们可以测试它是否有效:

class SubState(State):
    pass

class OtherSubState(State):
    pass

assert State.on_enter is State.on_enter
assert State.on_enter is not SubState.on_enter
assert State.on_enter is not OtherSubState.on_enter
assert SubState.on_enter is SubState.on_enter

instance = SubState()
assert instance.on_enter is not SubState.on_enter

但是我会建议摆脱子类功能,只是确保实例单独的值,然后将状态表示为State实例而不是子类(除非你有充分的理由不是,完全可能是这样的:

class CallListDescriptor(object):
    def __get__(self, instance, type):
        if instance is None:
            return self

        return instance._call_lists.setdefault(self, CallList())

class State(object):
    def __init__(self):
        # for the instance-level values
        self._call_lists = {}