복붙노트

[PYTHON] 클래스의 전달 선언?

PYTHON

클래스의 전달 선언?

다음과 같은 클래스가 있습니다.

class Base:
  subs = [Sub3,Sub1]
  # Note that this is NOT a list of all subclasses!
  # Order is also important

class Sub1(Base): pass
class Sub2(Base): pass
class Sub3(Base): pass
...

이제 Base.subs가있을 때 Sub1과 Sub3이 정의되지 않았기 때문에이 작업은 실패합니다. 하지만 분명히 Base 전에 서브 클래스를 둘 수는 없습니다. 파이썬에서 클래스를 forward-declare하는 방법이 있습니까? 나는 isinstance로 작업하기를 원한다. 그래서 서브 클래스의 타입은 실제로 선언 된 서브 클래스와 똑같아야만한다. 서브 클래스는 같은 이름과 다른 속성을 가진 것으로 충분하지 않다.

한 가지 해결 방법은 하위 클래스가 정의 된 후에 Base.subs = [Sub3, Sub1]을 수행하는 것입니다. 그러나 그런 식으로 클래스를 분할하지 않아도됩니다.

편집 : 주문 정보 추가

해결법

  1. ==============================

    1.본질적으로 @Ignacio Vazquez-Abrams와 @ aaronasterling의 답변의 하이브리드 버전으로, 목록에있는 하위 클래스의 순서를 유지합니다. 처음에는 원하는 하위 클래스 이름 (즉, 문자열)을 원하는 순서로 하위 목록에 수동으로 배치 한 다음 각 하위 클래스가 정의 될 때 클래스 장식자가 해당 문자열을 실제 하위 클래스로 바꿉니다.

    본질적으로 @Ignacio Vazquez-Abrams와 @ aaronasterling의 답변의 하이브리드 버전으로, 목록에있는 하위 클래스의 순서를 유지합니다. 처음에는 원하는 하위 클래스 이름 (즉, 문자열)을 원하는 순서로 하위 목록에 수동으로 배치 한 다음 각 하위 클래스가 정의 될 때 클래스 장식자가 해당 문자열을 실제 하위 클래스로 바꿉니다.

    class Base(object):  # New-style class (i.e. explicitly derived from object).
    
        @classmethod
        def register_subclass(cls, subclass):
            """ Class decorator for registering subclasses. """
    
            # Replace any occurrences of the class name in the class' subs list.
            # with the class itself.
            # Assumes the classes in the list are all subclasses of this one.
            # Works because class decorators are called *after* the decorated class'
            # initial creation.
            while subclass.__name__ in cls.subs:
                cls.subs[cls.subs.index(subclass.__name__)] = subclass
    
            return cls  # Return modified class.
    
        subs = ['Sub3', 'Sub1']  # Ordered list of subclass names.
    
    
    @Base.register_subclass
    class Sub1(Base): pass
    
    @Base.register_subclass
    class Sub2(Base): pass
    
    @Base.register_subclass
    class Sub3(Base): pass
    
    print('Base.subs: {}'.format(Base.subs))
    # Base.subs: [<class '__main__.Sub3'>, <class '__main__.Sub1'>]
    

    최신 정보

    Metaclass를 사용하여 똑같은 작업을 수행 할 수 있습니다. 위의 (사용자가 수락 한) 원래 응답에 표시된 것처럼 각 서브 클래스를 명시 적으로 꾸밀 필요가 없으므로 모든 작업이 자동으로 수행됩니다. 메타 클래스 '__init __ ()가 모든 서브 클래스의 생성을 위해 호출 되더라도, 서브 클래스의 이름의 초기 기본 클래스 정의가 여전히 제어되도록 무엇이 그것에서 대체 될 것인가 (질서 유지).

    class BaseMeta(type):
    
        def __init__(cls, name, bases, classdict):
            if classdict.get('__metaclass__') is not BaseMeta:  # Metaclass instance?
                # Replace any occurrences of a subclass' name in the class being
                # created the class' sub list with the subclass itself.
                # Names of classes which aren't direct subclasses will be ignored.
                while name in cls.subs:
                    cls.subs[cls.subs.index(name)] = cls
    
            # Chain to __init__() of the class instance being created after changes.
            # Note class instance being defined must be new-style class.
            super(BaseMeta, cls).__init__(name, bases, classdict)
    
    
    # Python 2 metaclass syntax.
    class Base(object):  # New-style class (derived from built-in object class).
        __metaclass__ = BaseMeta
        subs = ['Sub3', 'Sub1']  # Ordered list of subclass names.
    
    # Python 3 metaclass syntax.
    #class Base(metaclass=BaseMeta):
    #    subs = ['Sub3', 'Sub1']  # Ordered list of subclass names.
    
    
    # Note: No need to manually register the (direct) subclasses.
    class Sub1(Base): pass
    class Sub2(Base): pass
    class Sub3(Base): pass
    
    print('Base.subs: {}'.format(Base.subs))
    

    이 두 답변 사이에는 적어도 하나의 미묘한 차이가 있다는 점에 유의해야합니다. 즉, 첫 번째 클래스는 Base.register_subclass ()를 통해 등록 된 클래스 이름과 함께 작동합니다. 실제로는 Base 클래스의 서브 클래스인지 여부는 변경 / 수정이 가능할 수도 있습니다.)

    저는 이것을 두 가지 이유로 지적하고 있습니다 : 첫째, 여러분의 의견에서 서브 시스템은 "목록의 클래스들 중 일부는 서브 클래스 일 수 있습니다"라고 말했고 더 중요한 것은 서브 시스템이 메타 데이터를 통해 자동으로 "등록"되기 때문에 Base 하위 클래스에서만 작동하는 업데이트의 코드는 목록에 단독으로 남겨 둡니다. 이것은 버그 또는 기능으로 간주 될 수 있습니다. ; ¬)

  2. ==============================

    2.그것을베이스의 레지스트리에 추가하는 데코레이터를 작성하십시오.

    그것을베이스의 레지스트리에 추가하는 데코레이터를 작성하십시오.

    class Base(object):
      subs = []
    
      @classmethod
      def addsub(cls, scls):
        cls.subs.append(scls)
    
     ...
    
    @Base.addsub
    class Sub1(Base):
      pass
    
    class Sub2(Base):
      pass
    
    @Base.addsub
    class Sub3(Base):
      pass
    
  3. ==============================

    3.편집 : 주문의 추가 요구 사항 때문에 나는 완전히 내 대답을 수정. 나는 또한 @ Ignacio Vazquez-Abrams에 의해 처음 사용 된 클래스 데코레이터를 사용한다.

    편집 : 주문의 추가 요구 사항 때문에 나는 완전히 내 대답을 수정. 나는 또한 @ Ignacio Vazquez-Abrams에 의해 처음 사용 된 클래스 데코레이터를 사용한다.

    Edit2 : 지금 코드 테스트 및 일부 어리 석음 약간 수정

    
    class Base(object):
        subs = []
    
        @classmethod
        def addsub(cls, before=None): 
            def inner(subclass):
                if before and before in cls.subs:
                    cls.subs.insert(cls.subs.index(before), subclass)
                else:
                    cls.subs.append(subclass)
                return subclass
            return inner
    
    @Base.addsub()
    class Sub1(Base):
        pass
    
    class Sub2(Base):
        pass
    
    @Base.addsub(before=Sub1)
    class Sub3(Base):
        pass
    
    
  4. ==============================

    4.나는 이것이 당신을 위해 효과가있을 것이라고 확신합니다. 나중에 종속 클래스 속성을 지정하십시오. 이것은 또한 훨씬 덜 복잡합니다.

    나는 이것이 당신을 위해 효과가있을 것이라고 확신합니다. 나중에 종속 클래스 속성을 지정하십시오. 이것은 또한 훨씬 덜 복잡합니다.

    class Base:pass
    
    class Sub1(Base): pass
    class Sub2(Base): pass
    class Sub3(Base): pass
    
    Base.subs = [Sub3,Sub1]
    print(Sub1.subs)
    #[<class __main__.Sub3 at 0x0282B2D0>, <class __main__.Sub1 at 0x01C79810>]
    
  5. ==============================

    5.파이썬에서 앞으로 참조를 직접 선언 할 수있는 방법은 없지만 다음과 같은 몇 가지 해결 방법이 있습니다.

    파이썬에서 앞으로 참조를 직접 선언 할 수있는 방법은 없지만 다음과 같은 몇 가지 해결 방법이 있습니다.

    1) 정의 된 서브 클래스를 수동으로 추가하십시오.

        - Pros: easy to do; Base.subs is updated in one place
        - Cons: easy to forget (plus you don't want to do it this way)
    

    예:

    class Base(object):
        pass
    
    class Sub1(Base):
        pass
    
    class Sub2(Base):
        pass
    
    class Sub3(Base):
        pass
    
    Base.subs = [sub3, sub1]
    

    2) str 값을 사용하여 Base.subs를 만들고 클래스 데코레이터를 사용하여 실제 하위 클래스를 대체합니다 (Base 또는 함수에 대한 클래스 메서드가 될 수 있음 - 함수 버전을 표시 할 예정이지만 메서드 버전을 사용합니다. ).

    - Pros: easy to do
    - Cons: somewhat easy to forget; 
    

    예:

    def register_with_Base(cls):
        name = cls.__name__
        index = Base.subs.index(name)
        Base.subs[index] = cls
        return cls
    
    class Base(object):
        subs = ['Sub3', 'Sub1']
    
    @register_with_Base
    class Sub1(Base):
        pass
    
    class Sub2(Base):
        pass
    
    @register_with_Base
    class Sub3(Base):
        pass
    

    3) str 값을 사용하여 Base.subs를 만들고 Base.subs를 사용하는 메서드로 대체합니다.

    - Pros: no extra work in decorators, no forgetting to update `subs` later
    - Cons: small amount of extra work when accessing `subs`
    

    예:

    class Base(object):
        subs = ['Sub3', 'Sub1']
        def select_sub(self, criteria):
            for sub in self.subs:
                sub = globals()[sub]
                if #sub matches criteria#:
                    break
            else:
                # use a default, raise an exception, whatever
            # use sub, which is the class defined below
    
    class Sub1(Base):
        pass
    
    class Sub2(Base):
        pass
    
    class Sub3(Base):
        pass
    

    기능과 데이터를 모두 한 곳에서 유지하므로 옵션 3을 직접 사용합니다. 당신이해야 할 유일한 일은 서브를 최신 상태로 유지하는 것입니다 (물론 적절한 서브 클래스를 작성하십시오).

  6. ==============================

    6.난 그냥 하위 클래스를 문자열로 정의하고 필연적 인 데코레이터로 문자열을 클래스 이름으로 바꿉니다. 메타 클래스에서 데코레이터를 정의 할 수도 있습니다. 클래스의 동작을 수정하고 클래스의 수정을 통해 객체 동작을 수정하는 것처럼 메타 클래스를 수정하여 클래스 동작을 수정합니다.

    난 그냥 하위 클래스를 문자열로 정의하고 필연적 인 데코레이터로 문자열을 클래스 이름으로 바꿉니다. 메타 클래스에서 데코레이터를 정의 할 수도 있습니다. 클래스의 동작을 수정하고 클래스의 수정을 통해 객체 동작을 수정하는 것처럼 메타 클래스를 수정하여 클래스 동작을 수정합니다.

    class BaseMeta(type):
    
        def register(cls, subcls):
            try:
                i = cls.subs.index(subcls.__name__)
            except ValueError:
                pass
            else:
                cls.subs[i] = subcls
            finally:
                return cls
    
    
    class Base(object):
        __metaclass__ = BaseMeta
        subs = ['Sub3', 'Sub1']
    
    @Base.register
    class Sub1(Base): pass
    
    @Base.register
    class Sub2(Base): pass
    
    @Base.register
    class Sub3(Base): pass
    
    print Base.subs
    

    이 결과는 다음과 같습니다.

    [<class '__main__.Sub3'>, <class '__main__.Sub1'>]
    
  7. ==============================

    7.

    class Foo:
      pass
    
    class Bar:
      pass
    
    Foo.m = Bar()
    Bar.m = Foo()
    
  8. from https://stackoverflow.com/questions/4162456/forward-declaration-of-classes by cc-by-sa and MIT license