2016-09-10 1 views
2

나는 무언가를 분명히하고 싶습니다. 클래스 속성의 전달 선언을 허용하는 클래스를 만들고 싶습니다. (당신은 알고 있어야합니다 경우에, 나는 파서 콤비에 대한 몇 가지 달콤한 구문을 만들려고 노력하고 있어요.)메타 클래스를 사용하여 전달 선언을 허용합니다.

이 내가 만들려고 노력하고 물건의 종류 :

a = 1 
class MyClass(MyBaseClass): 
    b = a # Refers to something outside the class 
    c = d + b # Here's a forward declaration to 'd' 
    d = 1 # Declaration resolved 

나의 현재 방향은 메타 클래스를 만드는 것입니다 그래서 d이 발견되지 않으면 나는 NameError 예외를 잡아서 어떤 더미 클래스의 인스턴스를 반환합니다. ForwardDeclaration이라고 부를 것입니다. 나는 AutoEnum에서 영감을받습니다.이 영감은 메타 클래스 마법을 사용하여 노출 된 식별자와 할당이없는 enum 값을 선언합니다.

아래는 내가 지금까지 가지고있는 것입니다. 누락 된 조각은 다음과 같습니다

class MetaDict(dict): 
    def __init__(self): 
     self._forward_declarations = dict() 
    def __getitem__(self, key): 
     try: 
      ### WHAT DO I PUT HERE ??? ### 
      # How do I continue name resolution to see if the 
      # name already exists is the scope of the class 
     except NameError: 
      if key in self._forward_declarations: 
       return self._forward_declarations[key] 
      else: 
       new_forward_declaration = ForwardDeclaration() 
       self._forward_declarations[key] = new_forward_declaration 
       return new_forward_declaration 

class MyMeta(type): 
    def __prepare__(mcs, name, bases): 
     return MetaDict() 

class MyBaseClass(metaclass=MyMeta): 
    pass 

class ForwardDeclaration: 
    # Minimal behavior 
    def __init__(self, value=0): 
     self.value = value 
    def __add__(self, other): 
     return ForwardDeclaration(self.value + other) 
+0

기본 인수 (이 경우 특성)를 사용하여이 문제를 해결할 수 없습니까? – Rockybilly

+0

내가 당신을 오해하지 않는다면, MyClass의 속성이 무엇인지'MyBaseClass'를 정의 할 때 알 수 없으므로 작동하지 않을 것입니다. 내가 그랬다면, 당신은 옳을 것이다. 나는 그것들을 모두 기본 클래스에'd = ForwardDeclaration()'과 같이 선언 할 수 있습니다. – drhagen

+0

이것은 역 겹게 역겨운 것처럼 들린다! 잘 했어! 특정 문제/질문이 무엇인지는 분명하지 않습니다. –

답변

1

로 시작하려면 :

def __getitem__(self, key): 
     try: 
      return super().__getitem__(key) 
     except KeyError: 
      ... 

하지만 그건 당신이 밖에서 글로벌 변수를 검색 할 수 없습니다 나는 NameError의 정상 이름 확인을 계속 잡을 어떻게 클래스 바디. 같은 SymPy와 SQLAlchemy의 파이썬 물건 고급이에 의지 한 - "UnPythonic"는 것을

class MetaDict(dict): 
    def __init__(self): 
     self._forward_declarations = dict() 

    # Just leave __getitem__ as it is on "dict" 
    def __missing__(self, key): 
     if key in self._forward_declarations: 
      return self._forward_declarations[key] 
     else: 
      new_forward_declaration = ForwardDeclaration() 
      self._forward_declarations[key] = new_forward_declaration 
      return new_forward_declaration 

당신이 볼 수 있듯이, 그되지 않습니다 : 또한 딕셔너리의 서브 클래스 정확히 예약 된 __missin__ 방법을 사용할 수 있습니다 친절한 행동을 통해 좋은 마술을 행할 수 있습니다. 문서화되고 테스트를 거쳐야합니다.

이제 전역 (모듈) 변수를 허용하기 위해 모든 Python 구현에서 사용 가능하지 않을 수도있는 약간의 문제가 있습니다. 즉, 클래스 본문이있는 프레임을 검토하는 것입니다. 그 전역을 얻을 수 있습니다 :

