Кастомизация класса с помощью метакласса в питоне
Всем привет. Задание заключается в том, чтобы с помощью метакласса создавать собственный класс, где все атрибуты и методы (кроме магических) назывались по правилу prefix_nameofaatribute. В целом, мне удалось этого добиться, но только не для атрибутов, инициализируемых в init. Более при попытке создания класса, выдает ошибку "TypeError: CustomClass() takes no arguments". Как мне это исправить? Буду благодарен за любую помощь.
class CustomMeta(type):
prefix = 'custom_'
def __new__(mcs, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
custom_attrs = dict((CustomMeta.prefix + name, value) for name, value in attrs)
return super(CustomMeta, mcs).__new__(mcs, name, bases, custom_attrs)
class CustomClass(metaclass=CustomMeta):
x = 50
def __init__(self, val = 99):
self.val = val
def line(self):
return 100
if __name__ == "__main__":
inst = CustomClass(4)
inst.custom_x
inst.custom_val
inst.custom_line()
inst.x # должна быть ошибка
inst.val # должна быть ошибка
inst.line() # должна быть ошибка
Ответы (1 шт):
Ошибка создания объекта заключается в том, что при создании класса, вы стёрли упоминание __init__ функции. Также, пусть мне никогда не понять зачем вам это, однако подмену названий атрибутов в дочернем конструкторе - это такое себе занятие. Вы всегда можете переопределить функции __setattr__, __getattr__ и __delattr__, однако наличие подобного функционала сломало бы работу __slots__ механизма. Более того, IDE не смогут узнать о наличии "новых" имён аттрибутов.
Если вы хотите скрыть какие-либо переменные от дочерних классов и внешнего доступа - используйте двойное подчёркивание перед названием атрибута. Внутри класса оно продолжит быть __var_name, однако снаружи придётся писать __ClassName_var_name
Исправленное создание объекта класса:
class CustomMeta(type):
prefix = 'custom_'
def __new__(mcs, name, bases, dct):
custom_attrs = {
name if name.startswith('__') else f'{mcs.prefix}{name}': value
for name, value in dct.items()
}
return super(CustomMeta, mcs).__new__(mcs, name, bases, custom_attrs)