У меня много разных небольших классов, которые имеют по несколько полей, например. это:
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
больше невозможно?
Вот что я рассмотрел до сих пор:
-
Используйте getter (ugh!).
class Article: def __init__(self, name, available): self._name = name self.available = available def name(self): return self._name
Уродливый, непитонический - и много кода шаблона для написания (особенно, если у меня есть несколько полей для чтения только для чтения). Тем не менее, это делает работу, и легко понять, почему это так.
-
Используйте
__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__
ко всем из них. Или, может быть, использовать какой-то миксин? В любом случае, я должен решить, как вести себя, если клиент попытается присвоить значение полю только для чтения. Допустим какое-то исключение, я думаю - но что? -
Используйте функцию утилиты для автоматического определения свойств (и необязательно геттеров). Это в основном та же идея, что и (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.
UPDATE: Спасибо за все интересные ответы! К настоящему времени у меня есть следующее:
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)
.
Это выглядит довольно аккуратно для меня - может ли кто-нибудь увидеть недостаток с ним?