2012-03-18 4 views
18

클래스 인 클래스 Klass이 있습니다. 내가 부모 클래스에서 동일한 속성의 수정 된 버전 인 클래스 속성 my_list 갖고 싶어하는 그 서브 클래스 SubKlass,이 : 그래서하위 클래스 본문에서 상위 클래스 속성에 액세스

class Klass(): 
    my_list = [1, 2, 3] 


class SubKlass(Klass): 
    my_list = Klass.my_list + [4, 5] # this works, but i must specify parent class explicitly 
    #my_list = super().my_list + [4, 5] # SystemError: super(): __class__ cell not found 
    #my_list = my_list + [4, 5] # NameError: name 'my_list' is not defined 


print(Klass.my_list) 
print(SubKlass.my_list) 

을하지 않고 부모 클래스의 속성에 액세스하는 방법은 무엇입니까 이름을 지정 했습니까?

UPDATE : http://bugs.python.org/issue11339 :

파이썬 이슈 트래커에 버그가 있습니다. 어느 시점에서 solved이되기를 바랍니다.

답변

10

수 없습니다.

파이썬에서 작동하는 클래스 정의는 다음과 같이 작동합니다.

  1. 해석자는 class 문 뒤에 코드 블록이 오는 것을 확인합니다.

  2. 새 네임 스페이스를 만들고 네임 스페이스에 해당 코드를 실행합니다.

  3. 결과로 생성 된 네임 스페이스, 클래스 이름, 기본 클래스 및 메타 클래스 (해당하는 경우)가 포함 된 type 내장 함수를 호출합니다.

  4. 결과를 클래스 이름에 할당합니다.

클래스 정의 내부의 코드를 실행하는 동안

, 당신은 기본 클래스가 무엇인지 모르는, 그래서 당신은 자신의 특성을 얻을 수 없습니다.

당신이 정의한 바로 다음에 클래스를 수정하십시오.


편집 : 속성을 업데이트하는 데 사용할 수있는 작은 클래스 데코레이터가 있습니다. 아이디어는 이름과 기능을 부여한다는 것입니다. 클래스의 모든 기본 클래스를 살펴보고 해당 이름의 속성을 가져옵니다. 그런 다음 하위 클래스에서 정의한 값과 기본 클래스에서 상속 된 값 목록이있는 함수를 호출합니다. 이 호출의 결과는 이름에 바인딩됩니다.

>>> def inherit_attribute(name, f): 
...  def decorator(cls): 
...    old_value = getattr(cls, name) 
...    new_value = f([getattr(base, name) for base in cls.__bases__], old_value) 
...    setattr(cls, name, new_value) 
...    return cls 
...  return decorator 
... 
>>> def update_x(base_values, my_value): 
... return sum(base_values + [my_value], tuple()) 
... 
>>> class Foo: x = (1,) 
... 
>>> @inherit_attribute('x', update_x) 
... class Bar(Foo): x = (2,) 
... 
>>> Bar.x 
(1, 2) 

아이디어는 당신이 Bar에서 (2,)x를 정의하는 것입니다 :

코드가 더 적합 할 수 있습니다. 그러면 장식자는 Bar의 하위 클래스를 살펴보고 모두 x을 찾은 다음 update_x을 호출합니다. 그래서 그것은 그들을 연결하여 그들을 결합

update_x([(1,)], (2,)) 

를 호출 한 후 다시 돌아 x에 결합한다. 말이 돼?

+2

메타 클래스는 3 번을 가로 챌 것이며'type'에 전달하기 전에'bases'를 수정할 수도 있습니다. 그래, 할 수 없어. – delnan

+0

예, 맞는 말이군요. 나는 그 자신을 (메타 클래스를 통해) 생각했지만, 계승 된 목록을 확장하는 것은 단지 예일 뿐이다. 목록을 확장하는 대신 목록에서 일부 항목을 삭제하려고 할 수 있습니다. 아니면 목록이 아니라 다른 유형 ... 광범위한 답변을 주셔서 감사합니다! – warvariuc

