2015-04-15 3 views
1

Я пытаюсь создать декоратор python, который добавляет атрибуты методам класса, чтобы я мог получать и изменять эти атрибуты из самого метода. Код декоратораPython: Установить атрибут метода из метода

from types import MethodType 
class attribute(object): 

    def __init__(self, **attributes): 
     self.attributes = attributes 

    def __call__(self, function): 

     class override(object): 

      def __init__(self, function, attributes): 
       self.__function = function 
       for att in attributes: 
        setattr(self, att, attributes[att]) 

      def __call__(self, *args, **kwargs): 
       return self.__function(*args, **kwargs) 

      def __get__(self, instance, owner): 
       return MethodType(self, instance, owner) 

     retval = override(function, self.attributes) 
     return retval 

Я попробовал этот декоратор на следующем примере игрушек.

class bar(object): 

     @attribute(a=2) 
     def foo(self): 
      print self.foo.a 
      self.foo.a = 1 

Хотя я могу получить доступ к значению атрибута «а» внутри Foo(), я не могу установить его на другое значение. Действительно, когда я звоню bar().foo(), я получаю следующее свойство AttributeError.

AttributeError: 'instancemethod' object has no attribute 'a' 

Почему это? Что еще более важно, как я могу достичь своей цели?


Редактировать

Просто чтобы быть более точным, я пытаюсь найти простой способ реализации статической переменной, которые расположены в пределах методов класса. Продолжая из приведенного выше примера, я хотел бы создать экземпляр b = bar(), назовите оба метода foo() и doo(), а затем получите доступ к b.foo.a и b.doo.a.

class bar(object): 

     @attribute(a=2) 
     def foo(self): 
      self.foo.a = 1 

     @attribute(a=4) 
     def doo(self): 
      self.foo.a = 3 
+0

Мне было интересно, что такое прецедент такой конструкции? Поскольку вы хотите установить этот атрибут, почему бы не сделать его аргументом foo? – brainovergrow

+0

@brainovergrow, целью является сделать что-то эквивалентное статическим переменным в C++. – Francis

ответ

4

Лучший способ сделать это - не делать этого вообще.

Прежде всего, нет необходимости в декораторе атрибутов; вы можете просто назначить его самостоятельно:

class bar(object): 
    def foo(self): 
     print self.foo.a 
     self.foo.a = 1 
    foo.a = 2 

Однако это все еще встречает те же ошибки. Вам необходимо:

self.foo.__dict__['a'] = 1 

Вы можете вместо этого использовать метакласс ... но это быстро становится беспорядочным.

С другой стороны, существуют более чистые альтернативы.

Вы можете использовать по умолчанию:

def foo(self, a): 
    print a[0] 
    a[0] = 2 
foo.func_defaults = foo.func_defaults[:-1] + ([2],) 

Конечно, мой предпочтительный способ избежать этого совсем и использовать вызываемый класс ("функтор" в C++ слово):

class bar(object): 
    def __init__(self): 
     self.foo = self.foo_method(self) 
    class foo_method(object): 
     def __init__(self, bar): 
      self.bar = bar 
      self.a = 2 
     def __call__(self): 
      print self.a 
      self.a = 1 

Или просто использовать классический атрибуты класса:

class bar(object): 
    def __init__(self): 
     self.a = 1 
    def foo(self): 
     print self.a 
     self.a = 2 

Если это то, что вы хотите скрыть a п ROM производные классы, использовать любые частные атрибуты называются в терминологии Python:

class bar(object): 
    def __init__(self): 
     self.__a = 1 # this will be implicitly mangled as __bar__a or similar 
    def foo(self): 
     print self.__a 
     self.__a = 2 

EDIT: Вы хотите статические атрибуты?

class bar(object): 
    a = 1 
    def foo(self): 
     print self.a 
     self.a = 2 

EDIT 2: Если вы хотите статические атрибуты видны только текущую функцию, вы можете использовать PyExt «s modify_function:

import pyext 

def wrap_mod(*args, **kw): 
    def inner(f): 
     return pyext.modify_function(f, *args, **kw) 
    return inner 

class bar(object): 
    @wrap_mod(globals={'a': [1]}) 
    def foo(self): 
     print a[0] 
     a[0] = 2 

