Python类中的内部装饰器

时间:2016-05-07 16:52:45

标签: python python-3.x oop decorator python-decorators

我想在我的Python类中使用它创建一个内部装饰器来包装selenium帧切换进出操作。所以我尝试了这个:

class MyPage(object):
    # ...
    def framed(self, frame_query: str):
        def decorator(function):
            @functools.wraps(function)
            def wrapper(*args, **kwargs):
                frame = self.driver.find_element(By.XPATH, frame_query)
                self.driver.switch_to.frame(frame)
                out = function(*args, **kwargs)
                self.driver.switch_to.default_content()
                return out
            return wrapper
        return decorator
    # ...
    @framed('//myxpath/iframe')
    def framed_function(self):
        # ...

但后来我收到了这个错误:

TypeError: framed() missing 1 required positional argument: 'frame_query'

显然它期待2个参数,包括self,但在装饰器上下文中它并不知道关于self的任何内容,所以我必须在'framed_function'中定义和内部函数,使得解决方案不那么优雅:

# My workaround
def framed_function(self):
    @framed('//myxpath/iframe')
    def actual_framed():
        #...
    actual_framed()

建议?

1 个答案:

答案 0 :(得分:2)

为类定义方法时,还没有类对象。当然没有该类的实例,因此self无法绑定到 。相反,方法在实例对象上查找时绑定到实例,请参阅Python descriptor HOWTO。只需从装饰器返回一个新的函数对象,而不是绑定它。

此外,Python没有隐私模型,因此没有“内部”的概念(对于类或方法或任何东西)。只需将装饰器放在类之外,这样可以更容易维护和重用,并避免污染类API。

这意味着您的wrapped()函数将成为一个方法,并在绑定时传递self参数。使用它,并将其显式传递给包装的函数对象(因为该函数未绑定):

def framed(frame_query: str):
    def decorator(function):
        @functools.wraps(function)
        def wrapper(self, *args, **kwargs):
            frame = self.driver.find_element(By.XPATH, frame_query)
            self.driver.switch_to.frame(frame)
            out = function(self, *args, **kwargs)
            self.driver.switch_to.default_content()
            return out
        return wrapper
    return decorator

class MyPage(object):
    @framed('//myxpath/iframe')
    def framed_function(self):

因此framed()会返回decorator(),而framed_function()又用于装饰wrapper();这样做会返回MyPage,这会添加到生成的实际MyPage()类对象中。在instance.framed_function()的实例上,访问wrapper()会将self绑定到该实例,以便instance绑定到该wrapper对象。在self之内,您可以访问function实例,以及原始frame_query对象(未绑定)和id="navbar"字符串。