我想在我的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()
建议?
答案 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"
字符串。