Этот бит кода позволяет создавать новые классы с динамическими именами и именами параметров. Проверка параметров в __init__
просто не позволяет неизвестные параметры, если вам нужны другие проверки, как типа, или что они являются обязательными, просто добавить логику там:
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
def ClassFactory(name, argnames, BaseClass=BaseClass):
def __init__(self, **kwargs):
for key, value in kwargs.items():
# here, the argnames variable is the one passed to the
# ClassFactory call
if key not in argnames:
raise TypeError("Argument %s not valid for %s"
% (key, self.__class__.__name__))
setattr(self, key, value)
BaseClass.__init__(self, name[:-len("Class")])
newclass = type(name, (BaseClass,),{"__init__": __init__})
return newclass
И это работает, как это, например:
>>> SpecialClass = ClassFactory("SpecialClass", "a b c".split())
>>> s = SpecialClass(a=2)
>>> s.a
2
>>> s2 = SpecialClass(d=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in __init__
TypeError: Argument d not valid for SpecialClass
Я вижу, вы просите для вставки динамических имен в области видимости имен - теперь что не считается хорошим р ractice в Python - вы либо имена переменных, известные во время кодирования или данные - и имена узнали во время выполнения больше «данные», чем «переменные» -
Таким образом, вы можете просто добавить свои классы в словарь и использовать их оттуда:
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
и если ваш дизайн абсолютно необходимы имена прийти в сферу, просто сделать то же самое, но использовать словарь возвращенное globals()
вызова вместо произвольного словаря:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(Действительно, функция фабрики классов могла бы динамически вставлять имя в глобальную область вызывающего абонента, но это еще хуже, и не совместимо с реализацией Python.Способ сделать это - получить кадр выполнения вызывающего абонента через sys._getframe(1) и установить имя класса в глобальном словаре фрейма в его атрибуте f_globals
).
update, tl; dr: Этот ответ стал популярным, но тем не менее его очень специфичным для тела вопроса. Общий ответ о том, как «динамически создавать производные классы от базового класса» в Python является простым вызовом type
передавая новое имя класса, кортеж с BaseClass (ами) и __dict__
тела для нового класса -подобное это:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
обновления
кто нуждается в этом должны также проверить проект dill - он утверждает, что способно протравить и unpickle классов так же, как маринад делают к обычным объектам, и жил в ней в некоторые из моих тестов.
Чтобы убедиться, что тип экземпляра изменяется в зависимости от прилагаемых аргументов? Например, если я даю 'a', он всегда будет' MyClass', а 'TestClass' никогда не примет' a'? Почему бы просто не объявить все 3 аргумента в 'BaseClass .__ init __()', но по умолчанию все они 'None'? 'def __init __ (self, a = None, b = None, C = None)'? – acattle
Я не могу объявить что-либо в базовом классе, так как я не знаю всех аргументов, которые я мог бы использовать. У меня может быть 30 разных класов с 5 разными аргументами, поэтому объявление 150 аргументов в конструкторе не является решением. – Alex