Получение имен полей рефлексивно с помощью Python

#python #reflection

#python #отражение

Вопрос:

Возможно ли получить имя поля рефлексивно в Python (3.2)?

Смотрите следующий пример:

 class Something:
    def __init__(self):
        self.x = 1

    def validate():
        return validator.max(self.x, 10)
  

validator.max(self.x, 10) должно выдать сообщение об ошибке, содержащее имя поля x в виде строки (в данном случае "x" ).

Ответ №1:

Вам пришлось бы передать имя атрибута в виде строки

 def validate():
    return validator.max(self, "x", 10)
  

тогда validator.max может выглядеть следующим образом

 def max(ob, attr, max_value):
    val = getattr(ob, attr)    # val would be self.x now
    ...
  

Ответ №2:

Маловероятно. У вещей может даже не быть имен — что такое:

 validator.max(3,10)
  

что предполагается сделать?

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

 validator.max(self.x,10,"x")
  

каким бы ни был validator.max, ему нужен другой аргумент, или, если он встроенный, вам нужно его обернуть.

Комментарии:

1. Кроме того, аргументы функции вычисляются перед вызовом функции.

2. self.x и x является своего рода избыточным.

Ответ №3:

В Python выражение self.x — это просто текущее значение этого элемента, и поэтому информация о том, что значение поступает от объекта, теряется.

Однако вы можете переместить логику проверки на более высокий уровень (базовый класс) и заставить ее работать со всем объектом. При таком подходе «имя» валидатора будет известно функции validate и может использоваться для сообщения об ошибке:

 class ValidatedObject:
    def validate(self):
        for name in dir(self):
            if (name.startswith("validate_") and  # Is a validator
                not getattr(self, name)()):       # and failed
                raise RuntimeError("%s: %s" %
                                   (name, getattr(self, name).__doc__))

class Something(ValidatedObject):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def validate_x(self):
        "Horizontal position shouldn't be that big"
        return self.x < 10

    def validate_y(self):
        "Vertical position must be neither too low nor too high"
        return 20 <= self.y <= 30

    def validate_sum(self):
        "The position must be on the prescribed line"
        return self.x   self.y == 25

class Something2(Something):
    def validate_sum(self):
        return True

Something(3, 22).validate()   # Ok
Something2(5, 30).validate()  # Ok (derived class relaxed checks)
print "About to crash...."
Something2(5, 31).validate()  # Not ok (Y is too high - inherited check)
  

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

Комментарии:

1. FWIW validate() может быть изменен, чтобы извлекать и использовать текст, следующий за "validate_" именем метода, когда он создает сообщение об ошибке. СУХО.

2. @martineau: Использование __doc__ не является повторением, это может быть удобное для пользователя сообщение, не связанное с именем валидатора. Имя средства проверки уже используется в сообщении перед : (однако не удаляется "validate_" ). Я собираюсь добавить правку, чтобы сделать намерение более ясным…

3. У меня нет проблем с использованием __doc__ строки — я всего лишь внес предложение, которое позволило бы ей быть более согласованной с «получить имя поля», указанным в вопросе OP. То, что вы придумали, способно к большему, чем простая проверка значения поля (и это хорошо, ИМХО).