区分 class 层次结构内部和外部的调用者
Discriminate between callers inside and outside class hierarchy
我有两个 classes 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
。
当从 class 方法
中设置 value
时,我希望有不同的行为
@value.setter
def value(self, value):
if set from inside class hierarchy:
pass
else:
raise AttributeError("can't set attribute")
模块 inspect
似乎没有给我足够的信息来执行此操作,除了对照已知函数列表进行检查。
您可以使用发出调用的代码来确定调用是否来自 class 内部。仅当调用不是以 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
您可以检查堆栈以确定调用者,以及它是否在 class 层次结构中以决定是否允许它:
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
Class 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'
我有两个 classes 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
。
当从 class 方法
中设置value
时,我希望有不同的行为
@value.setter
def value(self, value):
if set from inside class hierarchy:
pass
else:
raise AttributeError("can't set attribute")
模块 inspect
似乎没有给我足够的信息来执行此操作,除了对照已知函数列表进行检查。
您可以使用发出调用的代码来确定调用是否来自 class 内部。仅当调用不是以 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
您可以检查堆栈以确定调用者,以及它是否在 class 层次结构中以决定是否允许它:
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
Class 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'