Переопределить Python метакласс __getitem__ - PullRequest
1 голос
/ 21 марта 2020

Учитывая следующий код, я ожидаю возвращаемое значение 'overridden' не 'value1':

class MyMetaclass(type):
  def __new__(cls, name, bases, attrs):
    attrs.update({'_my_dict': {'key1': 'value1', 'key2': 'value2'}})
    return super().__new__(cls, name, bases, attrs)
  def __getitem__(cls, value):
    return cls._my_dict[str(value)]


class MyBaseClass(metaclass=MyMetaclass):
  pass


class MyClass(MyBaseClass):
  @classmethod
  def __getitem__(cls, value):
    return 'overridden'


>>> MyClass['key1']
'value1'  # I expect: 'overridden'

Что мне нужно изменить?

Ответы [ 2 ]

2 голосов
/ 21 марта 2020

__getitem__ относится к способам получения элементов экземпляров класса, в котором он определен.

Итак, если вы определите __getitem__ в метаклассе, он будет работать для классов - если вы напишите __getitem__ в самом классе, он будет работать только для экземпляров этого класса.

Однако вы можете написать метакласс __getitem__, чтобы он вызывал класс '__getitem__, если он существует - и затем эмулировать это поведение:

class MyMetaclass(type):
  def __new__(cls, name, bases, attrs):
    attrs.update({'_my_dict': {'key1': 'value1', 'key2': 'value2'}})
    return super().__new__(cls, name, bases, attrs)
  def __getitem__(cls, value):
    for supercls in cls.__mro__:
         if "__getitem__" in supercls.__dict__:
               # getting the value straight from "__dict__" will prevent Python's mechanisms of converting it to a bound instance method:
               return supercls.__dict__["__getitem__"](cls, value)
    return cls._my_dict[str(value)]


class MyBaseClass(metaclass=MyMetaclass):
  pass


class MyClass(MyBaseClass):
  def __getitem__(cls, value):
    return 'overridden'

Обратите внимание, что таким образом, вы можете даже использовать любое другое имя метода для "class_getitem" в нашей иерархии и при этом оставить обычное __getitem__ доступно для использования экземплярами.

1 голос
/ 21 марта 2020

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

...