2017-11-30 3 views
1

제가 인스턴스화 한 객체가 기본적으로 하위 객체를 인스턴스화하는지 확인하려고합니다. 그래서 객체 검사/입력 오류 검사를 마칠 때까지이 방법을 계속 수행 할 수 있습니다. 목표는 다른 코드에 의해 자동 생성 된 특성이 무엇인지 모른 채 해당 개체의 정보를 채우는 것이므로 사전에 예고없이 변경 될 수 있습니다. 재귀 함수를 사용하여이 작업을 수행하려고합니다. 그것은 예와 좀 더 명확 수 있습니다 :인스턴스화 된 클래스 속성 유형을 확인하는 방법은 무엇입니까?

### content of B.py: 
class B: 
    def __init__(self): 
     self.Ba = '' 
     self.Bb = 0 


### content of A.py 
from B import B 

class A: 
    def __init__(self): 
     self.B = B() 
     self.Aa = '' 
     self.Ab = False 


### content of SDK.py 
from A import A 
primitiveTypes = (int, str, bool, type(None)) 

class G: 
    def createIt(self, **options) 
     fillMe = A() 
     fillMe = self.fillWithData(fillMe, options) 
     #Do stuff with fillMe (post in JSON format to REST API...) 

    def fillWithData(self, obj, options) 
     for a in dir(obj): 
      if not callable(a) and not a.startswith('_'): 
       if isinstance(a, primitiveTypes): 
        obj.__dict__.update({a: options.get(a)}) 
       else: 
        #This never gets executed because isinstance always returns true -> because it evaluates A.B as type string instead of being of type B 
        obj.__dict__.update({a: self.fillWithData(a, options)}) 

### content of test.py 
from SDK import G 

g = G() 
g.createIt(Aa='Aa', Ab=True, Ba='Ba', Bb=1) #Make correct input succeed 
g.createIt(Aa=1, Ab='', Ba=None, Bb=False) #Make incorrect input fail 

편집 : return 문을 제거, 그 내 의심을 지우기 주셔서 감사합니다.

나는 왜이 모든 다른 파일이 필요한지 설명합니다. 저는 현재 사람들이 REST API (사용자 생성, 로그인 생성, 물건 추가, 물건 수정, 역할, 권한 등과 같은 것들 삭제)와 상호 작용할 수 있도록 SDK를 작성하고 있습니다. 그래서 우리는 C#은 코드 생성기에 의해 python으로 변환되는 모델과 enum (메소드 없음)을 정의합니다. 위의 예에서 모델은 A.py 및 B.py입니다. SDK (G.py)로 가져 오기 때문에 SDK 메소드를 사용하는 test.py가 있습니다.

그래서이 문제는 다음과 같습니다 isinstance는 항상 true를 반환 - 그것은, 정말

+1

G.fillWithData()에서는 반환 값으로하는 모든 작업이 로컬 변수에 지정되기 때문에 반환 객체가 필요 없습니다 'G.creatIt()'에'fillMe'라는 이름을 붙였습니다.이 메쏘드는 아무 것도하지 않습니다 (그래서 버려집니다). 귀하의 질문에있는 코드의 마지막 두 줄에서 하나가 맞지만 다른 것은 맞지 않습니다. 각각의 경우에 무엇이 옳고 그 결과에 문제가 있는지 질문하고 설명하십시오. – martineau

+0

@martineau 사실,'self.fillWithData (a, options)}'는 재귀 적으로 호출되며'fillWithData'의'else' 본문에서 반환 값이 사용됩니다. 사용되었으므로 인스턴스 변수 여야합니까? 또는'G' 모두가 수업 일 필요는 없습니다. –

+0

@ juanpa.arrivillaga : 재귀 적으로 호출 된 경우에도 궁극적 인 반환 값은 초기 호출자가 사용하지 않습니다. – martineau

답변

1

그래서 형 B.이어야 할 때 객체 B는 String 형이다 점 몇 가지를 생각하기 때문에. 첫째, 대신에 "기본 형식"의 튜플을 만드는, 그래서 당신이을 처리 할 유형을 열거 더 의미가 있습니다 (참고, 파이썬 는 기본 유형이없는) :

