Это невозможно, даже через проверку стека.hasattr
не создает объект фрейма в стеке вызовов Python, как это написано в C, и пытается проверить последний фрейм Python, чтобы определить, приостановлен ли он в середине вызова hasattr
, и подвержен ли он всем ложным негативами ложные срабатывания.
Если вы в любом случае решительно настроены сделать свой лучший выстрел, самый надежный (но все еще хрупкий) кладж, который я могу придумать, - это сделать обезьянку-патч builtins.hasattr
с функцией Python создает кадр стека 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
, даже если вы хотите, чтобы он считал.