Python 检测uuu getattribute_uuuu调用是否是由于hasattr引起的
我正在为一个类实现Python 检测uuu getattribute_uuuu调用是否是由于hasattr引起的,python,getattribute,hasattr,Python,Getattribute,Hasattr,我正在为一个类实现\uuu getattribute\uuu 我想指出任何不正确的(当然,这意味着可能会失败)提供属性的失败(因为\uuuu getattribute\uuuu实现非常复杂)。因此,如果我的代码在引发AttributeError之前无法找到/提供属性,我会记录一条警告 我知道: \uuuu getattribute\uuuu鼓励实现尽可能小而简单 根据调用方式/原因,\uuuu getattribute\uuu实现的行为不同,这被认为是错误的 访问属性的代码也可以尝试/excep
\uuu getattribute\uuu
我想指出任何不正确的(当然,这意味着可能会失败)提供属性的失败(因为\uuuu getattribute\uuuu
实现非常复杂)。因此,如果我的代码在引发AttributeError
之前无法找到/提供属性,我会记录一条警告
我知道:
\uuuu getattribute\uuuu
鼓励实现尽可能小而简单\uuuu getattribute\uuu
实现的行为不同,这被认为是错误的尝试/except
而不是使用hasattr
TL;DR:尽管如此,我还是想检测对
\uu getattribute\uuu
的调用是否是由于hasattr
(与访问属性的“真实”尝试相反)。您可以使用sys.\u 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
即使通过烟囱检查,这也是不可能的<代码> HASTART 在Python调用堆栈中不生成框架对象,因为它是用C编写的,并且试图检查最后一个Python帧,以猜测它是否被挂在“代码”> HASTART的中间,调用容易出现各种假阴性和假阳性。 如果你下定决心要尽最大努力,那么我能想到的最可靠(但仍然很脆弱)的方法就是使用一个Python函数来生成Python堆栈框架,即monkey patch
builtins.hasattr
:
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__
调用可能是从内部的\u hasattr
调用的\u然后将测试您的可能是从hasattr
调用的。这避免了假设调用代码使用名称“hasattr”,或者使用名称“hasattr”对应于这个特定的\uuu getattribute\uuuu
调用,或者hasattr
调用源自Python级别的代码而不是C
这里脆弱性的主要来源是,如果有人在monkey补丁运行之前保存了对realhasattr
的引用,或者其他人在monkey补丁运行之前保存了对realhasattr
的引用(例如,如果有人将此代码复制粘贴到同一程序中的另一个文件中)。isinstance
check试图捕捉我们面前的大多数其他猴子补丁案例hasattr
,但它并不完美
此外,如果用C编写的对象上的hasattr
触发了对对象的属性访问,则这看起来像是从hasattr
调用了您的\uuu getattribute\uuuuuu
。这是获得假阳性的最有可能的方法;上一段中的所有内容都会给出错误的否定。您可以通过检查hasattr
frame的f_locals
中的obj
条目是否是它应该是的对象来防止出现这种情况
最后,如果您的\uuuuuu getattribute\uuuuuuuu
是从装饰程序创建的包装、子类\uuuuuuuuuu getattribute\uuuuuuuuuuuuuuuuu
调用的,那么即使从hasaattr
调用了包装或重写,也不算是从hasaattr
调用的,即使你想让它计数。这在很多合理的情况下都会失败,例如如果hasattr(thing,'existing_attribute')和thing.existing_attribute==5
或如果hasattr(different_thing,'foo')或thing_with_getattribute.bar==5
。谢谢你的回答!我已经考虑过做一些类似的事情,但是分析这句话容易出错。我希望找到更具容错性,或者至少具有较少误报的功能(即,我可以接受缺少的基于hasattr
的调用,但不会错过“真正的”调用)。@user2357112是的,但如果一个人确实需要某个属性的值,那么他/她首先就不应该使用hasattr
,而是使用带有默认值的getattr
,或者使用try
阻塞对象。现有的\u属性
,以避免对\uu getattribute
的冗余和潜在危险的调用(当存在多个线程时)。如果只关心属性的存在,而不关心属性的值,则只能使用hasattr
。如果程序员遵循这一原则,那么我的方法的缺陷就不会成为问题。这个问题的前提是询问如何做违反设计原则的事情。假设代码库的其余部分严格遵循任何特定原则似乎是不安全的。另外,当您需要能够区分属性缺失和碰巧是默认值的属性值时,使用默认值的getattr
会很尴尬,即使在遵循您列出的规则的情况下,这仍然会失败。谢谢!覆盖hasattr确实是一个简单的解决方案。我自己应该想到的!