Это немного некрасиво и хак. Но это работает.

Моя рекомендация будет только использовать двойные подчеркивания:

class bar(object): 
    __a = 1 
    def foo(self): 
     print self.__a 
     self.__a = 2 

Хотя это является видимым для других функций, она невидима к чему-то еще (на самом деле, это есть, но это искаженное).

FINAL EDIT: Используйте это:

import pyext 

def wrap_mod(*args, **kw): 
    def inner(f): 
     return pyext.modify_function(f, *args, **kw) 
    return inner 

class bar(object): 
    @wrap_mod(globals={'a': [1]}) 
    def foo(self): 
     print a[0] 
     a[0] = 2 
    foo.a = foo.func_globals['a'] 

b = bar() 
b.foo() # prints 1 
b.foo() # prints 2 
# external access 
b.foo.a[0] = 77 
b.foo() # prints 77 
+0

@ kirbyfan64os, спасибо за ваш ответ. Идея состоит в том, чтобы иметь эквивалент статических переменных в C++. Хотя присвоение foo.a = 2 является изящным, единственный способ изменить атрибут a из метода, похоже, модифицирует словарь функций (self.foo .__ dict __ ['a'] = 1), который не является изящным. Разумная идея класса довольно приятна. Тем не менее я удивляюсь, почему язык python не реализует более сжатый способ сделать это. – Francis

+0

@Francis Это возможно в Python. Смотрите мои правки. – refi64

+0

@ kirbyfan64os, спасибо за это. Однако я бы хотел, чтобы статический атрибут находился на уровне метода. – Francis

1

В то время как Вы можете отпишусь, заменив self.foo.a = 1 с self.foo.__dict__['a'] = 1 это, как правило, не рекомендуется.

1

Если вы используете Python2 - (а не Python3) - всякий раз, когда вы извлекаете метод из экземпляра, создается новый объект instance method, который является оберткой исходной функции, определенной в классе.

Метод экземпляра является довольно прозрачным прокси-сервером для функции - вы можете получить через него атрибуты функции, но не устанавливать их, поэтому установка элемента в self.foo.__dict__ работает.

В качестве альтернативы вы можете добраться до самого объекта функции, используя: self.foo.im_func - атрибут методов экземпляра указывает базовую функцию.

0

Основываясь на ответах других вкладчиков, я придумал следующий обходной путь. Во-первых, оберните dictionnary в классе, разрешающем несуществующие атрибуты для завернутого словаря, такого как следующий код.

class DictWrapper(object): 
    def __init__(self, d): 
     self.d = d 
    def __getattr__(self, key): 
     return self.d[key] 

Кредиты Lucas Jones за этот код.

Затем нарисуйте декоратор addstatic с атрибутом statics, который сохранит атрибуты static.

class addstatic(object): 

    def __init__(self, **statics): 
     self.statics = statics 

    def __call__(self, function): 

     class override(object): 

      def __init__(self, function, statics): 
       self.__function = function 
       self.statics = DictWrapper(statics) 

      def __call__(self, *args, **kwargs): 
       return self.__function(*args, **kwargs) 

      def __get__(self, instance, objtype): 
       from types import MethodType 
       return MethodType(self, instance) 

     retval = override(function, self.statics) 
     return retval 

Следующий код является примером того, как addstatic декоратора может быть использован на методах.

class bar(object): 

     @attribute(a=2, b=3) 
     def foo(self): 
      self.foo.statics.a = 1 
      self.foo.statics.b = 2 

Затем, играя с экземпляром Вырабатывает bar класса:

>>> b = bar() 
>>> b.foo.statics.a 
2 
>>> b.foo.statics.b 
3 
>>> b.foo() 
>>> b.foo.statics.a 
3 
>>> b.foo.statics.b 
5 

Причина для использования этой статики dictionnary следует ответ jsbueno, который предположить, что то, что я хочу, потребуется перегрузить оператор точечного и метод экземпляра, обертывающий функцию foo, что я не уверен в этом.Конечно, атрибут метода может быть установлен в self.foo.__dict__, но поскольку он не рекомендуется (как было предложено brainovergrow), я придумал это обходное решение. Я не уверен, что это тоже будет рекомендовано, и я думаю, что это для комментариев.

Смежные вопросы