Я пишу невероятно хакерский кусок не совсем производственного кода на Python, и мне нужен какой-то способ определить, был ли вызван доступ к атрибуту _XYZ__foo
из метода, определенного в классе с именем /_*XYZ/
. Однако это не так просто, так как мне нужно обнаружить доступ к методу original в случае, если что-то переопределило __getattribute__
и вызвало super()
.
Я плохо объясняю, поэтому ... правила похожи на private
в Java, за исключением того, что я хочу предотвратить обман. (Да, я знаю, что это противоречит философии Python; потерпите меня здесь.)
Мой текущий план атаки:
- Используйте
re.compile('_(?P<class>.*?)__(?P<name>.*)')
, чтобы определить имя класса (с предыдущими _
s удалены).
- Поднимитесь по цепочке
super
с помощью sys._getframe(n)
, чтобы узнать, где был доступ к атрибуту.
- Определить, в каком классе он был ... каким-то образом. Я застрял здесь.
Я мог бы сделать это, эмулируя ходьбу MRO super
, но я бы предпочел опираться на обнаружение, потому что проверить, что было вызвано super
и что было вызвано пользовательскими функциями, сложно.
Итак, к моему актуальному вопросу. По заданному кадру, как я могу определить, с каким классом связан метод? Если бы у меня был доступ к объекту функции, я мог бы сделать f.__qualname__[:-1-len(f.__name__)]
, но я не могу (или, по крайней мере, я не думаю я делаю). Как и я, я понятия не имею, как это сделать!
Вот простой пример, который демонстрирует, что я хочу сделать:
import sys
import re
import itertools
import builtins
from builtins import __build_class__
def build_class(func, name, *bases, metaclass=None, **kwds):
if bases[-1] is object:
bases = bases[:-1]
bases += HackishClass, object
if metaclass is None:
return __build_class__(func, name, *bases, **kwds)
return __build_class__(func, name, *bases, metaclass=metaclass, **kwds)
private_regex = re.compile('_(?P<class>.*?)__(?P<name>.*)')
class HackishClass:
__slots__ = ()
def __getattribute__(self, key):
match = private_regex.match(key)
if match is not None:
for depth in itertools.count(1):
frame = sys._getframe(depth)
if ...: # snip
# Check for the original attribute access here.
break
class_name = ... # HERE! MAGIC GOES HERE!
if class_name != match['class']:
raise AttributeError("This is private! Keep out.")
return super().__getattribute__(key)
builtins.__build_class__ = build_class