import sys 
... 
class MetaDict(dict): 
    def __init__(self): 
     self._forward_declarations = dict() 

    # Just leave __getitem__ as it is on "dict" 
    def __missing__(self, key): 
     class_body_globals = sys._getframe().f_back.f_globals 
     if key in class_body_globals: 
      return class_body_globals[key] 
     if key in self._forward_declarations: 
      return self._forward_declarations[key] 
     else: 
      new_forward_declaration = ForwardDeclaration() 
      self._forward_declarations[key] = new_forward_declaration 
      return new_forward_declaration 

을 이제 당신이 여기 있음 - 당신의 특별한 사전이 NameErrors을 피하기 위해 충분히 좋은,하지만 ForwardDeclaration 객체가 충분히 스마트에서입니다 - 실행시 :

a = 1 
class MyClass(MyBaseClass): 
    b = a # Refers to something outside the class 
    c = d + b # Here's a forward declaration to 'd' 
    d = 1 

은 어떻게됩니까 th이다 c에있는 개체는 ForwardDeclaration 개체가되지만 즉시 값 d 인 0이됩니다. 다음 줄에서 d은 단순히 값 1으로 덮어 쓰여지고 더 이상 게으른 개체가 아닙니다. 따라서 c = 0 + b을 선언 할 수도 있습니다.

이것을 극복하려면 스마트 웨이에서 설계된 클래스 여야하며 그 값은 항상 느슨하게 평가되고 "반응 형 프로그래밍"접근 방식과 같이 동작해야합니다. 즉, 값을 업데이트하면 업데이트가 계단식으로 연결됩니다. 그것에 의존하는 다른 모든 값. 나는 당신에게 "반응하는"인식 FOrwardDeclaration 클래스의 완전한 구현을 제공하는 것이이 질문의 범위에서 벗어난 것이라고 생각한다. - 나는 장난감 코드를 가지고 있기 때문에, github에서 할 수 있습니다.

에도 적절한 "반응성"ForwardDeclaration 클래스와, 당신은 d = 1 클래스가 작동하도록 다시 사전을 수정해야합니다 :

class MetaDict(dict): 
    def __init__(self): 
     self._forward_declarations = dict() 

    def __setitem__(self, key, value): 
     if key in self._forward_declarations: 
      self._forward_declations[key] = value 
      # Trigger your reactive update here if your approach is not 
      # automatic 
      return None 
     return super().__setitem__(key, value) 
    def __missing__(self, key): 
     # as above 

을 마지막으로 완전 반응을 구현하기 위해 havign를 방지 할 수있는 방법이 인식 클래스 - 당신은 메타 클래스의 __new__ 방법에 대기중인 모든 FOrwardDependencies를 해결할 수 있습니다 - (그래서 당신의 ForwardDeclaration 객체 클래스 작성시 수동으로 "고정", 그리고 것을 더 이상 걱정하지 않습니다 -)

뭔가 함께 :

from functools import reduce 

sentinel = object() 
class ForwardDeclaration: 
    # Minimal behavior 
    def __init__(self, value=sentinel, dependencies=None): 
     self.dependencies = dependencies or [] 
     self.value = value 
    def __add__(self, other): 
     if isinstance(other, ForwardDeclaration): 
      return ForwardDeclaration(dependencies=self.dependencies + [self]) 
     return ForwardDeclaration(self.value + other) 

class MyMeta(type): 
    def __new__(metacls, name, bases, attrs): 
     for key, value in list(attrs.items()): 
       if not isinstance(value, ForwardDeclaration): continue 
       if any(v.value is sentinel for v in value.dependencies): continue 
       attrs[key] = reduce(lambda a, b: a + b.value, value.dependencies, 0) 

     return super().__new__(metacls, name, bases, attrs) 
    def __prepare__(mcs, name, bases): 
     return MetaDict() 

그리고, 클래스 계층 구조와 정확히 무슨 일을에 따라, 또한 조상에서 만든 _forward_dependencies 하나 개의 클래스의 DICT _forward_dependencies을 업데이트 rememebr. + 이외의 연산자가 필요하다면,주의해야 할 것은 연산자 자체에 대한 정보를 보관해야합니다.이 시점에서 sympy을 사용할 수도 있습니다.

+0

기존 이름 검색은'f_globals' 다음에 검색되는'__builtins__'을 포함해야합니다. – drhagen

+0

기존 이름을 검색하려면 외부 범위의 변수를 검사해야합니다 ('MyClass'가 함수에 정의되어 있고 함수 자체에서 정의 될 수있는 경우). 나는'inspect'을 들여다 보았지만 틀의 바깥 쪽을 걷는 법을 알 수 없었다. – drhagen

관련 문제