2014-01-14 8 views
4

Я работаю с довольно большой базой кода ООП, и я хотел бы добавить некоторые трассировки/протоколирования. Самый простой способ сделать это - представить декоратор вокруг определенных методов на некоторых базовых классах, но, к сожалению, декораторы не наследуются.Python: Наследование наложения декоратора?

я попробовать что-то вроде следующего:

def trace(fn): 
    def wrapper(instance, *args, **kwargs): 
     result = fn(instance, *args, **kwargs) 
     # trace logic... 
     return result 
    return wrapper 

class BaseClass(object): 
    def __init__(self, ...): 
     ... 
     self.__call__ = trace(self.__call__) # line added to end of method 

... и в то время как метод __call__является завернутые (который я могу видеть из печати информации экземпляра на консоль) в wrapper функции ISN» t выполняется, как ожидалось.

Я также кратко рассмотрел использование метакласса на основе this answer, но он мгновенно разбивает другие части системы, которые используют интроспекцию, поэтому я думаю, что это не-go.

Есть ли какой-либо другой способ я могу заставить приложение этих декораторов использовать метод классов __call__, наследующий от BaseClass?

+0

Итак, вы говорите, что хотите обернуть '__call__' в' BaseClass' и все его дочерние элементы? – freakish

+0

@ freakish: да, вроде. '__call__' в' BaseClass' перезаписывается всеми дочерними элементами (и их много), поэтому применение декоратора вокруг '__call__' на уровне' BaseClass' является самым простым способом последовательно добавлять трассировку в одном месте. –

+0

Это то же самое, что вы пытаетесь решить, декоратор на базовом классе наследует подкласс. http://stackoverflow.com/questions/3782040/python-decorators-that-are-part-of-a-base-class-cannot-be-used-to-decorate-membe –

ответ

3

Зачем метапрограммировать беспорядок в самоанализе? Возможно, вы не используете его правильно? Попробуйте это (предполагая Python2.х):

class MyMeta(type): 
    def __new__(mcl, name, bases, nmspc): 
     if "__call__" in nmspc: 
      nmspc["__call__"] = trace(nmspc["__call__"]) 
     return super(MyMeta, mcl).__new__(mcl, name, bases, nmspc) 

class BaseClass(object): 
    __metaclass__ = MyMeta 

Вы можете просто наследовать от BaseClass и __call__ будет получить завернутые автоматически.

Я не уверен, что такое самоанализ нарушит это. Если BaseClass фактически не наследует от object, а от чего-то, что реализует свою собственную мета? Но вы можете справиться и с этим делом, заставив MyMeta унаследовать от метафайла этого родителя (вместо type).

+0

Это сработало; Я думаю, что мое использование метода '__init__' вызывало сбои в интроспекции, тогда как' __new__' «разрезает» в правой точке. –

+1

@unpluggd Это странно, я думаю, это можно сделать и с '__init__'. Я буду копаться в нем. Хотя, когда дело доходит до метапрограммирования, я чаще использую '__new__'. – freakish

+0

Я не думаю, что вам нужно передать 'mcl' в качестве аргумента в вызове' __new__' суперкласса; 'super' связывает этот аргумент. – user2357112

3

Вы не можете сделать это ... видеть вещи, как здесь: Python method resolution mystery

В принципе. большинство специальных функций просматриваются на уровне класса. Обезвреживание его после инициализации не будет иметь никакого эффекта.

Тем не менее, вы должны быть в состоянии сделать что-то вроде этого:

class BaseClass(object): 
    __call__ = trace(object.__call__) 
    def __init__(self, ...): 
     ... 

Теперь она обернута в класса уровне, поэтому он будет работать.

Редактировать: Это не совсем ответ. То, что вы хотите что-то вроде:

class BaseClass(object): 
    @decorator 
    __call__ = something 

class SubClass(BaseClass): 
    __call__ = something_else 

В этом случае ... это не имеет значения, что вы положили для метода BaseClass __call__, метод SubClass отменит его. Речь идет не о декораторах и не-декораторах, так как это функции (ну, действительно, атрибуты, так как на самом деле не очень много различий в python), которые наследуются. Декораторы смотрят отдельно от функции, но они на самом деле не являются - в приведенном выше, все, что делает декоратор, изменяет определение __call__ от something до decorator(something) (которое все равно должно возвращать вызываемый).

Затем, в подклассе, вы переназначаете эту функцию на нечто совершенно другое.

Вы можете использовать декоратор класса, но эти вещи, как вы заметили, имеют тенденцию нарушать самоанализ; они превращают класс в функцию, возвращающую класс. Аналогичная проблема с заводскими функциями.

Возможно, посмотрите на модуль profile? Я знаю, что есть некоторые крючки нижнего уровня, которые он использует для отслеживания вызовов функций и т. Д., Которые вы также можете использовать (хотя они мало знают о них).

+0

Это правильный ответ –

+0

К сожалению, мой версия 'trace' не является профилированием, а больше отслеживает данные через систему. –

+0

, я хочу сказать, что профилировщик решает аналогичную проблему - он должен обернуть/проследить все вызовы функций, чтобы он мог хранить start_time/stop_time для каждого вызова. вам нужно сделать аналогичную вещь, за исключением того, что вместо времени они будут регистрировать их и/или проверять аргументы для отслеживания потока данных. –

Смежные вопросы