Как я могу добавить свойства в класс, используя декоратор, который принимает список имен в качестве аргумента? - PullRequest
6 голосов
/ 03 февраля 2012

Я хотел бы добавить многие фиктивные свойства к классу через декоратор, например:

def addAttrs(attr_names):
  def deco(cls):
    for attr_name in attr_names:
      def getAttr(self):
        return getattr(self, "_" + attr_name)
      def setAttr(self, value):
        setattr(self, "_" + attr_name, value)
      prop = property(getAttr, setAttr)
      setattr(cls, attr_name, prop)
      setattr(cls, "_" + attr_name, None) # Default value for that attribute
    return cls
  return deco

@addAttrs(['x', 'y'])
class MyClass(object):
  pass

К сожалению, декоратор, похоже, сохраняет ссылку attr_name вместо своего содержимого. Следовательно, MyClass.x и MyClass.y обращаются к обоим MyClass._y:

a = MyClass()
a.x = 5
print a._x, a._y
>>> None, 5
a.y = 8
print a._x, a._y
>>> None, 8

Что мне нужно изменить, чтобы получить ожидаемое поведение?

Ответы [ 2 ]

7 голосов
/ 03 февраля 2012

У вас почти все получилось.Есть только одна гнида.При создании внутренних функций привязайте текущее значение attr_name к функциям получения и установки:

def addAttrs(attr_names):
  def deco(cls):
    for attr_name in attr_names:
      def getAttr(self, attr_name=attr_name):
        return getattr(self, "_" + attr_name)
      def setAttr(self, value, attr_name=attr_name):
        setattr(self, "_" + attr_name, value)
      prop = property(getAttr, setAttr)
      setattr(cls, attr_name, prop)
      setattr(cls, "_" + attr_name, None) # Default value for that attribute
    return cls
  return deco

@addAttrs(['x', 'y'])
class MyClass(object):
  pass

Это дает ожидаемый результат:

>>> a = MyClass()
>>> a.x = 5
>>> print a._x, a._y
5 None
>>> a.y = 8
>>> print a._x, a._y
5 8

Надеюсь, это поможет.Счастливого украшения: -)

3 голосов
/ 03 февраля 2012

Python не поддерживает масштабирование на уровне блоков, только на уровне функций.Поэтому любая переменная, назначенная в цикле, будет доступна вне цикла как последнее доступное значение.Чтобы получить искомый результат, вам нужно использовать замыкание в цикле:

def addAttrs(attr_names):
  def deco(cls):
    for attr_name in attr_names:
      def closure(attr):
        def getAttr(self):
          return getattr(self, "_" + attr)
        def setAttr(self, value):
          setattr(self, "_" + attr, value)
        prop = property(getAttr, setAttr)
        setattr(cls, attr, prop)
        setattr(cls, "_" + attr, None)
      closure(attr_name)
    return cls
  return deco

Используя закрытие closure, атрибуты, назначенные в getAttr и setAttr, будут ограниченыправильно.

РЕДАКТИРОВАТЬ: исправлено отступ

...