TL; DR Python 2.7.5, при использовании дескрипторов в качестве декораторов, есть ли способ передать аргументы (в метод __init__
)? ИЛИ Как я могу получить доступ к атрибутам экземпляра класса, используя декоратор метода с аргументами (как здесь)? -- Однако я считаю, что это невозможно, поэтому основное внимание ниже уделяется дескрипторам...
Длинная версия
У меня есть класс объектов с разными атрибутами типа. Основываясь на «типе» экземпляра, я хотел бы, чтобы метод был доступен или нет. Я знаю, что один из способов - создать несколько классов, но я стараюсь не использовать кучу операторов 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'
Но проблема была в том, что у меня не было доступа к фактическому экземпляру класса в декораторе, где я мог бы проверить атрибут типа экземпляра. Основываясь на этом вопросе SO, Я подумал: «Я могу использовать дескрипторы!».
Итак, я попробовал:
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__
. Я мог бы создать жестко закодированные дескрипторы для каждого типа и вложить их, но потом мне интересно, столкнусь ли я с эта проблема. Предполагая, что я смог преодолеть проблему вложенности, также кажется менее чистым сделать что-то вроде:
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
пусто).
Я искал подсказки здесь и здесь, но не нашел ничего похожего на чистый или допустимый обходной путь. Есть ли чистый способ сделать это, или мне нужно использовать несколько классов и просто смешивать различные методы? Или прямо сейчас я склоняюсь к методу экземпляра, который проверяет, но он кажется менее чистым и пригодным для повторного использования...
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
. Спасибо!
self
(и, следовательно, проверитьisinstance(self, ...)
илиtype(self)
) вwrapper
(в настоящее время этоargs[0]
, но вы можете сделать его явным), но вы не можете включить класс, который в настоящее время определяется вwhitelist
. Как вы разрешите'typeB'
в фактический класс? - person jonrsharpe   schedule 12.06.2015args[0].genus_type in whitelist
внутриwrapper
? - person jonrsharpe   schedule 12.06.2015self
вместоargs[0]
... - person user   schedule 12.06.2015