기본 클래스에서 파생 클래스를 동적으로 만드는 방법


95

예를 들어 다음과 같은 기본 클래스가 있습니다.

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항상있을 것입니다 MyClassTestClass을하지 않습니다 a? 3 개의 인수를 모두 선언하지 않고 모두 BaseClass.__init__()기본값으로 설정하는 이유는 무엇 None입니까? def __init__(self, a=None, b=None, C=None)?
acattle 2013 년

내가 사용할 수있는 모든 인수를 알지 못하기 때문에 기본 클래스에서 아무것도 선언 할 수 없습니다. 각각 5 개의 다른 인수를 가진 30 개의 다른 클래스가있을 수 있으므로 생성자에서 150 개의 인수를 선언하는 것은 해결책이 아닙니다.
Alex

답변:


145

이 코드를 사용하면 동적 이름과 매개 변수 이름으로 새 클래스를 만들 수 있습니다. 의 매개 변수 확인은 __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

지금 - 당신이 명명 범위에 동적 이름을 삽입을 요구하고 볼 것을 당신도 변수 코딩시에 알려진 이름, 또는 데이터가 - - 파이썬에서 좋은 연습으로 간주되지 않으며, 런타임에서 배운 이름은 "더 있습니다 데이터 "보다"변수 "-

따라서 클래스를 사전에 추가하고 거기에서 사용할 수 있습니다.

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: ...})

업데이트
가 필요한 사람은 누구나 dill 프로젝트를 확인해야합니다. 피클이 일반 객체에 수행하는 것처럼 클래스를 피클 및 언 피클 할 수 있다고 주장하며 일부 테스트에서 그에 대해 살았습니다.


2
내가 올바르게 기억한다면 , 새 클래스가 서브 클래 싱 될 때 더 잘 재생되는 BaseClass.__init__()더 일반적인으로 더 나을 것 super(self.__class__).__init__()입니다. (참조 : rhettinger.wordpress.com/2011/05/26/super-considered-super )
Eric O Lebigot

@EOL : 정적으로 선언 된 클래스를위한 것이지만 Super의 첫 번째 매개 변수로 하드 코딩 할 실제 클래스 이름이 없기 때문에 많은 춤을 추어 야합니다. 이를 super위의 것으로 바꾸고 동적으로 생성 된 클래스의 하위 클래스를 만들어 이해합니다. 그리고 다른 한편으로,이 경우 기본 클래스를 호출 할 일반 객체로 가질 수 있습니다 __init__.
jsbueno 2013 년

이제 제안 된 솔루션을 살펴볼 시간이 있었지만 내가 원하는 것은 아닙니다. 첫째, __init__of BaseClass가 하나의 인수로 호출되는 것처럼 보이지만 실제로는 BaseClass.__init__항상 임의의 키워드 인수 목록을 사용합니다. 둘째, 위의 솔루션은 허용되는 모든 매개 변수 이름을 속성으로 설정합니다. 모든 인수는로 이동해야 BaseClass하지만 파생 클래스를 만들 때 알고있는 인수 입니다. 나는 아마도 질문을 업데이트하거나 더 명확하게하기 위해 더 정확한 질문을 할 것입니다.
Alex

@jsbueno : 오른쪽은 사용 super()내가 언급했다 준다 TypeError: must be type, not SubSubClass. 만약 내가 제대로 이해하고,이 첫 번째 인수에서 유래 self__init__()A는, SubSubClass경우 type객체가 예상됩니다 :이 사실에 관한 것 super(self.__class__)언 바운드 슈퍼 객체입니다. 그 __init__()방법 은 무엇입니까 ? 이러한 메서드가 type의 첫 번째 인수를 요구할 수 있는지 잘 모르겠습니다 type. 설명해 주시겠습니까? (사이드 노트 : 내 super()때문에 접근 방식은 참으로, 여기에 의미를하지 않는 __init__()변수 서명이 있습니다.)
에릭 O Lebigot

1
@EOL : 중요한 문제는 실제로 분해 된 클래스의 다른 하위 클래스를 만드는 경우입니다. self .__ class__는 "super"가 호출되는 클래스가 아니라 해당 하위 클래스를 참조하고 무한 재귀를 얻습니다.
jsbueno 2013 년

91

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

파이썬 2.7을 사용하여이 예를 이해하려고 노력에서, 나는 가지고 TypeError말했다 __init__() takes exactly 2 arguments (1 given). 틈을 메우기 위해 무언가를 추가하는 것으로 충분하다는 것을 알았습니다. 예를 들어 obj = SubClass('foo')오류없이 실행됩니다.
DaveL17 2017-10-03

질문 SubClass의 하위 클래스이고 매개 변수 ( , 귀하의 예에 있음)를 사용 하므로 이는 정상 입니다. BaseClassBaseClassclasstype'foo'
Eric O Lebigot

@EricOLebigot 여기서 BaseClass의 초기화를 호출 할 수 있습니까? 슈퍼처럼?
mikazz

-3

동적 속성 값으로 클래스를 만들려면 아래 코드를 확인하세요. NB. 이것은 파이썬 프로그래밍 언어로 된 코드 조각입니다.

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*
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.