开发者

Python: Anything wrong with dynamically assigning instance methods as instance attributes

I came up with the following code to decorate instance methods using a decorator that requires the instance itself as an argument:

from functools import wraps

def logging_decorator(tricky_instance):
    def wrapper(fn):
        @wraps(fn)
        def wrapped(*a, **kw):
            if tricky_instance.log:
                print("Calling %s.." % fn.__name__)
            return fn(*a, *开发者_开发技巧*kw)
        return wrapped
    return wrapper     

class Tricky(object):
    def __init__(self, log):
        self.log = log
        self.say_hi = logging_decorator(self)(self.say_hi)

    def say_hi(self):
       print("Hello, world!")


i1 = Tricky(log=True)
i2 = Tricky(log=False)

i1.say_hi()
i2.say_hi()

This seems to work great, but I fear that I may have overlooked some unintended side effects of this trick. Am I about to shoot myself in the foot, or is this safe?

Note that I don't actually want to use this for logging, it's just the shortest meaningful example I could come up with.


It's not really clear to me why you would ever want to do this. If you want to assign a new method type dynamically use types:

import types

class Tricky(object):
    def __init__(self):
        def method(self):
            print('Hello')
        self.method = types.MethodType(method, self)

If you want to do something with the instance, do it in the __init__ method. If you just want access to the method's instance inside the decorator, you can use the im_self attribute:

def decorator(tricky_instance):
    def wrapper(meth):
        print(meth.im_self == tricky_instance)
        return meth
    return wrapper

Personally, I think this is veering into Maybe-I-Shouldn't-Use-Decorators land.


I think I was trying to be needlessly smart. There seems to be an embarrassingly simpler solution:

from functools import wraps

def logging_decorator(fn):
    @wraps(fn)
    def wrapped(self, *a, **kw):
        if self.log:
            print("Calling %s.." % fn.__name__)
        return fn(self, *a, **kw)
    return wrapped

class Tricky(object):
    def __init__(self, log):
        self.log = log

    @logging_decorator
    def say_hi(self):
       print("Hello, world!")

i1 = Tricky(log=True)
i2 = Tricky(log=False)

i1.say_hi()
i2.say_hi()
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