Как сделать доступными для чтения-поля из классов Python?

У меня есть много разных небольших классов, которые иметь несколько полей каждый, например this:

class Article:
    def __init__(self, name, available):
        self.name = name
        self.available = available

Какой самый простой и/или самый идиоматический способ сделать поле nameдоступным только для чтения, чтобы

a = Article("Pineapple", True)
a.name = "Banana"  # <-- should not be possible

больше не было возможно?

Вот что я до сих пор рассматривал:

  1. Использовать геттер (тьфу! ).

    class Article:
        def __init__(self, name, available):
            self._name = name
            self.available = available
    
        def name(self):
            return self._name
    

    Уродливый, не-питоновский -и много стандартного кода для написания (, особенно если у меня есть несколько полей, которые нужно сделать -только для чтения). Тем не менее, он выполняет свою работу, и легко понять, почему это так.

  2. Использование__setattr__:

    class Article:
        def __init__(self, name, available):
            self.name = name
            self.available = available
    
        def __setattr__(self, name, value):
            if name == "name":
                raise Exception("%s property is read-only" % name)
            self.__dict__[name] = value
    

    Выглядит красиво со стороны вызывающей стороны, кажется, это идиоматический способ выполнения работы -, но, к сожалению, у меня есть много классов с несколькими полями, каждый из которых можно сделать только для чтения. Поэтому мне нужно добавить реализацию __setattr__ко всем из них. Или использовать какой-то миксин, может быть? В любом случае мне нужно решить, как себя вести, если клиент попытается присвоить значение полю -только для чтения. Дайте какое-то исключение, я думаю -, но какое?

  3. Используйте служебную функцию для автоматического определения свойств (и, возможно, геттеров). В основном это та же идея, что и (1), за исключением того, что я не пишу геттеры явно, а делаю что-то вроде

    class Article:
        def __init__(self, name, available):
            # This function would somehow give a '_name' field to self
            # and a 'name()' getter to the 'Article' class object (if
            # necessary); the getter simply returns self._name
            defineField(self, "name")
            self.available = available
    

    . Недостатком этого является то, что я даже не знаю, возможно ли это (. ]или как это реализовать), так как я не знаком с генерацией кода во время выполнения в Python.:-)

Пока (2)кажется мне наиболее многообещающим, за исключением того факта, что мне понадобятся __setattr__определения для всех моих классов. Хотелось бы, чтобы был способ «аннотировать» поля, чтобы это происходило автоматически. У кого-нибудь есть идея получше?

Как бы то ни было, я буду использовать Python 2.6.

ОБНОВЛЕНИЕ: Спасибо за интересные ответы! К настоящему времени у меня есть это :

def ro_property(o, name, value):
    setattr(o.__class__, name, property(lambda o: o.__dict__["_" + name]))
    setattr(o, "_" + name, value)

class Article(object):
    def __init__(self, name, available):
        ro_property(self, "name", name)
        self.available = available

. Похоже, это работает довольно хорошо. Единственные изменения, необходимые для исходного класса, это

  • Мне нужно унаследовать object(, что в любом случае не такая глупость, я думаю)
  • Мне нужно изменить self._name = nameна ro_property(self, "name", name).

На мой взгляд, это довольно неплохо -Кто-нибудь может увидеть в этом недостаток?

28
задан Frerich Raabe 29 March 2012 в 08:54
поделиться