将自身传递给装饰器对象
Pass self to decorator object
有这样的装饰器对象
class wait_for_page_load(object):
def __init__(self, driver, time_to_wait=20):
self.driver = driver
self.time_to_wait = time_to_wait
def __call__(self, function):
@functools.wraps(function)
def wrapper(*args):
old_page = self.driver.find_element_by_tag_name('html')
function(*args)
WebDriverWait(self.driver, self.time_to_wait).until(staleness_of(old_page))
return wrapper
我想将它应用于另一个 class 的方法,像这样:
class VehiclePage(object):
def __init__(self, driver):
self.driver = driver
@wait_for_page_load(self.driver)
def open(self):
self.driver.get('%s/vehicles/' % BASE_URL)
这给了我一个错误。有没有办法将 self.driver
传递给装饰器?
简答:不,没有。
长答案:
driver
属性是在实例化 class 时设置的。但是,当 class 被解释时,装饰器是 运行 。也就是说,当解释器在加载模块时首先读取它。此时,您还没有准备好任何实例。要执行此类操作,您将不得不重构代码。
此外,即使这样做有效,您最终也会对所有对象使用装饰器的单个实例 class。可能不是你所期望的。
不过,一个简单的解决方法是在 __init__
中应用装饰器。虽然不是很优雅,但如果你真的需要应用装饰器,那将是可行的。
def __init__(self, driver):
self.driver = driver
self.open = wait_for_page_load(self.driver)(self.open)
但是我相信您需要通过调用 types.MethodType 将包装器绑定到 class - 老实说,重新组织代码可能更好。
您不必将 self
传递给装饰器对象。如果装饰器 returns 一个函数,那么该函数将在调用时访问 self
。例如
def pass_value(function):
def wrapper(self):
function(self, self.value)
return wrapper
class Printer(object):
def __init__(self, value):
self.value = value
@pass_value
def print_(self, v):
print v
Printer("blah").print_()
这种方法的一个问题是它需要 self
实现一个特定的接口(比如有一个字段叫做 driver
,而不是直接将驱动程序传递给装饰器)。
你的装饰器会变成:
def wait_for_page_load(time_to_wait=20):
def decorator(function):
@functools.wraps(function)
def wrapper(self, *args):
old_page = self.driver.find_element_by_tag_name('html')
function(self, *args)
WebDriverWait(self.driver, time_to_wait).until(staleness_of(old_page))
return wrapper
return decorator
用作:
@wait_for_page_load() # brackets are needed
def open(self):
...
有这样的装饰器对象
class wait_for_page_load(object):
def __init__(self, driver, time_to_wait=20):
self.driver = driver
self.time_to_wait = time_to_wait
def __call__(self, function):
@functools.wraps(function)
def wrapper(*args):
old_page = self.driver.find_element_by_tag_name('html')
function(*args)
WebDriverWait(self.driver, self.time_to_wait).until(staleness_of(old_page))
return wrapper
我想将它应用于另一个 class 的方法,像这样:
class VehiclePage(object):
def __init__(self, driver):
self.driver = driver
@wait_for_page_load(self.driver)
def open(self):
self.driver.get('%s/vehicles/' % BASE_URL)
这给了我一个错误。有没有办法将 self.driver
传递给装饰器?
简答:不,没有。
长答案:
driver
属性是在实例化 class 时设置的。但是,当 class 被解释时,装饰器是 运行 。也就是说,当解释器在加载模块时首先读取它。此时,您还没有准备好任何实例。要执行此类操作,您将不得不重构代码。
此外,即使这样做有效,您最终也会对所有对象使用装饰器的单个实例 class。可能不是你所期望的。
不过,一个简单的解决方法是在 __init__
中应用装饰器。虽然不是很优雅,但如果你真的需要应用装饰器,那将是可行的。
def __init__(self, driver):
self.driver = driver
self.open = wait_for_page_load(self.driver)(self.open)
但是我相信您需要通过调用 types.MethodType 将包装器绑定到 class - 老实说,重新组织代码可能更好。
您不必将 self
传递给装饰器对象。如果装饰器 returns 一个函数,那么该函数将在调用时访问 self
。例如
def pass_value(function):
def wrapper(self):
function(self, self.value)
return wrapper
class Printer(object):
def __init__(self, value):
self.value = value
@pass_value
def print_(self, v):
print v
Printer("blah").print_()
这种方法的一个问题是它需要 self
实现一个特定的接口(比如有一个字段叫做 driver
,而不是直接将驱动程序传递给装饰器)。
你的装饰器会变成:
def wait_for_page_load(time_to_wait=20):
def decorator(function):
@functools.wraps(function)
def wrapper(self, *args):
old_page = self.driver.find_element_by_tag_name('html')
function(self, *args)
WebDriverWait(self.driver, time_to_wait).until(staleness_of(old_page))
return wrapper
return decorator
用作:
@wait_for_page_load() # brackets are needed
def open(self):
...