装饰方法 - 如何以oo方式而不是程序方式调用方法

时间:2012-08-01 15:36:53

标签: python oop decorator

关注earlier thread我的问题是如何使用此表达式:fn(self,*args, **kwargs并以此方式调用它self.fn(...)这是我的整个程序,其中包含失败的行评论出:

def formatHeader(fn):
    def wrapped(*args, **kwargs):
        print "here is args prior to extraction - {0}".format(args)
        if len(args) > 1:
            self,args = args  # remove self from args
        else:
            self, args= args[0], ()
        print("Here are the arguments after extraction: {0} {1}".format(self, args))
        #return '<div class="page_header">' + self.fn(*args, **kwargs)+'</div>'
        return '<div class="page_header">' + fn(self,*args, **kwargs)+'</div>'
    return wrapped    

class MyPage(object):
    def __init__(self):
        self.PageName = ''

    def createPage(self):
        pageHeader = self.createHeader()
        return pageHeader

    def addem(self, a, b):
        return a + b

    @formatHeader   #<----- decorator
    def createHeader(self):
        return "Page Header " + self.PageName


obj = MyPage()

print obj.createHeader()

2 个答案:

答案 0 :(得分:3)

首先,该范围内不存在selfself是一个标签,按惯例引用类的实例方法中的当前实例。其次,装饰器并不意味着要知道它们正在包装的函数的实例(至少在默认情况下)。该方法绑定到实例,并且装饰器对绑定方法进行操作,该方法作为可调用的黑盒子传递。如果你想从装饰器中访问实例成员(你不应该这样做,因为它实际上会破坏oo),你必须将实例传递给装饰器,这意味着将装饰器封闭在另一个封闭器中,或者使用内省从装饰器代码中动态发现拥有的实例。

答案 1 :(得分:1)

问题是你的包装器想要访问它所应用的实例的另一个方法,但它直到运行时才传递 - 而不是类定义时间,即装饰器运行时)。基本上你需要一个特定于实例的方法装饰器。您可以通过使装饰器成为class method decorator using instancePythonDecoratorLibrary配方中描述的描述符来实现此目的。

这是应用于您的示例代码(有一些外观变化):

from functools import wraps

def formatHeader(fn):

    class Descriptor(object):
        def __init__(self, fn):
            self.fn = fn

        def __get__(self, instance, klass):
            if instance is None:  # Class method was requested
                return self.make_unbound(klass) #  will raise TypeError
            return self.make_bound(instance)

        def make_unbound(self, klass):
            @wraps(self.fn)
            def wrapped(*args, **kwargs):
                '''This documentation will vanish :)'''
                raise TypeError(
                    'unbound method {}() must be called with {} instance '
                    'as first argument (got nothing instead)'.format(
                        self.fn.__name__, klass.__name__)
                )
            return wrapped

        def make_bound(self, instance):
            @wraps(self.fn)
            def wrapped(*args, **kwargs):
                '''This documentation will disapear :)'''
                return ('<div class="page_header">' +
                        self.fn(instance, *args, **kwargs) + '</div>')

            # This instance does not need the descriptor anymore,
            # let it find the wrapped method directly next time:
            setattr(instance, self.fn.__name__, wrapped)
            return wrapped

    return Descriptor(fn)

class MyPage(object):
    def __init__(self):
        self.PageName = ''

    def createPage(self):
        pageHeader = self.createHeader()
        return pageHeader

    def addem(self, a, b):
        return a + b

    @formatHeader   #<----- decorator
    def createHeader(self):
        return "Page Header " + self.PageName


obj = MyPage()

print obj.createHeader()

请注意,嵌套Descriptor类方法中的self参数引用Descriptor类的实例,而不是其方法被包装的类(代码中为instance)。