Вопрос

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

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.Поэтому я попробовал использовать оба в качестве метакласса, но и здесь безуспешно.Возможно, у них как у метакласса есть особые требования, но, похоже, на данный момент мы находимся на недокументированной территории.

Есть идеи?

Это было полезно?

Решение

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

Кстати, если вы используете Python 2.6 или более позднюю версию, рассмотрите возможность использования декоратора класса вместо этого метакласса — мы добавили декораторы классов именно потому, что так много метаклассов использовалось именно для таких целей оформления, а декораторы действительно намного проще использовать. .

Другие советы

Редактировать:Я в очередной раз лгу.А __?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))

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

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

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

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")

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top