[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.본질적으로 @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.그것을베이스의 레지스트리에 추가하는 데코레이터를 작성하십시오.
그것을베이스의 레지스트리에 추가하는 데코레이터를 작성하십시오.
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.편집 : 주문의 추가 요구 사항 때문에 나는 완전히 내 대답을 수정. 나는 또한 @ 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.나는 이것이 당신을 위해 효과가있을 것이라고 확신합니다. 나중에 종속 클래스 속성을 지정하십시오. 이것은 또한 훨씬 덜 복잡합니다.
나는 이것이 당신을 위해 효과가있을 것이라고 확신합니다. 나중에 종속 클래스 속성을 지정하십시오. 이것은 또한 훨씬 덜 복잡합니다.
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.파이썬에서 앞으로 참조를 직접 선언 할 수있는 방법은 없지만 다음과 같은 몇 가지 해결 방법이 있습니다.
파이썬에서 앞으로 참조를 직접 선언 할 수있는 방법은 없지만 다음과 같은 몇 가지 해결 방법이 있습니다.
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.난 그냥 하위 클래스를 문자열로 정의하고 필연적 인 데코레이터로 문자열을 클래스 이름으로 바꿉니다. 메타 클래스에서 데코레이터를 정의 할 수도 있습니다. 클래스의 동작을 수정하고 클래스의 수정을 통해 객체 동작을 수정하는 것처럼 메타 클래스를 수정하여 클래스 동작을 수정합니다.
난 그냥 하위 클래스를 문자열로 정의하고 필연적 인 데코레이터로 문자열을 클래스 이름으로 바꿉니다. 메타 클래스에서 데코레이터를 정의 할 수도 있습니다. 클래스의 동작을 수정하고 클래스의 수정을 통해 객체 동작을 수정하는 것처럼 메타 클래스를 수정하여 클래스 동작을 수정합니다.
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.
class Foo: pass class Bar: pass Foo.m = Bar() Bar.m = Foo()
from https://stackoverflow.com/questions/4162456/forward-declaration-of-classes by cc-by-sa and MIT license
'PYTHON' 카테고리의 다른 글
[PYTHON] 파이썬 3.3을 사용할 때 파이썬 패키지 virtualenv가 필요합니까? (0) | 2018.11.11 |
---|---|
[PYTHON] 플랫폼 독립적 인 파일 경로? (0) | 2018.11.11 |
[PYTHON] 중괄호의 의미는 무엇입니까? [닫은] (0) | 2018.11.11 |
[PYTHON] Python AttributeError : 객체에 속성이 없습니다. (0) | 2018.11.11 |
[PYTHON] 장고 테스트를 실행할 때 stdout을 어떻게 볼 수 있습니까? (0) | 2018.11.11 |