【发布时间】:2015-08-28 20:40:59
【问题描述】:
TL;DR
Python 2.7.5,当使用描述符作为装饰器时,有什么方法可以传入参数(给__init__ 方法)?
要么
如何使用带参数的方法装饰器 (as here) 访问类实例的属性? -- 不过,我认为这是不可能的,因此下面将重点放在描述符上......
加长版
我有一类对象,具有不同的“类型”属性。根据实例的“类型”,我想要一种方法是否可用。我知道一种方法是创建多个类,但我试图在创建这些对象时不要有一堆 if / else 语句。例如,我有两个几乎相同的对象 A 和 B,除了对象 B 我不想让 get_start_date() 方法可用。所以本质上,我想要的是 A 和 B 都是 MyObjects 类的实例,但具有不同的“类型”属性。
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'] 参数传递到描述符中……默认情况下,Python 将调用的方法作为参数传递给__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 为空)。
我一直在寻找提示 here 和 here,但没有找到任何看起来像干净或有效的解决方法。有没有一种干净的方法可以做到这一点,还是我必须使用多个类并混合各种方法?或者,现在我倾向于使用检查的实例方法,但这似乎不太干净和可重用......
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
根据 cmets 中的建议,我尝试了:
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 相同的输出。
谢谢!
【问题讨论】:
-
是的,我尝试过使用装饰器——但使用装饰器时,我相信我无法访问该类实例的属性...
-
当然最大的问题是,当你装饰方法时,你无法访问该方法所在的类?您可以在
wrapper中访问self(因此测试isinstance(self, ...)或type(self))(当前为args[0],但您可以明确表示),但您不能包含当前正在使用的类在whitelist中定义。您将如何将'typeB'解析为实际课程? -
所以我的术语可能会令人困惑。当我使用 'typeB' 作为上面的示例时,它被定义为类实例上的一个属性——比如 B.genus_type = 'typeB'。我希望一切都可以是一个类,所以 type(A) == type(B),但是 A.genus_type != B.genus_type。
-
那么你为什么不检查一下例如
args[0].genus_type in whitelist里面wrapper? -
嗯,所以我不太了解为什么这不起作用,但我会在上面粘贴结果。基本上它只是返回 arg 列表?
标签: python python-2.7 python-decorators descriptor