2015-06-12 23 views
1

TL; DR Python 2.7.5,当使用描述符作为装饰器时,是否有任何方法可以传入参数(到__init__方法)? 或 如何使用带参数的方法装饰器(as here)访问类实例的属性? - 我认为这是不可能的,不过,因此低于重点描述符...带参数的Python描述符?


龙版

我有一个类的对象,不同的“类型”属性。基于一个实例的“类型”,我想要一个方法可用或不可用。我知道一种方法是创建多个类,但是我试图在创建这些对象时没有一堆if/else语句。例如,我有两个对象A和B几乎相同,除了对象B我不想让get_start_date()方法可用。所以基本上,我想要的是A和B都是类MyObjects的实例,但具有不同的“type”属性。

type(A) == type(B) 
A.genus_type != B.genus_type 

我会使用.genus_type属性来区分哪些方法是允许的,哪些不是......

我想我可以用装饰用白名单,如:

def valid_for(whitelist): 
    def wrap(f): 
     def wrapper(*args, **kwargs): 
      return f(*args, **kwargs) 
    return wrapper 
return wrap 

class A(object): 
    @valid_for(['typeB']) 
    def do_something_cool(self): 
     print 'blah' 

但问题是我没有访问装饰器中的实际类实例,我可以在其中测试实例类型属性。根据this SO question,我想,“我可以使用描述符!”。

于是,我又试图:

class valid_for(object): 
    """ descriptor to check the type of an item, to see 
    if the method is valid for that type""" 
    def __init__(self, func): 
     self.f = func 

    def __get__(self, instance, owner): 
     def wrapper(*args): 
      return self.f(instance, *args) 
     return wrapper 

但我无法弄清楚如何获得['typeB']参数传递到默认的描述......,巨蟒通过在调用的方法作为参数__init__。我可以为每种类型创建硬编码的描述符并嵌套它们,但是我不知道是否会碰到this problem。假设我可以克服嵌套问题,似乎也不太干净做这样的事情:

class A(object): 
    @valid_for_type_b 
    @valid_for_type_f 
    @valid_for_type_g 
    def do_something_cool(self): 
     print 'blah' 

做这样的事情只是让我func等于列表['typeB'] ...

class valid_for(object): 
    """ descriptor to check the type of an item, to see 
    if the method is valid for that type""" 
    def __init__(self, func, *args): 
     self.f = func 

    def __get__(self, instance, owner): 
     def wrapper(*args): 
      return self.f(instance, *args) 
     return wrapper 

class A(object): 
    @valid_for(['typeB']) 
    def do_something_cool(self): 
     print 'blah' 

和我func不在*args列表中,所以我不能只做一个简单的参数交换(*args为空)。

我一直在寻找提示herehere,但还没有找到任何看起来像干净或有效的解决方法。有没有一种干净的方式来做到这一点,还是我必须使用多个类,并混合各种方法?或者,现在我倾向于认为检查一个实例方法,但似乎不干净的,可重复使用...

class A(object): 
    def _valid_for(self, whitelist): 
     if self.genus_type not in whitelist: 
      raise Exception 

    def do_something_cool(self): 
     self._valid_for(['foo']) 
     print 'blah' 

我使用Python 2.7.5。


更新1

每在评论一个建议,我想:

def valid_for2(whitelist): 
    def wrap(f): 
     def wrapper(*args, **kwargs): 
      import pdb 
      pdb.set_trace() 
      print args[0].genus_type 
      return f(*args, **kwargs) 
     return wrapper 
    return wrap 

但在这一点上,ARGS [0]。只是返回参数:

(Pdb) args[0] 
args = (<FormRecord object at 0x112f824d0>,) 
kwargs = {} 
(Pdb) args[0].genus_type 
args = (<FormRecord object at 0x112f824d0>,) 
kwargs = {} 

但是,使用functools建议的工作 - 所以我会奖励答案。在functools似乎有一些黑魔法让参数在?


更新2

所以调查jonrsharpe的建议多,他的方法也似乎工作,但我要明确地使用self代替args[0]。不知道为什么行为是不同的...

def valid_for2(whitelist): 
    def wrap(f): 
     def wrapper(self, *args, **kwargs): 
      print self.genus_type 
      return f(*args, **kwargs) 
     return wrapper 
    return wrap 

产生相同的输出与functools。 谢谢!

+0

呀,我试图与装饰 - 但一个装饰,我相信我能不能访问这个类实例的属性... – user

+0

当然最大的问题是,当你装修方法,您无法访问方法所在的类?你可以在'wrapper'(它现在是'args [0]'''''''中测试'self'(因此测试'isinstance(self,...)'或'type(self)'),但是你可以明确地),但不能包含当前在“白名单”中定义的类。你将如何解决'typeB''到实际的课堂? – jonrsharpe

+0

所以我的术语可能会令人困惑。当我使用'typeB'作为上面的例子时,它被定义为类实例的一个属性 - 比如B.genus_type ='typeB'。我希望一切都可以是一个类,所以键入(A)==类型(B),但是A.genus_type!= B.genus_type。 – user

回答

2

如果我正确理解你的情况,你在找什么是a closure - 一个函数可以引用 外函数的本地名称空间。

既然你是在

@valid_for(['typeB']) 

传递['typeB']valid_for,因为我们应该valid_for一个功能返回一个装饰。 装饰器反过来接受一个函数(新生方法)作为输入并返回另一个函数(wrapper)。

低于wrapper是一个闭包,它可以在运行时在其主体内访问typelist的值 。


import functools 
def valid_for(typelist): 
    def decorator(func): 
     @functools.wraps(func) 
     def wrapper(self, *args): 
      if self.genus_type in typelist: 
       return func(self, *args) 
      else: 
       # handle this case 
       raise NotImplementedError(
        '{} not in {}'.format(self.genus_type, typelist)) 
     return wrapper 
    return decorator 

class A(object): 
    def __init__(self): 
     self.genus_type = 'typeA' 
    @valid_for(['typeB']) 
    def do_something_cool(self): 
     print 'blah' 

a = A() 
try: 
    a.do_something_cool() 
except NotImplementedError as err: 
    print(err) 
    # typeA not in ['typeB'] 

a.genus_type = 'typeB' 
a.do_something_cool() 
# blah