Как реализовать свойство () с динамическим именем (в Python)

Я программирую моделирование отдельных нейронов. Поэтому мне приходится обрабатывать множество параметров. Идея состоит в том, что у меня есть два класса: один для SingleParameter и набор параметров. Я использую свойство (), чтобы легко получить доступ к значению параметра и сделать код более читабельным. Это идеально подходит для параметра sinlge, но я не знаю, как реализовать его для коллекции, поскольку я хочу назвать свойство в Collection после SingleParameter. Вот пример:

class SingleParameter(object):
  def __init__(self, name, default_value=0, unit='not specified'):
    self.name = name
    self.default_value = default_value
    self.unit = unit
    self.set(default_value)
  def get(self):
    return self._v
  def set(self, value):
    self._v = value
  v = property(fget=get, fset=set, doc='value of parameter')

par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')

# par1 and par2 I can access perfectly via 'p1.v = ...'
# or get its value with 'p1.v'

class Collection(object):
  def __init__(self):
    self.dict = {}
  def __getitem__(self, name):
    return self.dict[name] # get the whole object
    # to get the value instead:
    # return self.dict[name].v
  def add(self, parameter):
    self.dict[parameter.name] = parameter
    # now comes the part that I don't know how to implement with property():
    # It shoule be something like
    # self.__dict__[parameter.name] = property(...) ?

col = Collection()
col.add(par1)
col.add(par2)
col['par1'] # gives the whole object

# Now here is what I would like to get:
# col.par1 -> should result like col['par1'].v
# col.par1 = 5 -> should result like col['par1'].v = 5

Другие вопросы, которые я задаю, чтобы понять свойство ():

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
9
0
9 060
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Посмотрите на встроенные функции getattr и setattr. Вы, вероятно, станете намного счастливее.

Свойства предназначены для динамической оценки атрибутов или для того, чтобы сделать их доступными только для чтения. Что вам нужно, так это настроить доступ к атрибутам. __getattr__ и __setattr__ отлично справляются с этим, а также есть __getattribute__, если __getattr__ недостаточно.

Подробнее см. Документы Python по настройке доступа к атрибутам.

когда я добавляю: ----- def setattr __ (self, name, value): self.dict [name] = value ---- Я получаю сообщение об ошибке, что «объект 'Collection' не имеет атрибута 'dict'» - хотя это реализовано в __init.

Philipp der Rautenberg 05.01.2009 16:43

хорошо, я реализовал это сейчас с помощью set- / getattr. см. ответ ниже!

Philipp der Rautenberg 05.01.2009 17:04

Вы смотрели пакет черт? Похоже, что вы изобретаете колесо с помощью классов параметров. У черт также есть дополнительные функции, которые могут быть полезны для вашего типа приложения (кстати, я знаю человека, который с радостью использует черты в нейронном моделировании).

Большое спасибо. Однажды я наткнулся на черты характера, но не понял, для чего они нужны. Наверное, сейчас самое время еще раз взглянуть :-)!

Philipp der Rautenberg 05.01.2009 17:12

Теперь я реализовал решение с помощью set- / getattr:

class Collection(object):
...
  def __setattr__(self, name, value):
    if 'dict' in self.__dict__:
      if name in self.dict:
        self[name].v = value
    else:
      self.__dict__[name] = value
  def __getattr__(self, name):
    return self[name].v

Есть одна вещь, которая мне совсем не нравится: атрибутов нет в __dict__. И если бы они у меня тоже были, у меня была бы копия ценности - что может быть опасно ...

ну тогда просто не кладите их туда. В любом случае это будет сложно сделать, поскольку вы переопределили setattr.

nosklo 05.01.2009 19:54

Не рекомендуется использовать встроенные имена типов для переменных. Попробуйте изменить имя атрибута dict на что-нибудь более понятное.

Abgan 06.01.2009 22:54

Наконец мне удалось реализовать классы с помощью property (). Большое спасибо за совет. Мне потребовалось немало времени, чтобы разобраться в этом, но я могу обещать вам, что это упражнение поможет вам лучше понять ООП питонов.

Я реализовал это также с помощью __getattr__ и __setattr__, но до сих пор не знаю преимуществ и недостатков решения-свойства. Но, кажется, стоит задать еще один вопрос. Свойства-решения кажутся совершенно чистыми.

Итак, вот код:

class SingleParameter(object):
  def __init__(self, name, default_value=0, unit='not specified'):
    self.name = name
    self.default_value = default_value
    self.unit = unit
    self.set(default_value)
  def get(*args):
    self = args[0]
    print "get(): "
    print args
    return self._v
  def set(*args):
    print "set(): "
    print args
    self = args[0]
    value = args[-1]
    self._v = value
  v = property(fget=get, fset=set, doc='value of parameter')

class Collection(dict):
  # inheriting from dict saves the methods: __getitem__ and __init__
  def add(self, par):
    self[par.name] = par
    # Now here comes the tricky part.
    # (Note: this property call the get() and set() methods with one
    # more argument than the property of SingleParameter)
    setattr(Collection, par.name,
      property(fget=par.get, fset=par.set))

# Applying the classes:
par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')
col = Collection()
col.add(par1)
col.add(par2)
# Setting parameter values:
par1.v = 13
col.par1 = 14
# Getting parameter values:
par1.v
col.par1
# checking identity:
par1.v is col.par1
# to access the whole object:
col['par1']

Поскольку я новичок, я не знаю, как двигаться дальше: как относиться к уточняющим вопросам (например, к самому этому):

  • get (), кажется, вызывается дважды - почему?
  • oop-design: property vs. "__getattr__ & __setattr__" - когда что использовать?
  • это грубо проверять собственный ответ на собственный вопрос как принял?
  • рекомендуется ли переименовать заголовок, чтобы сопоставить вопросы или вопросы, разработанные на одном и том же примере, в одном контексте?

Другие вопросы, которые я задаю, чтобы понять свойство ():

Ответ принят как подходящий

Использование одних и тех же функций get / set для обоих классов вынуждает вас сделать уродливый взлом со списком аргументов. Очень схематично, вот как я бы это сделал:

В классе SingleParameter определите получение и установку как обычно:

def get(self):
  return self._s
def set(self, value):
  self._s = value

В классе Collection вы не можете узнать информацию до тех пор, пока не создадите свойство, поэтому вы определяете функцию метасета / метагета и конкретизируете их только позже с помощью лямбда-функции:

def metaget(self, par):
  return par.s
def metaset(self, value, par):
  par.s = value
def add(self, par):
  self[par.name] = par
  setattr(Collection, par.name,
    property(
      fget=lambda x : Collection.metaget(x, par),
      fset=lambda x, y : Collection.metaset(x,y, par))

У меня есть класс, который делает что-то подобное, но в объекте коллекции я сделал следующее:

setattr (self, par.name, par.v)

Другие вопросы по теме