区分类层次结构内外的调用者

时间:2016-04-15 11:48:39

标签: python introspection

我有两个类AB,其中B继承自A并覆盖属性。 A不受我的控制,所以我无法改变它。

代码如下:

class A():
    def __init__(self, value):
        self.value = value
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, value):
        self._value = value

class B(A):
    def __init__(self, value):
        super(B, self).__init__(value)
    @property
    def value(self):
        return super(B, self).value
    @value.setter
    def value(self, value):
        raise AttributeError("can't set attribute")

当我尝试拨打B(1)时,我显然会AttributeError: can't set attribute

我想从类方法

中设置value时有不同的行为
 @value.setter
 def value(self, value):
     if set from inside class hierarchy:
         pass
     else:
         raise AttributeError("can't set attribute")

模块inspect似乎没有提供足够的信息来执行此操作,除非检查已知函数列表。

2 个答案:

答案 0 :(得分:0)

您可以使用进行调用的代码来确定调用是否来自类内部。如果呼叫未以self.value =开头,则仅抛出异常。

import re
import traceback

class A(object):
    def __init__(self, value):
        self.value = value
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, value):
        self._value = value

class B(A):
    def __init__(self, value):
        super(B, self).__init__(value)
    @property
    def value(self):
        return super(B, self).value
    @value.setter
    def value(self, value):
        call = traceback.extract_stack(limit=2)[0][3]
        if re.match(r'self.value\s*=', call):
            pass
        else:
            raise AttributeError("can't set attribute")

b = B(1) # OK
b.value = 3 # Exception

当然,只要您开始调用变量self

,就会中断
self = B(1) # OK
self.value = 3 # Meh, doesn't fail

答案 1 :(得分:0)

您可以检查堆栈以确定谁调用了,以及它是否在类层次结构中决定是否允许它:

import inspect


def who_called():
    frame = inspect.stack()[2][0]
    if 'self' not in frame.f_locals:
        return None, None
    cls = frame.f_locals['self'].__class__
    method = frame.f_code.co_name
    return cls, method

class A(object):
    def __init__(self, value):
        self._value = value

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        self._value = value

    # Assuming this existed it would also work
    def change_value(self, value):
        self.value = value

班级B现在正在检查:

class B(A):
    def __init__(self, value):
        super(B, self).__init__(value)

    @property
    def value(self):
        return super(B, self).value

    @value.setter
    def value(self, value):
        cls, method = who_called()
        if cls in B.__mro__ and method in A.__dict__:
            self._value = value
        else:
            raise AttributeError("can't set attribute")

证明:

b = B('does not raise error')
b.change_value('does not raise error')
b.value = 'raises error'