这是不可能的,即使通过堆栈检查也是如此。hasattr
在 Python 调用堆栈中不会产生帧对象,因为它是用 C 编写的,并且试图检查最后一个 Python 帧以猜测它是否在hasattr
调用中间暂停很容易出现各种漏报和误报。
如果您绝对决心无论如何都要尽力而为,那么我能想到的最可靠(但仍然很脆弱)的kludge是使用确实产生Python堆栈框架builtins.hasattr
的Python函数进行猴子补丁:
import builtins
import inspect
import types
_builtin_hasattr = builtins.hasattr
if not isinstance(_builtin_hasattr, types.BuiltinFunctionType):
raise Exception('hasattr already patched by someone else!')
def hasattr(obj, name):
return _builtin_hasattr(obj, name)
builtins.hasattr = hasattr
def probably_called_from_hasattr():
# Caller's caller's frame.
frame = inspect.currentframe().f_back.f_back
return frame.f_code is hasattr.__code__
然后调用probably_called_from_hasattr
内部__getattribute__
将测试您__getattribute__
是否可能是从 调用的hasattr
。这避免了假设调用代码使用了名称“hasattr”,或者名称“hasattr”的使用对应于这个特定__getattribute__
的调用,或者hasattr
调用源自 Python 级代码而不是 C。
这里脆弱性的主要来源是如果有人hasattr
在猴子补丁通过之前保存了对真实的引用,或者如果其他人在猴子补丁hasattr
中(例如,如果有人将此代码复制粘贴到同一程序中的另一个文件中)。该isinstance
检查试图捕捉大多数其他人hasattr
在我们面前修补猴子的情况,但它并不完美。
此外,如果hasattr
在用 C 编写的对象上触发对您的对象的属性访问,那看起来就像__getattribute__
是从hasattr
. 这是最有可能得到误报的方法;上一段中的所有内容都会给出假阴性。您可以通过检查框架obj
中的条目是否是它应该是的对象来防止这种情况。hasattr
f_locals
最后,如果你__getattribute__
是从装饰器创建的包装器、子类或类似的东西中调用的,那么即使包装器或覆盖是从调用的,即使你希望它计数__getattribute__
,也不会算作调用。hasattr
hasattr