2010-11-22 12 views
22

클래스를 검사하는 방법이 필요하므로 사용자 정의 클래스 속성 인 속성을 안전하게 식별 할 수 있습니다. 문제는 dir(), inspect.getmembers() 및 friends 같은 함수는 __class__, __doc__, __dict__, __hash__과 같이 사전 정의 된 클래스 속성을 비롯하여 모든 클래스 속성을 반환한다는 점입니다. 이것은 당연히 이해할 만하 며 무시할 멤버 목록을 만들 수 있다고 주장 할 수 있습니다.하지만 불행하게도이 미리 정의 된 속성은 다른 버전의 Python으로 바뀌므로 내 프로젝트를 Python 프로젝트에서 변경 가능하도록 만듭니다. - 나는 그것을 좋아하지 않는다.파이썬 클래스 속성 검사

예 : 예에서는

>>> class A: 
... a=10 
... b=20 
... def __init__(self): 
...  self.c=30 
>>> dir(A) 
['__doc__', '__init__', '__module__', 'a', 'b'] 
>>> get_user_attributes(A) 
['a','b'] 

I 그것은 인스턴스 속성 그대로 전용 사용자 정의 클래스는 [ 'A', 'B']없는 'C'를 속성을 검색하기 위해 안전하게 원하는 위에서 . 그래서 내 질문은 ... 아무도 위의 가상 함수 get_user_attributes(cls) 도와 줄래?

P. 나는 AST 레벨에서 클래스를 파싱함으로써 문제를 해결하기 위해 약간의 시간을 보냈다. 하지만 이미 구문 분석 된 객체를 AST 노드 트리로 변환하는 방법을 찾을 수 없습니다. 클래스가 바이트 코드로 컴파일되면 모든 AST 정보가 삭제됩니다. '특별한 속성'의 양쪽 끝에서 야콥

+1

당신은 당신이 AST에서 그것을 시도했다는 것을 언급했습니다. 즉, 클래스에 즉시 정의되고 슈퍼 클래스에 정의 된 속성 만 원한다는 의미입니까? 나는 당신이 'builtin'것들을 원하지 않는다는 것을 알고 있지만 나는이 문제에 대해 혼란스러워합니다. – aaronasterling

답변

27

이하 어려운 방법이다. 쉬운 방법이 있습니다. 왜 그것이 나에게 빨리 일어나지 않았는지 모르겠다.

import inspect 

def get_user_attributes(cls): 
    boring = dir(type('dummy', (object,), {})) 
    return [item 
      for item in inspect.getmembers(cls) 
      if item[0] not in boring] 

여기에 매우 강력한해야 시작을

def get_user_attributes(cls): 
    boring = dir(type('dummy', (object,), {})) 
    attrs = {} 
    bases = reversed(inspect.getmro(cls)) 
    for base in bases: 
     if hasattr(base, '__dict__'): 
      attrs.update(base.__dict__) 
     elif hasattr(base, '__slots__'): 
      if hasattr(base, base.__slots__[0]): 
       # We're dealing with a non-string sequence or one char string 
       for item in base.__slots__: 
        attrs[item] = getattr(base, item) 
      else: 
       # We're dealing with a single identifier as a string 
       attrs[base.__slots__] = getattr(base, base.__slots__) 
    for key in boring: 
     del attrs['key'] # we can be sure it will be present so no need to guard this 
    return attrs 

이있어. 기본적으로 object의 기본 하위 클래스에있는 특성을 무시하여 작동합니다. 그런 다음 전달 된 클래스의 mro를 가져 와서 역순으로 탐색하므로 하위 클래스 키가 수퍼 클래스 키를 덮어 쓸 수 있습니다. 키 - 값 쌍 사전을 리턴합니다. 당신은 키의 목록을 원하는 경우 다음 하위 클래스에 직접 정의 된 속성을 실제로 MRO를 통과 할 그냥하지 않으려면, inspect.getmembers에서 같은 값 튜플은 단지 그것의 파이썬 3

attrs.items() 또는 list(attrs.items()) 중 하나를 반환 쉽게 :

def get_user_attributes(cls): 
    boring = dir(type('dummy', (object,), {})) 
    if hasattr(cls, '__dict__'): 
     attrs = cls.__dict__.copy() 
    elif hasattr(cls, '__slots__'): 
     if hasattr(base, base.__slots__[0]): 
      # We're dealing with a non-string sequence or one char string 
      for item in base.__slots__: 
       attrs[item] = getattr(base, item) 
      else: 
       # We're dealing with a single identifier as a string 
       attrs[base.__slots__] = getattr(base, base.__slots__) 
    for key in boring: 
     del attrs['key'] # we can be sure it will be present so no need to guard this 
    return attrs 
+3

'__slots__'? ;-) –

+1

@Chris Morgan. 좋은 눈. 뭐가 잘못 됐니? – aaronasterling

+0

그렇게 생각하지 마십시오. 그때 나는 단지'__slots__'에 대해 생각하고 있었기 때문에 악마의 옹호자였습니다. –

6

두 번 밑줄

안부 2.0 전에 파이썬의 일부가되어있다. 가까운 장래에 언제든지 바꿀 수는 없을 것입니다.

class Foo(object): 
    a = 1 
    b = 2 

def get_attrs(klass): 
    return [k for k in klass.__dict__.keys() 
      if not k.startswith('__') 
      and not k.endswith('__')] 

print get_attrs(Foo) 

[ 'A', 'B'는]

+2

사용자가'__add__','__mul__','__iter__' 등을 정의한 것은 무엇입니까? – aaronasterling

+0

사용자가 정의한 사용자 인 경우 그 사용자도 필요합니다. 이미 파싱되고 바이트 컴파일 된 클래스에 대해 AST 트리를 확보 할 수 있습니까? –

+0

@jakob, 아니요. "살아있는"코드로 AST를 구하는 것은 불가능합니다. 파싱 된 후 소스가 더 이상 메모리에 남아 있지 않으며 파이썬에서 소스가 없어도 바이트 코드가 없어져서 AST가 없을 수 있습니다. – toriningen

2

새로운 스타일의 클래스를 사용하는 경우는 단순히 부모 클래스의 속성을 뺄 수 있을까?

class A(object): 
    a = 10 
    b = 20 
    #... 

def get_attrs(Foo): 
    return [k for k in dir(Foo) if k not in dir(super(Foo))] 

편집 : 그렇진. __dict__, __module____weakref__은 개체에서 상속되었을 때 나타나지만 개체 자체에는 없습니다.특별한 경우가있을 수 있습니다 - 나는 그들이 매우 자주 바뀔지는 의심 스럽습니다.

3

덕분에 aaronasterling, 당신은 나에게 내가 :-) 내 최종 클래스 속성 관리자의 기능은 다음과 같습니다 필요한 표현했다 :

def get_user_attributes(cls,exclude_methods=True): 
    base_attrs = dir(type('dummy', (object,), {})) 
    this_cls_attrs = dir(cls) 
    res = [] 
    for attr in this_cls_attrs: 
    if base_attrs.count(attr) or (callable(getattr(cls,attr)) and exclude_methods): 
     continue 
    res += [attr] 
    return res 

어느 반환 클래스 속성은 (exclude_methods = true)를 variabels 또는도를 검색 행동 양식. 위의 함수는 파이썬 클래스를 지원합니다.

/jakob

+0

좋은. 'boolean check '가 실패 할 때'callable'만이 실행되도록'exclude_methods'와'callable (getattr (...))'에 대한 체크를 바꾸는 것이 한가지 개선 사항입니다. – aaronasterling

+0

당신이 옳습니다 :-) –