#python #python-3.x #class #metaclass
#python #python-3.x #класс #метакласс
Вопрос:
У меня ситуация, когда я создаю кучу классов, и многие из них на самом деле в основном одинаковы, поэтому я хотел бы создать их в цикле. Они используются с системой регистрации, поэтому с точки зрения использования проблем нет, но я не уверен, как на самом деле определить класс с переменной, определяющей имя класса…
Простой пример:
classList = ['foo', 'bar', 'baz']
for className in classList:
class {{{className}}}_calc(BaseCalc):
def __init__ (self, dataFrame):
self.column = dataFrame[className]
def calc ():
return self.column.sum()
Очевидно, что это очень упрощенный случай. Я не могу изменить аргументы на init, потому что их уже существует целая куча, которые являются частью более крупной структуры.
В остальной части примера используется синтаксис pandas, просто чтобы дать представление о том, как это используется… но на самом деле он используется с базой данных SQL и намного сложнее… Я просто не хочу защищаться: «почему вы это делаете в первую очередь?» У меня есть веские причины, оставьте все как есть.
имя_класса находится в {{{ }}} в строке класса, чтобы указать, что это переменная, и на самом деле синтаксически не корректно. Вопрос в том, «как мне обозначить, для чего я использовал {{{ }}} ?» Я думаю.
Ответом могут быть метаклассы, но я все еще не уверен, как создать ИМЯ моей переменной класса….
ETA: пытаюсь использовать ответ @python_user:
classList = ['foooo', 'bar', 'baaz']
class Base ():
def __init__ (self, buq):
self.buq = buq
def getBuq(self):
return 'buq: ' self.buq
for cls in classList:
class TEMP(Base):
className = cls
def __init__ (self):
self.qux = len(cls)
Base.__init__(self, cls)
def blee(self, inpt):
return inpt self.qux
TEMP.__name__ = f'{cls}'
TEMP.__qualname__ = f'{cls}'
globals()[cls] = TEMP
f = foo()
f.getBuq()
>>>> 'buq: baaz'
Это дает мне только класс baaz. Все три дают baaz … Я делаю что-то действительно глупое?
Комментарии:
1. используйте type() для создания класса с динамически присваиваемым именем,
type(f"MyClass{number}", (), {})
. Вам придется соответствующим образом заполнять базы и dict
Ответ №1:
Вы можете сделать это так, используя globals()
classList = ['foo', 'bar', 'baz']
for className in classList:
class Temp:
def __init__ (self, dataFrame):
self.column = dataFrame[className]
def calc ():
return self.column.sum()
Temp.__name__ = className
globals()[className] = Temp
Затем вы можете сделать foo()
, чтобы создать объект класса foo
. Вы должны вызвать с аргументами, необходимыми для __init__
метода для фактического примера. Это просто для того, чтобы показать, что это работает.
print(type(foo()).__name__) # foo
print(type(bar()).__name__) # bar
print(type(baz()).__name__) # baz
Если вы хотите изменить имена своих классов, вы можете сделать
Temp.__name__ = f'{className}_calc'
globals()[f'{className}_calc'] = Temp
Как указано wim в комментариях, вам также необходимо установить __qualname__
. Temp.__qualname__ = f'{className}_calc'
Редактировать:
Поскольку поиск имен в методах класса выполняется во время выполнения (а не во время компиляции), фрагмент кода содержит ошибку. className
всегда будет ссылаться на последний элемент в classList
( baz
в данном случае), это имя существует вне области действия цикла и будет значением для className
в методах для ВСЕХ классов. (например : self.column = dataFrame[className]
). <— Это всегда dataFrame['baz']
Чтобы исправить это, нужно объявить вызываемую переменную уровня класса className
и присвоить className
ей (ту, что из цикла). Таким образом, во время компиляции это значение будет привязано к классу. Все ссылки на className
внутренние методы класса необходимо изменить, чтобы self.className
код работал должным образом.
class Temp:
className = className # note this line
def __init__ (self, dataFrame):
self.column = dataFrame[self.className] # and this line with the original snippet
Комментарии:
1. Вероятно, это наименее грубое из решений здесь. Вы
__qualname__
также должны установить.2. Мне нравится идея этого ответа, но она не работает. Я добавлю свой код в текст вопроса…
3. Итак, если я заменю 1-ю строку на cls=cls и везде буду использовать self.cls вместо cls, это сработает! Спасибо!
4. да, это то, что я имел в виду, не обнаружил эту ошибку до тех пор, пока через час после публикации ответа, рад узнать, что это сработало
Ответ №2:
Однако вы можете использовать функцию type для создания таких динамических классов. имена = [‘name1’, ‘name2’] Предположим, вам нужно создать 10 объектов класса на python и что-то с ними сделать, например: obj_1 = MyClass() other_object .
Надеюсь, я смогу вам помочь.
Комментарии:
1. Я думаю, что это может сработать, в настоящее время я пытаюсь сделать что-то подобное с помощью: type(className ‘calc’, BaseCalc, {……}. Но словарь становится очень сложным, потому что в нем много лямбд, состоящих из нескольких строк … хм
Ответ №3:
Ну, есть какой-то грязный способ сделать это с помощью функции «exec ()», которая принимает любую строку и выполняет ее; затем вы можете создавать свои классы в виде строки и запускать это. Вот пример, который создает ваши классы, вы можете видеть, что они объявляются, просматривая глобальное пространство имен, которое печатается в конце.
classList = ['foo', 'bar', 'baz']
class BaseCalc():
def __init__ (self, dataFrame):
self.column = dataFrame[className]
def calc ():
return self.column.sum()
for className in classList:
template = '''class %s_calc(BaseCalc):
def __init__ (self, dataFrame):
self.column = dataFrame[className]
def calc ():
return self.column.sum()''' % className
exec(template)
print(globals())