Как сделать свойства класса Python доступными по индексу?

У меня есть такая модель Django:

class Competitor(models.Model):
  """
  Competitor model object
  """
  name = models.CharField(max_length=20)
  easy = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Easy Mode')
  hard = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Hard Mode')
  tematicas = ArrayField(models.PositiveSmallIntegerField(), size=7, null=True, blank=True, verbose_name='Tematicas')
  random_score = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Random Mode')
  min1 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 1')
  min2 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 2')
  deluxe = ArrayField(models.PositiveSmallIntegerField(), size=14, null=True, blank=True, verbose_name='Deluxe')
  replica = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Replica')

И я хочу иметь доступ к свойствам с индексом, поэтому, если я напишу competitor[0], он должен вернуть значение name.

Я посмотрел и, согласно этому вопросу, я должен реализовать как __iter__(), так и __getitem__() и другие методы. Но я понятия не имею, что делать внутри этих методов.

Кто-нибудь знает, как это сделать?


person Joaquin    schedule 03.04.2021    source источник


Ответы (3)


Я не знаю, сможете ли вы сделать это без сопоставления каждого индекса. Если вы можете гарантировать, что порядок индексов всегда будет одинаковым, это работает для вас:

class Competitor:
    def __init__(self):
        self.name = 'Leonardo'

    def __getitem__(self, key):
        if key == 0:
            return self.name

if __name__ == "__main__":
    print(Competitor()[0])  # Leonardo
person Leonardo Rick    schedule 03.04.2021

Если вы планируете получать только предметы, вы должны переопределить __getitem__.

class Competitor(models.Model):
  """
  Competitor model object
  """
  name = models.CharField(max_length=20)
  easy = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Easy Mode')
  hard = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Hard Mode')
  tematicas = ArrayField(models.PositiveSmallIntegerField(), size=7, null=True, blank=True, verbose_name='Tematicas')
  random_score = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Random Mode')
  min1 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 1')
  min2 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 2')
  deluxe = ArrayField(models.PositiveSmallIntegerField(), size=14, null=True, blank=True, verbose_name='Deluxe')
  replica = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Replica')

  def __getitem__(self, key):
      if key == 0:
          return self.nome
      elif key == 1:
          return self.easy
      ...
      elif key == 8:
          return self.replica

Простой способ — отслеживать свойства с помощью списка:

class Competitor(models.Model):
  """
  Competitor model object
  """
  name = models.CharField(max_length=20)
  easy = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Easy Mode')
  hard = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Hard Mode')
  tematicas = ArrayField(models.PositiveSmallIntegerField(), size=7, null=True, blank=True, verbose_name='Tematicas')
  random_score = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Random Mode')
  min1 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 1')
  min2 = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 2')
  deluxe = ArrayField(models.PositiveSmallIntegerField(), size=14, null=True, blank=True, verbose_name='Deluxe')
  replica = ArrayField(models.PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Replica')
  _list = [name, easy, hard, tematicas, random_score, min1, min2, deluxe, replica]

  def __getitem__(self, key):
      return self._list[key]

Чтобы полностью реализовать нотацию нижнего индекса ([]), вам также потребуется реализовать __setitem__ и __delitem__.

  def __setitem__(self, index, value):
      self._list[index] = value
person julianofischer    schedule 03.04.2021
comment
Я планирую получать и изменять элементы. Итак, как comp[0] = 'hello'. Если я хочу это сделать, я должен реализовать __setitem__, верно? Если мне нужно, это интуитивно понятно, как я должен это сделать, но я хочу убедиться, что @julianofischer - person Joaquin; 03.04.2021
comment
Я отредактировал ответ и добавил предложение метода setitem - person julianofischer; 03.04.2021
comment
Это не сработало. Ошибки нет, значение не меняется. Я заметил, что если я делаю: comp.__dict__['hard'], я получаю [9, 9, 9, 9, 9, 9, 9, 9, 9], но если я делаю comp[2], я получаю <django.contrib.postgres.fields.array.ArrayField: hard> Думаю, поэтому изменение значения с помощью [] не работает @julianofischer - person Joaquin; 03.04.2021
comment
извини Хоакин, моя ошибка. проверьте это: self._list[index] = value - person julianofischer; 03.04.2021
comment
тоже не работает @julianofischer - person Joaquin; 03.04.2021

Прежде всего, я хочу поблагодарить @julianofischer за его ответ. Это научило меня, как реализовать методы, которые я буду реализовывать в этом ответе.

Сначала добавьте список строк с именами полей, к которым вы хотите иметь доступ с помощью [] в качестве свойства класса, например:

class Competitor(Model):
  """
  Competitor model object
  """
  name = CharField(max_length=20)
  easy = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Easy Mode')
  hard = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Hard Mode')
  tematicas = ArrayField(PositiveSmallIntegerField(), size=7, null=True, blank=True, verbose_name='Tematicas')
  random_score = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Random Mode')
  min1 = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 1')
  min2 = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='minuto 2')
  deluxe = ArrayField(PositiveSmallIntegerField(), size=14, null=True, blank=True, verbose_name='Deluxe')
  replica = ArrayField(PositiveSmallIntegerField(), size=9, null=True, blank=True, verbose_name='Replica')
  _list = [ 'easy', 'hard', 'tematicas', 'random_score', 'min1', 'min2', 'deluxe', 'replica' ]

Затем, если вы хотите получить и установить значения, вам нужно реализовать __getitem__ и __setitem__, например так:

def __getitem__(self, index: int):
  return self.__dict__[self._list[index]]

def __setitem__(self, index: int, value: list):
  self.__dict__[self._list[index]] = value
  self.save(update_fields=[self._list[index]])

Таким образом, вы получаете доступ к значениям полей с помощью __getitem__, используете .__dict__[index] и возвращаете их, а с помощью __setitem__ вы изменяете их напрямую.

person Joaquin    schedule 03.04.2021