Python – метакласс – добавление свойств

Я хотел бы определить метакласс, который позволит мне создавать свойства (т.е. setter, getter) в новом классе на основе атрибутов класса.

Например, я хотел бы определить класс:

class Person(metaclass=MetaReadOnly): name = "Ketty" age = 22 def __str__(self): return ("Name: " + str(self.name) + "; age: " + str(self.age)) 

Но я хотел бы получить что-то вроде этого:

 class Person(): __name = "Ketty" __age = 22 @property def name(self): return self.__name; @name.setter def name(self, value): raise RuntimeError("Read only") @property def age(self): return self.__age @age.setter def age(self, value): raise RuntimeError("Read only") def __str__(self): return ("Name: " + str(self.name) + "; age: " + str(self.age)) 

Вот метакласс, который я написал:

 class MetaReadOnly(type): def __new__(cls, clsname, bases, dct): result_dct = {} for key, value in dct.items(): if not key.startswith("__"): result_dct["__" + key] = value fget = lambda self: getattr(self, "__%s" % key) fset = lambda self, value: setattr(self, "__" + key, value) result_dct[key] = property(fget, fset) else: result_dct[key] = value inst = super(MetaReadOnly, cls).__new__(cls, clsname, bases, result_dct) return inst def raiseerror(self, attribute): raise RuntimeError("%s is read only." % attribute) 

Однако он не работает должным образом.

 client = Person() print(client) 

Иногда я получаю:

 Name: Ketty; age: Ketty 

иногда:

 Name: 22; age: 22 

или даже ошибка:

 Traceback (most recent call last): File "F:\Projects\TestP\src\main.py", line 38, in <module> print(client) File "F:\Projects\TestP\src\main.py", line 34, in __str__ return ("Name: " + str(self.name) + "; age: " + str(self.age)) File "F:\Projects\TestP\src\main.py", line 13, in <lambda> fget = lambda self: getattr(self, "__%s" % key) AttributeError: 'Person' object has no attribute '____qualname__' 

Я нашел пример, как это можно сделать другим способом ( классы Python: динамические свойства ), но я хотел бы сделать это с помощью метакласса. Вы знаете, как это можно сделать или это вообще возможно?

One Solution collect form web for “Python – метакласс – добавление свойств”

Привяжите текущее значение key к параметру в определениях fget и fset :

 fget = lambda self, k=key: getattr(self, "__%s" % k) fset = lambda self, value, k=key: setattr(self, "__" + k, value) 

Это классическая ловушка Питона. Когда вы определяете

 fget = lambda self: getattr(self, "__%s" % key) 

значение key определяется при fget , а не при определении fget . Так как это нелокальная переменная, ее значение находится в охватывающей области функции __new__ . К моменту fget for-loop завершился, поэтому последнее значение key – это найденное значение. Метод dict.item от dict.item возвращает элементы в непредсказуемом порядке, поэтому иногда последний ключ, скажем, __qualname__ , поэтому иногда возникает повышенная ошибка, и иногда возвращается одинаковое неправильное значение для всех атрибутов без ошибок.


Когда вы определяете функцию с параметром со значением по умолчанию, значение по умолчанию привязывается к параметру во время определения функции. Таким образом, текущие значения key будут исправлены, привязанные к fget и fset когда вы привязываете значения по умолчанию к k .

В отличие от ранее, k теперь является локальной переменной. Значение по умолчанию сохраняется в fget.__defaults__ и fset.__defaults__ .


Другой вариант – использовать закрытие . Вы можете определить это вне метакласса:

 def make_fget(key): def fget(self): return getattr(self, "__%s" % key) return fget def make_fset(key): def fset(self, value): setattr(self, "__" + key, value) return fset 

и использовать его внутри метакласса следующим образом:

 result_dct[key] = property(make_fget(key), make_fset(key)) 

Теперь, когда fget или fset , правильное значение key находится в охватывающей области make_fget или make_fset .

  • Метакласс не вызывается в подклассах
  • Странное наследование с метаклассами
  • Обратные атрибуты класса сопоставления классам в Python
  • Как получить атрибуты в том порядке, в котором они объявлены в классе Python?
  • методы метаклассов на экземплярах класса
  • Возможно ли динамическое создание метакласса для класса с несколькими базами в Python 3?
  • Должен ли я использовать метакласс, декоратор класса или переопределить метод __new__?
  • Запретить использование класса в качестве родительского класса в Python
  • Python - лучший язык программирования в мире.