2013-10-18 109 views
2

我在与装饰的第一个经验,我创建了一个装饰类,用于过滤目标函数的结果,在默认情况下会返回一个特定的顺序:参数传递给装饰器时调用装饰功能

class Filter(object): 
    def __init__(self, id=None): 
     self.id = id 

    def __call__(self, func): 
     def wrapper(*args): 
      entity_ids = func(*args) 
      result = {} 
      for k, v in entity_ids.items(): 
       if self.id: 
        if '_' + str(self.id) in k: 
         result.update({k: v}) 
      return result 
     return wrapper 

我用的装饰在一些其他类的方法是这样的:

class SomeClass(object): 
    @Filter(id=None) 
    def get_ids(*args): 
     return result_sequence 

我如何定义的论据,装饰调用类方法时:

>>>sc = SomeClass() 
>>>sc.get_ids(*args) # I want to pass the id kwarg for Filter here 

在此先感谢

回答

2

马丁的回答是详尽的...或者差不多。如果你想调用的函数装饰你可以使用关键字ARG做到这一点,即当要能够覆盖“ID”的说法:

class Filter(object): 
    def __init__(self, id=None): 
     self.id = id 

    def __call__(self, func): 
     def wrapper(*args, **kwargs): 
      id = kwargs.get("id", self.id) 
      entity_ids = func(*args) 
      result = {} 
      for k, v in entity_ids.items(): 
       if id: 
        if '_' + str(id) in k: 
         result.update({k: v}) 
      return result 
     return wrapper 

但请注意,这意味着 1.你将不得不通过'id'作为关键字参数,如果你想过载默认的 2.你WONT能够通过kargs装饰功能的方式(但你不会传递kargs)

作为一个附注(和这里稍微有点OT但是......),你的包装功能的实现可能会有所改进:

 def wrapper(*args, **kwargs): 
      id = kwargs.get("id", self.id) 
      if not id: 
       # no need to go further 
       return {} 

      id = "_%s" % id 
      entity_ids = func(*args) 
      result = dict(
       (k, v) for k, v in entity_ids.items() 
       if id in k 
       ) 
      return result 
3

您在应用类定义的Filter装饰;它是您在id参数传递:

@Filter(id=None) 

如果id应该是别的东西,你需要通过在那里的价值。

Filter()对象在@Filter(id=None)线创建的,则调用。您也可以将代码重写为:

class SomeClass(object): 
    def get_ids(*args): 
     return result_sequence 
    get_ids = Filter(id=None)(get_ids) 

因为这是Python在处理装饰器时所做的。

这是Filter.__call__()方法,取代get_ids,可以不再指定参数,以当时的Filter()对象的返回值SomeClass.get_ids()现在是您的装饰器返回的嵌套wrapper()函数。

如果要在调用装饰方法时指定id,则需要更改wrapper()签名以接受(可选)额外的id参数。因为你已经支持*args,你唯一的选择就是增加一个**kwargs包罗万象的论据来支持可选关键字参数:

def wrapper(*args, **kwargs): 
    id = kwargs.get('id', self.id) 
    entity_ids = func(*args) 
    result = {} 
    for k, v in entity_ids.items(): 
     if id: 
      if '_' + str(id) in k: 
       result.update({k: v}) 
    return result 

在这里,而不是直接使用self.id,一个id关键字参数在包装件覆盖id值设置在装饰类:

sc.get_ids(*args, id='foo') 

您可能想要将任何关键字参数传递给包装函数;在这种情况下,我会使用:

def wrapper(*args, **kwargs): 
    id = kwargs.pop('id', self.id) 
    entity_ids = func(*args, **kwargs) 
    result = {} 
    for k, v in entity_ids.items(): 
     if id: 
      if '_' + str(id) in k: 
       result.update({k: v}) 
    return result 

这里,id关键字参数是传递剩余的关键字参数被包装的函数之前删除

0

你不得不改变你的包装:

class Filter(object): 
    def __init__(self, id=None): 
     self.id = id 

    def __call__(self, func): 
     def wrapper(*args, **kwargs): 
      id = kwargs.get('id', self.id) 
      entity_ids = func(*args) 
      result = {} 
      for k, v in entity_ids.items(): 
       if self.id: 
        if '_' + str(self.id) in k: 
         result.update({k: v}) 
      return result 
     return wrapper 

这给你一个机会,在调用包装方法的时候给一个替代id值:

class SomeClass(object): 
    @Filter(id=None) 
    def get_ids(*args): 
     return result_sequence 

Filter对象__call__方法现在返回一个包含额外的id kwarg的包装。因此,您可以覆盖在类定义时给出的id值:

sc = SomeClass() 
sc.get_ids(*args, id='other') # I want to pass the id kwarg for Filter here 
+1

def wrapper(* args,id = None):引发SyntaxError:无效语法 –

+0

@FrancescoDellaVedova Thx,已更正。 – glglgl