Можно ли создать инстанс класса используя квадратные скобки вместо круглых? 🙄
Казалось бы, чего сложного, делаем метод __getitem__ как classmethod и готово
class MyClass:
def __init__(self, value):
self.value = value
@classmethod
def __getitem__(cls, item):
return cls(item)
Но нет, такой финт не сработает.
>>> inst = MyClass[5]
TypeError: 'type' object is not subscriptable
Тип данного класса не поддерживает такой интерфейс. Чтобы поддерживал, нужно изменять его метакласс.
Ниже представлен простой пример добавления метода __getitem__ в тип класса.
class PresetsType(type):
presets = {
'red': '#ff0000',
'green': '#00ff00',
'blue': '#0000ff'}
def __getitem__(self, item):
instance = self(self.presets[item])
return instance
Получилось что-то вроде пресетов, доступных при создании инстанса через квадратные скобки.
Теперь используем новый тип как метакласс нашего класса.
class Color(metaclass=PresetsType):
def __init__(self, value):
self.value = value
def __repr__(self):
return f'<Color "{self.value}">'
Можно тестить!
# создаём инстанс обычным способом
>>> c1 = Color('#ffb905')
>>> print(c1)
<Color "#ffb905">
# теперь через квадратные скобки
>>> c2 = Color['red']
>>> print(c2)
<Color "#ff0000">
На сколько пример актуальный в реальной работе, решать вам. Но он точно неочевидный😜
Как еще можно это использовать?
🔸 как-либо модифицировать класс и возвращать тоже класс вместо готового инстанса
🔸 возвращать сразу список инстансов. Например, в случаях когда используется срез
🔸 возвращать разные версии классов по имени под разные ситуации, например унаследованные классы с доп. интерфейсом
Начиная с версии 3.7 у появилась возможность сделать тоже самое без мета класса!
В PEP 560 добавили magic-метод __class_getitem__(cls).
Теперь наш класс будет выглядеть так:
class Color:
presets = {
'red': '#ff0000',
'green': '#00ff00',
'blue': '#0000ff'}
def __init__(self, value):
self.value = value
def __repr__(self):
return f'<Color "{self.value}">'
def __class_getitem__(cls, item):
return cls(cls.presets[item])
#tricks