+0

루비에서 완전히 정의되기 전에 클래스에 액세스 할 수 있다고 들었습니다 ... – warvariuc

0

클래스 본문에서 오는 길은 없습니다. 자체가 사용 가능한 컨텍스트 (또는 클래스 자체)에서 __bases__을 검사 할 수 있습니다.

+0

아니요, '자체'를 사용할 수 없습니다. 그렇지 않으면 나는'super()'를 사용할 수 있었다. – warvariuc

+0

예, 자기가 클래스의 본문에서 사용할 수 없다는 것을 알고 있습니다. 그것이 내가 그렇게 말한 이유입니다. 답변을 제대로 읽을 수 없다면 왜 질문해야합니까? – Marcin

1

메타 클래스 또는 클래스 데코레이터를 사용할 수 있습니다. 파이썬 3으로

class Klass(object): 
    my_list = [1, 2, 3] 

class KlassMeta(type): 

    def __new__(cls, name, bases, attrs): 
     # grab last base class, which in this case is Klass 
     attrs['my_list'] = bases[-1].my_list + [4, 5] 
     return super(KlassMeta, cls).__new__(cls, name, bases, attrs) 

class SubKlass(Klass): 
    __metaclass__ = KlassMeta 

당신이 새로운 구문을 사용하여 메타 클래스를 선언 할 것 : 여기 파이썬 2.x에서의 메타 클래스를 사용하는 것이 방법이 다른 답변에서 보았 듯이

class SubKlass(Klass, metaclass=KlassMeta): 
    pass 
+0

사람들이 불필요하게 메타 클래스를 사용하도록 장려하지 마십시오. 그들은 복잡하고 10 배 중 9 회는 피해야한다고 생각합니다. 대부분의 사람들은 메타 클래스의 작동 방식을 알 필요가 없습니다. – gps

0

katriealex와 같은 : 아니오, 할 수 없습니다. 적어도 쉽게.

Q : 실제로 무엇을하려고합니까?

Q : 왜 이것을하고 싶습니까?

+1

Python에는 Alex Martelli가 '데이터 상속'이라고 부르는 멋진 기능이 있습니다. '__init__'에서'self.setting1 = self.setting1 + 5'라고 쓸 수 있습니다. 여기서'setting1'은 클래스 속성입니다. 부모 클래스 속성 일 수도 있습니다. 후자의 예제에서는 특별한 코드를 사용하지 않았기 때문에 클래스 속성에서 이름별로 조회가 자동으로 수행됩니다. 클래스 본문 내에서 동일한 기능을 원합니다. – warvariuc

+1

@warvariuc는'self.setting1'이라는 인스턴스 속성을 생성 할 것이고, 그러면 어떤 클래스 속성보다 먼저 액세스 할 것입니다. 그래서 그것이 어떤 상황에서 도움이 될지라도 사람들이 클래스 속성을 설정할 수는 없습니다. – Maximilian

2

@katrielalex의 답변에 따르면 my_list은 기본적으로 의 네임 스페이스에 있지 않으므로 클래스가 만들어졌습니다. 당신이 메타 클래스에 뛰어하려는 경우, 파이썬 3하지만 할 수있는 수동 네임 스페이스에 my_list을 추가하는 것입니다 :이 예는 아마 아직 잘 다이아몬드 모양의 상속 구조를 처리하지 않습니다

class Meta(type): 
    def __prepare__(name, bases, **kwds): 
     print("preparing {}".format(name), kwds) 
     my_list = [] 
     for b in bases: 
      if hasattr(b, 'my_list'): 
       my_list.extend(b.my_list) 
     return {'my_list': my_list} 

class A(metaclass=Meta): 
    my_list = ["a"] 

class B(A): 
    my_list.append(2) 

print(A().my_list) 
print(B().my_list) 

참고.

새로운 __prepare__ 속성은 PEP 3115에 정의되어 있습니다.

+0

흥미로운 접근 방식 – warvariuc

관련 문제