我正在为课程重新实现__getattribute__
。
我想注意到提供属性的任何不正确(当然,这是预期的失败)(因为__getattribute__
实现非常复杂)。为此,如果我的代码在引发AttributeError
之前无法找到/提供属性,则会记录一条警告。
我知道:
__getattribute__
实现尽可能小。__getattribute__
实现根据其调用方式/原因被认为是错误的。try/except
,而不是使用hasattr
。 TL; DR :尽管如此,我还是想检测是否由于__getattribute__
而对hasattr
进行了呼叫(相反,尝试访问“ {属性)。
答案 0 :(得分:2)
这是不可能的,即使通过堆栈检查也是如此。 hasattr
在Python调用堆栈中不生成任何框架对象,因为它是用C语言编写的,因此尝试检查最后一个Python框架以猜测它是否在hasattr
调用中间被暂停,这很容易发生各种误报和误报。
如果您绝对决心尽一切所能,那么我能想到的最可靠(但仍很脆弱)的挥霍动作是使用做的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
的情况,但这并不完美。
此外,如果用C编写的对象上的hasattr
触发了对象的属性访问,则看起来就像从__getattribute__
调用了hasattr
。这是获得误报的最可能方法。上一段中的所有内容都会产生假阴性。您可以通过检查obj
框架的hasattr
中f_locals
的条目是它应该成为的对象来防止这种情况。
最后,如果您的__getattribute__
是由装饰器创建的包装器,子类__getattribute__
或类似的东西调用的,则即使包装器被调用为hasattr
也不会算作调用或覆盖是从hasattr
调用的,即使您希望它计数也是如此。
答案 1 :(得分:-1)
您可以使用sys._getframe
获取调用者框架,并使用inspect.getframeinfo
获取进行调用的代码行,然后使用诸如regex之类的解析机制(您不能请使用ast.parse
,因为一行代码通常是不完整的语句)以查看hasattr
是否是调用者。它不是很健壮,但在大多数合理的情况下都可以使用:
import inspect
import sys
import re
class A:
def __getattribute__(self, item):
if re.search(r'\bhasattr\b', inspect.getframeinfo(sys._getframe(1)).code_context[0]):
print('called by hasattr')
else:
print('called by something else')
hasattr(A(), 'foo')
getattr(A(), 'foo')
这将输出:
called by hasattr
called by something else