2016-05-07 3 views
0

Я хочу создать внутренний декоратор, использующий его внутри моего класса Python, для того, чтобы обернуть операции переключения и переключения селена. Так что я попытался это:Внутренние декораторы в классе Python

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 параметров, включая себя, но в контексте декоратора он ничего о себе не знает, так что я должен был определить и внутренняя функция внутри'framed_function' делает решение гораздо менее элегантное:

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

предложение?

ответ

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.

+0

Работает, но определенно сложно, когда вы не знаете, как декораторы добавляются к экземпляру объекта (мой отказ), я рассмотрю это чтение, на которое вы ссылались, чтобы больше узнать об этом. – gerosalesc