Как украсить объект методом? - PullRequest
1 голос
/ 10 мая 2011

Мне нужно украсить метод объекта. Это должно происходить во время выполнения, потому что декораторы, применяемые к объекту, зависят от аргументов, которые пользователь дал при вызове программы (аргументы, предоставляемые с argv), поэтому один и тот же объект может быть декорирован 3 раза, 2 раза или не декорирован в все.

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

Вот что я пробовал:

class GraphDecorator(object):
    def __init__(self, wrappee):
        self.wrappee = wrappee
    def method(self):
        # do my stuff here
        self.wrappee.method()
        # do more of stuff here
    def __getattr__(self,attr):
        return getattr(self.wrappee,attr)

И почему это НЕ работает: Это не сработало из-за способа, которым я создавал приложение, когда вызывался метод, который не существовал в моем классе Decorator, он возвращался к реализации декорированного класса, проблема в том, что приложение всегда начинало вызывать метод run это не нужно было украшать, поэтому использовался неокрашенный отступ, а внутри недекорированной формы он всегда вызывал недекорированные методы, мне нужно было заменить метод из объекта, а не проксировать его :

# method responsible to replace the undecorated form by the decorated one
def graphDecorator(obj):
    old_method = obj.method

    def method(self):
        # do my stuff here
        old_method()
        # do more of my stuff

    setattr(obj,'method',method) # replace with the decorated form

А вот моя проблема: оформленная форма не получает self, когда она вызывается, что приводит к ошибке TypeError из-за неправильного числа аргументов.

Ответы [ 4 ]

1 голос
/ 11 мая 2011

Проблема заключалась в том, что я не мог использовать func (self) в качестве метода. Причина в том, что метод setattr () не ограничивает функцию, а функция действует как статический метод, а не метод класса, благодаря интроспективной природе python, я смог придумать это решение:

def decorator(obj):
    old_func = obj.func # can't call 'by name' because of recursion

    def decorated_func(self):
        # do my stuff here
        old_func() # does not need pass obj
        # do some othere stuff here

    # here is the magic, this get the type of a 'normal method' of a class
    method = type(obj.func)

    # this bounds the method to the object, so self is passed by default 
    obj.func = method(decorated_func, obj)

Я думаю, что это лучший способ декорировать метод объекта во время выполнения, хотя было бы неплохо найти способ вызова method() напрямую, без строки method = type(obj.func)

0 голосов
/ 02 октября 2011

"Это должно быть во время выполнения, потому что декораторы, применяемые к объекту, зависят от аргументов, которые пользователь дал при вызове программы"

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

0 голосов
/ 11 мая 2011

Мне нужно украсить метод объекта. Это должно происходить во время выполнения, потому что декораторы, применяемые к объекту, зависят от аргументов, которые пользователь дал при вызове программы (аргументы, предоставляемые с argv), поэтому один и тот же объект может быть декорирован 3 раза, 2 раза или не декорирован в все.

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

import sys
args = sys.argv[1:]

class MyClass(object):
    pass

if args[0]=='--decorateWithFoo':
    MyClass = decoratorFoo(MyClass)
if args[1]=='--decorateWithBar'
    MyClass = decoratorBar(MyClass)

Синтаксис:

@deco
define something

Это то же самое, что и:

define something
something = deco(something)

Вы можете также сделать фабрику декораторов @makeDecorator(command_line_arguments)

0 голосов
/ 10 мая 2011

Возможно, вы захотите использовать __getattribute__ вместо __getattr__ (последний вызывается только в случае сбоя «стандартного» поиска):

class GraphDecorator(object):
    def __init__(self, wrappee):
        self.__wrappee = wrappee

    def method(self):
        # do my stuff here
        self.wrappe.method()
        # do more of stuff here

    def __getattribute__(self, name):
        try:
            wrappee = object.__getattribute__(self, "_GraphDecorator__wrappee")
            return getattr(wrappee, name)
        except AttributeError:
            return object.__getattribute__(self, name)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...