2014-11-06 2 views
0

Возможно, название немного вводит в заблуждение, однако я хотел создать простой декоратор, чтобы украсить некоторые методы класса как «разрешенные» в механизме RPC, но я застрял на странной ошибке при попытке доступа к переменным класса (Python 2.7.5). Проверьте код ниже:Класс Python не в глобалях при использовании декоратора

class myclass(): 

    rpcallowedmethods = [] 

    def __init__(self):    
     pass 

    def rpcenabled(fn): 
     print fn 
     print globals() 
     print myclass 

    @rpcenabled 
    def somefunc(self,param): 
     pass 

c = myclass() 

Исключение: NameError: global name 'myclass' is not defined

Любой человек может объяснить причину этого мне?

EDIT: Что я прошу, это больше о том, что python выполняет декоратор, определенный в классе, и работает против декорированных методов класса, даже до того, как был класс в глобальных переменных, поэтому я полагал, что это скорее логическая ошибка "в реализации python, чем кажущееся очевидным NameError

+2

Декоратор 'rpcenabled' не имеет смысла. Прежде всего, это не должно быть определено _inside_ вашего класса, во-вторых, «NameError» исходит из оператора печати _within_ вызов 'rpcenabled', поскольку' myclass' еще не определен, когда выполняется декоратор (который во время _creation_ объекта класса - ваш класс компилируется и еще не существует). Ваш 'print globals()' должен был показать вам это. Что вы на самом деле хотите, чтобы декоратор сделал? – l4mpi

+0

Я не знал, что у python есть такой селективный синтаксический анализатор, поскольку он уже был в определении класса, я думал, что он должен был проанализировать его перед выполнением любых декорированных методов ... –

+0

Ну, парсер в основном работает так, как при создании классов : он создает пустой объект класса, принимает весь код, принадлежащий определению класса, заполняет объект класса, выполняя все инструкции на уровне класса (создание функций, выполнение декораторов функций, назначение переменных класса), передает заполненный объект класса в любой класс декораторы и _then_ присваивает результат названию класса в области surronding.Это назначение - последний шаг в этом процессе. В вашем конкретном случае просто передайте свой 'rpcallowedmethods' список декоратору вместо использования класса. – l4mpi

ответ

1

Фактический объект класса присваивается только его имени после его определение закончено. Таким образом, вы не можете использовать имя класса во время его определения. Вы можете либо создать декоратор вне класса, к которому вы явно передать список, который вы хотите заполнить, или используйте следующую команду:

class myclass(): 
    rpcmethods = [] 

    def _rpcallowed(fct, l=rpcmethods): 
     l.append(fct) 
     return fct 

    @_rpcallowed 
    def myfct(): pass 

Обратите внимание, что параметр по умолчанию (l=rpcmethods) обходной путь, как вы не можете получить доступ к переменная класса внутри функции без ссылки на класс или экземпляр.

Вариант с декоратором за пределами класса, вероятно, квалифицируется как «более чистый», чем этот, поскольку он явный и многоразовый, но это будет немного больше кода и менее конкретным.

+0

Это отлично поработало, после некоторого тестирования с полной реализацией нескольких классов это станет самым простым и простым решением, спасибо. –

0

Странная ошибка?

print myclass 

вызвало ошибку. Вы не можете использовать имя myclass в своем определении ...

+0

Печать была добавлена, чтобы продемонстрировать проблему, в действительности код был больше похож на 'myclass.rpcallowedmethods.append (fn .__ name __)' –

+0

@MartinoDino. Если вы хотите использовать класс, определяемый '@ classmethod' и его первый аргумент это 'cls', который является классом. – laike9m

1

Вы злоупотребляете декораторами. Декоратор предназначен для добавления чего-то к предмету. «украшая» это как-то.

Более обычный способ сделать что-то подобное - это украсить как метод, так и класс. Метаклассы - это еще один способ решить эту проблему. Они более мощные, но слишком переполнены для вашей текущей проблемы. Тем не менее, прямое украшение функций может быть все, что вам нужно сделать. И сохраните сопоставление функций rpc для прокси-сервера.

from types import FunctionType 

def enable_rpc(func): 
    func.rpc_enabled = True 
    return func 

def rpc_enabled_class(cls): 
    functions = [attr for attr in vars(cls).values() 
     if isinstance(attr, FunctionType)] 
    cls._rpc_enabled_methods = [ 
     func for func in functions 
      if getattr(func, "rpc_enabled", False) 
    ] 
    return cls 

@rpc_enabled_class 
class SampleClass(object): 

    @enable_rpc 
    def my_func(self): 
     pass 

print(SampleClass._rpc_enabled_methods)