Например, у меня есть следующий базовый класс:
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
Из этого класса я получил несколько других классов, например
class TestClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Test')
class SpecialClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Special')
Есть ли хороший питонический способ динамического создания этих классов с помощью вызова функции, которая помещает новый класс в мою текущую область видимости, например:
foo(BaseClass, "My")
a = MyClass()
...
Поскольку будут комментарии и вопросы, зачем мне это нужно: все производные классы имеют одинаковую внутреннюю структуру с той разницей, что конструктор принимает ряд ранее неопределенных аргументов. Так, например, MyClass
принимает ключевые слова, a
а конструктор класса TestClass
принимает b
и c
.
inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")
Но они НИКОГДА не должны использовать тип класса в качестве входного аргумента, например
inst1 = BaseClass(classtype = "My", a=4)
Я заставил это работать, но предпочел бы другой способ, то есть динамически созданные объекты класса.
источник
a
это всегда будетMyClass
иTestClass
никогда не будетa
? Почему бы просто не объявить все 3 аргумента,BaseClass.__init__()
но по умолчаниюNone
?def __init__(self, a=None, b=None, C=None)
?Ответы:
Этот фрагмент кода позволяет вам создавать новые классы с динамическими именами и именами параметров. Проверка параметров
__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
Я вижу , вы просите для вставки имен динамических в рамках именования - теперь, что не считается хорошей практикой в 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
передачи имени нового класса, кортежа с базовым классом (-ами) и__dict__
тела нового класса, например:>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
update
Любой, кто в этом нуждается, должен также проверить проект укропа - он утверждает, что может обрабатывать и очищать классы так же, как pickle делает с обычными объектами, и в некоторых из моих тестов он жил в соответствии с этим.
источник
BaseClass.__init__()
было бы лучше как более общееsuper(self.__class__).__init__()
, которое лучше играет, когда новые классы подклассы. (Ссылка: rhettinger.wordpress.com/2011/05/26/super-considered-super )super
приведенный выше и создать подкласс динамически создаваемого класса, чтобы понять его; И, с другой стороны, в этом случае у вас может быть базовый класс как общий объект, из которого следует вызывать__init__
.__init__
ofBaseClass
вызывается с одним аргументом, но на самом делеBaseClass.__init__
всегда принимает произвольный список аргументов ключевого слова. Во-вторых, в приведенном выше решении все разрешенные имена параметров устанавливаются как атрибуты, чего я не хочу. ДОЛЖЕН использоваться ЛЮБОЙ аргументBaseClass
, но какой из них я знаю при создании производного класса. Я, наверное, обновлю вопрос или задам более точный, чтобы было понятнее.super()
я упоминал, даетTypeError: must be type, not SubSubClass
. Если я правильно понимаю, это происходит из первого аргументаself
из__init__()
, который является ,SubSubClass
гдеtype
ожидается объект: это , кажется , связано с тем ,super(self.__class__)
является несвязанным объектом супер. Каков его__init__()
метод? Я не уверен, для какого такого метода может потребоваться первый аргумент типаtype
. Могли бы вы объяснить? (Примечание: здесь мойsuper()
подход действительно не имеет смысла, потому что__init__()
имеет переменную сигнатуру.)type()
- это функция, которая создает классы (и, в частности, подклассы):def set_x(self, value): self.x = value SubClass = type('SubClass', (BaseClass,), {'set_x': set_x}) # (More methods can be put in SubClass, including __init__().) obj = SubClass() obj.set_x(42) print obj.x # Prints 42 print isinstance(obj, BaseClass) # True
источник
TypeError
что сказал__init__() takes exactly 2 arguments (1 given)
. Я обнаружил, что достаточно добавить что-нибудь (что-нибудь?), Чтобы заполнить пробел. Например,obj = SubClass('foo')
работает без ошибок.SubClass
является подклассомBaseClass
в вопросе иBaseClass
принимает параметр (classtype
, который есть'foo'
в вашем примере).Чтобы создать класс со значением динамического атрибута, ознакомьтесь с приведенным ниже кодом. NB. Это фрагменты кода на языке программирования Python.
def create_class(attribute_data, **more_data): # define a function with required attributes class ClassCreated(optional extensions): # define class with optional inheritance attribute1 = adattribute_data # set class attributes with function parameter attribute2 = more_data.get("attribute2") return ClassCreated # return the created class # use class myclass1 = create_class("hello") # *generates a class*
источник