Унаследованные, но не разделяемые (статические) члены класса в Python - PullRequest
2 голосов
/ 04 мая 2011

Я пишу небольшой 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 Ответ

2 голосов
/ 04 мая 2011

Этот вопрос состоит из двух частей.

  1. Как установить значения структур данных Validatable на уровне подкласса, а не в унаследованном классе Validatable
  2. Как определить метод 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 property (используется в качестве декоратора) с пользовательской функцией декоратора под названием 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

При использовании декораторов таким способом вся инициализация выполняется один раз во время определения класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...