TL; DR Python 2.7.5, используя дескрипторы в качестве декораторов, есть ли способ передать аргументы (в метод __init__
)? OR Как получить доступ к атрибутам экземпляра класса с помощью декоратора метода с аргументами (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']
переходила в дескрипторе ... по умолчанию, 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.
UPDATE 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
Но на данный момент, арг [0]. просто возвращает арг:
(Pdb) args[0]
args = (<FormRecord object at 0x112f824d0>,)
kwargs = {}
(Pdb) args[0].genus_type
args = (<FormRecord object at 0x112f824d0>,)
kwargs = {}
Однако использование functools
как предложено делает работу - так что я буду награждать ответ. Кажется, есть черная магия в functools
, которая позволяет аргументы в?
UPDATE 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
. Спасибо!
Да, я попытался с декораторами, - но с декоратором, я считаю, что я не могу получить доступ к атрибутам этого экземпляра класса в ... – user
Конечно, самая большая проблема в том, что, когда вы декорирования метод, у вас нет доступа к классу, в котором находится метод? Вы можете получить доступ к 'self' (и, следовательно, проверить' isinstance (self, ...) 'или' type (self) ') в' wrapper' (в настоящее время 'args [0]', но вы можете сделать его явным), но вы не можете включить класс, который в настоящее время определяется в 'whitelist'. Как вы разрешите '' typeB'' для фактического класса? – jonrsharpe
Так что моя терминология может сбить с толку. Когда я использую «типB» в качестве примера выше, он определяется как атрибут экземпляра класса - например, B.genus_type = 'typeB'. Я надеюсь, что все может быть одним классом, поэтому type (A) == type (B), но A.genus_type! = B.genus_type. – user