#python #oop
#python #ооп
Вопрос:
Я пишу небольшой API проверки и инициализирую ограничения для каждого поля в __init__
методе класса. Но эта настройка в принципе может быть выполнена для класса один раз.
class Demo(Validatable):
def __init__(self):
Validatable.__init__(self)
self.count = 11
# could be done at class level
self.constrain_field("count", Max(9), Even())
Но проблема в том, что ограничения для каждого поля должны где-то храниться, и структура данных для этого является частью унаследованного класса Validatable
. Таким образом, все производные классы использовали бы одну и ту же структуру данных, если бы ограничения были установлены на уровне класса, чего не должно произойти!
class Demo(Validatable):
# doesn't work!
Validatable.constrain_field("count", Max(9), Even())
def __init__(self):
self.count = 11
Есть ли возможность наследовать структуру данных и инициализировать ее на уровне класса в производном классе без совместного использования структуры данных для ограничений?
Комментарии:
1. Является
constraint_field()
методом класса или статическим методом? Как это определяется?2. На данный момент это обычный метод (instance), но я хочу это изменить. По сути, метод добавляет ограничения к dict с именем поля в качестве ключа и списком ограничений в качестве значения.
Ответ №1:
Этот вопрос состоит из двух частей.
- Как установить значения
Validatable
структур данных на уровне подкласса, а не в унаследованномValidatable
классе; и - Как определить
constrain_field
метод, чтобы его можно было вызывать один раз при инициализации класса, а не каждый раз при создании экземпляра.
Что касается (1), инициализатор Validatable
класса может получить доступ к классу экземпляра, используя его __class__
свойство. Например:
class Validatable(object):
def __init__(self):
self.__class__.fieldName = "value for " self.__class__.__name__
class Demo(Validatable):
def __init__(self):
super(Demo, self).__init__()
class Demo2(Validatable):
def __init__(self):
super(Demo2, self).__init__()
d = Demo()
d2 = Demo2()
print "Demo.fieldName = " Demo.fieldName
print "Demo2.fieldName = " Demo2.fieldName
Этот код выводит:
Demo.fieldName = value for Demo
Demo2.fieldName = value for Demo2
Затем можно было бы определить constrain_field
метод для использования __class__
свойства экземпляра, с которым он вызывается, для настройки необходимых структур данных.
К сожалению, все это требует, чтобы экземпляр класса был создан до того, как можно будет настроить структуры данных, и это также означает, что constrain_field
метод вызывается каждый раз, когда создается экземпляр. Очевидно, что было бы предпочтительнее сделать это при инициализации класса, которая является частью (2) вопроса.
Для решения части (2) я бы рекомендовал использовать декораторы python. Рассмотрим следующий код, который объединяет функцию свойства Python (используемую в качестве декоратора) с пользовательской функцией декоратора, вызываемой constrain_field
:
def Max(maxValue):
def checkMax(value):
return value <= maxValue
checkMax.__doc__ = "Value must be less than or equal to " str(maxValue)
return checkMax
def Even():
def checkEven(value):
"Value must be even"
return value%2 == 0
return checkEven
def constrain_field(*constraints):
def constraint_decorator(setter):
def checkConstraints(self, value):
ok = True
for c in constraints:
if not c(value):
ok = False
print "Constraint breached: " c.__doc__
if ok:
setter(self, value)
return checkConstraints
return constraint_decorator
class Demo(object):
def __init__(self):
self._count = 2
@property
def count(self):
return self._count
@count.setter
@constrain_field(Max(9), Even())
def count(self, value):
self._count = value
d = Demo()
print "Setting to 8"
d.count = 8
print "Setting to 9"
d.count = 9
print "Setting to 10"
d.count = 10
print "Count is now " str(d.count)
Он печатает:
Setting to 8
Setting to 9
Constraint breached: Value must be even
Setting to 10
Constraint breached: Value must be less than or equal to 9
Count is now 8
При использовании декораторов таким образом, вся инициализация выполняется один раз во время определения класса.