我有两个类A
和B
,其中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
似乎没有提供足够的信息来执行此操作,除非检查已知函数列表。
答案 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'