2009-05-02 4 views
2

Допустим, у нас есть метакласса CallableWrappingMeta который ходит тело нового класса, обернув его методы с классом, InstanceMethodWrapper:., вызываемый как instancemethod?

import types 

class CallableWrappingMeta(type): 
    def __new__(mcls, name, bases, cls_dict): 
     for k, v in cls_dict.iteritems(): 
      if isinstance(v, types.FunctionType): 
       cls_dict[k] = InstanceMethodWrapper(v) 
     return type.__new__(mcls, name, bases, cls_dict) 

class InstanceMethodWrapper(object): 
    def __init__(self, method): 
     self.method = method 
    def __call__(self, *args, **kw): 
     print "InstanceMethodWrapper.__call__(%s, *%r, **%r)" % (self, args, kw) 
     return self.method(*args, **kw) 

class Bar(object): 
    __metaclass__ = CallableWrappingMeta 
    def __init__(self): 
     print 'bar!' 

Наш манекен обертка просто печатает аргументы, как они приходят в Но Отметьте что-то заметное: метод не передается получателем экземпляра объекта, потому что хотя InstanceMethodWrapper вызываем, он не рассматривается как функция с целью преобразования в метод экземпляра во время создания класса (после того, как наш метакласс сделано с ним).

Потенциальным решением является использование декоратора вместо класса для обертывания методов - эта функция станет методом экземпляра. Но в реальном мире InstanceMethodWrapper намного сложнее: он предоставляет API и публикует события вызова метода. Класс более удобен (и более результативен, но это не так важно).

Я также пробовал некоторые тупики. Подклассификация types.MethodType и types.UnboundMethodType не шла никуда. Немного интроспекции, и кажется, что они делятся с type. Поэтому я попытался использовать как метакласс, но и не повезло. Может быть, у них есть особые требования в качестве метакласса, но, похоже, на данный момент мы находимся на недокументированной территории.

Любые идеи?

+0

Не могли бы вы объяснить больше? Мне трудно понять, что вы пытаетесь сделать. – Unknown

ответ

3

Просто обогатить вас InstanceMethodWrapper класс с __get__ (который вполне может просто return self) - то есть, сделать этот класс в дескриптор типа, так что его экземпляры являются объектами дескрипторов. См. http://users.rcn.com/python/download/Descriptor.htm для фона и деталей.

BTW, если вы используете Python 2.6 или лучше, рассмотрите использование класса-декоратора вместо этого метакласса - мы добавили декораторы классов именно потому, что так много метаклассов использовались только для таких целей украшения, а декораторы действительно гораздо проще в использовании.

0

Редактировать: Я снова лежу. Атрибуты __?attr__ для функций читаются только, но, по-видимому, не всегда вызывают исключение AttributeException при назначении? Не знаю. В исходную точку!

Редактировать: Это фактически не решает проблему, так как функция обертки не будет запрашивать атрибуты прокси на InstanceMethodWrapper. Я мог бы, конечно, укусить атрибуты __?attr__ в декораторе - и это то, что я сейчас делаю, но это уродливо. Лучшие идеи очень приветствуются.


Конечно, я сразу понял, что сочетание простого декоратора с нашими классами будет делать трюк:

def methodize(method, callable): 
    "Circumvents the fact that callables are not converted to instance methods." 
    @wraps(method) 
    def wrapper(*args, **kw): 
     return wrapper._callable(*args, **kw) 
    wrapper._callable = callable 
    return wrapper 

Затем вы добавляете декоратор к вызову InstanceMethodWrapper в метаклассе:

cls_dict[k] = methodize(v, InstanceMethodWrapper(v)) 

Пуф. Немного наклонный, но он работает.

0

Я предполагаю, что вы пытаетесь создать метакласс, который обертывает каждый метод в классе с помощью специальной функции.

Вот моя версия, которая, я думаю, немного менее наклонная.

import types 

class CallableWrappingMeta(type): 
    def __new__(mcls, name, bases, cls_dict): 
     instance = type.__new__(mcls, name, bases, cls_dict) 
     for k in dir(instance): 
      v = getattr(instance, k) 
      if isinstance(v, types.MethodType): 
       setattr(instance, k, instanceMethodWrapper(v)) 

     return instance 

def instanceMethodWrapper(function): 
    def customfunc(*args, **kw): 
     print "instanceMethodWrapper(*%r, **%r)" % (args, kw) 
     return function(*args, **kw) 
    return customfunc 

class Bar(object): 
    __metaclass__ = CallableWrappingMeta 

    def method(self, a, b): 
     print a,b 

a = Bar() 
a.method("foo","bar") 
0

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

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

  • Нет связанных вопросов^_^