In [1]: class B: 
    ...:  def __init__(self): 
    ...:   self.Ba = '' 
    ...:   self.Bb = 0 
    ...: 

In [2]: class A: 
    ...:  def __init__(self): 
    ...:   self.B = B() 
    ...:   self.Aa = '' 
    ...:   self.Ab = False 
    ...: 

In [3]: registered_types = A, B 

두 번째 , 클래스 G에 대한 필요가 없습니다, 그것은 어떤 상태도 보유하지 않고 단지 복잡합니다. 실제 사용 사례가 클래스 인 향상 될 경우, 앞서 가서 적응,하지만 난 명확성을 위해 단지 일반 기능을 사용하려고 해요 :

In [5]: SENTINEL = object() 

In [6]: def initialize(obj, **options): 
    ...:  for name, subobj in vars(obj).items(): 
    ...:   if isinstance(subobj, registered_types): 
    ...:    filled = initialize(subobj, **options) 
    ...:    setattr(obj, name, filled) 
    ...:   else: 
    ...:    val = options.get(name, SENTINEL) 
    ...:    if val is not SENTINEL: 
    ...:     setattr(obj, name, val) 
    ...:  return obj 
    ...: 

주, 나는 vars를 사용하는 모든 vars(obj)의 인스턴스 속성을 확인하면 dir (실제로는 프로덕션에서는 사용되지 않지만 디버깅에 대해서는 더 이상 사용되지 않음)으로 반환되는 내용과 함께 if x.startswith('_') 등을 확인하는 대신 원하는 결과 인 obj.__dict__이 반환됩니다. 그 (와 dir)는 dict 개체를 반환하고, 항상 이전에 처리 된 어떤 문자열있는 키를 반복 처리를 통해 직접 반복하기 때문에 또한, 나는,vars항목을 반복. 또한 주목할 점은 None 대신 유효한 임의의 SENTINEL 개체를 생성하는 것입니다.이 개체는 유효한 대상 일 수 있습니다. 또한 더러운 obj.__dict__.update(...) 구성 대신 setattr을 사용합니다.

마지막으로, 결과 : juanpa.arrivillaga @ 키이 값을 사용하는 것이 정말로 아닌 키 (LOL 지금 그런 식으로 넣어 그리 명확 소리) 해결에

In [7]: a = A() 

In [8]: a = initialize(a, Aa='Aa', Ab=True, Ba='Ba', Bb=1) 

In [9]: a.Aa, a.Ab, a.B.Ba, a.B.Bb 
Out[9]: ('Aa', True, 'Ba', 1) 
+0

감사합니다. 나는 그렇게하려고 시도했습니다. 그러나 문제는 여전히 발생한다고 생각합니다. ieinstance()는 항상 속성을 문자열로 평가하고 등록 된 유형은 평가하지 않습니다. 나는 틀린 일을해야만합니다. –

0

덕분에 여기 어떻게 우리는 우리의 측면에 결국 그것을 해결 :

def fillWithData(self, obj, options): 
keysToDelete = [] 
for name, subObj in vars(obj).items(): 
    if isinstance(subObj, primitiveTypes): 
     obj.__dict__.update({name: options.get(name)}) 
     if obj.__dict__[name] is None: #To remove keys with empty values later 
      keysToDelete.append(name) 
    elif not isinstance(subObj, types.FunctionType) and not isinstance(subObj, Enum) and not name.startswith('_'): 
     obj.__dict__.update({name: self.fillData(subObj, options)}) 
for k in keysToDelete: #Remove keys with empty values 
    del obj.__dict__[k] 
return obj 

다음 단계를 ... 그래서 실제로 null 값을 가진 개체를 입력하지 않고 다음 나중에이를 삭제이 일을 정리 ...;) -> 미래의 다른 질문 일 수도 있습니다 : P

+0

네, 키/문자열 문제와 관련하여 내 대답에 남긴 의견에 대해 답변을 드렸습니다. :) 한가지 제안은, 스타일의 문제로서'obatt .__ dict__'를 직접 조작하는 대신'getattr','setattr','delattr'과 같은 내장 함수를 사용하는 것이 더 깔끔해 보입니다. –

+0

예, 실제로 실제 코드에서 변경했습니다. –

관련 문제