2012-02-24 2 views
2

이진 로그 파일을 구문 분석하고 있습니다. 로그 파일은 다음과 같이 형식화됩니다. 10 바이트마다 레코드이며, 레코드의 첫 번째 바이트는 레코드 유형이고, 다음 5 바이트는 시간 소인이며, 마지막 4 바이트는 레코드 유형 특정 데이터입니다.수퍼 클래스의 생성자는 서브 클래스의 인스턴스를 대신 반환 할 수 있습니까?

은 현재 내가 뭐하는 거지 다음

# read the input binary stream 
with open(filename, mode='rb') as trace_stream: 
    # create an empty list of trace records 
    trace = [] 
    # iterate over each record in the binary stream 
    for record_type, record_data in yield_record(trace_stream, 
               size=RECORD_LENGTH): 
     # create a new record instance 
     if record_type == SEN_RECORD: 
      new_record = sen_record(record_data) 
     elif record_type == DSP_RECORD: 
      new_record = dsp_record(record_data) 
     elif record_type == USO_RECORD: 
      new_record = uso_record(record_data) 
     elif record_type == SDM_RECORD: 
      new_record = sdm_record(record_data) 
     elif record_type == DOC_RECORD: 
      new_record = doc_record(record_data) 
     elif record_type == DAT_RECORD: 
      new_record = dat_record(record_data) 
     elif record_type == LAT_RECORD: 
      new_record = lat_record(record_data) 
     elif record_type == SWI_RECORD: 
      new_record = swi_record(record_data) 
     elif record_type == FTL_RECORD: 
      new_record = ftl_record(record_data) 

     # append this new record to our trace 
     trace.append(new_record) 

경우 sen_record, dsp_record, uso_record 등 일반적인 기록 클래스의 모든 하위 클래스가

내가하고 싶은 무엇

것은 다음은 있습니다 :

레코드 클래스 생성자에게 레코드 유형을 결정하고 적절한 클래스 인스턴스를 만드는 작업을 수행하게하십시오. 이상적으로 나의 "메인"루틴은 레코드 유형에 대해 알 필요가 없다.

이렇게 할 방법이 있습니까?

+0

당신은'기록을 오버라이드 (override) 할 수있을 것 '['__new__'] (http://docs.python.org/reference/datamodel.html#object.__new__) 메서드를 사용하여 하위 클래스의 인스턴스를 반환 할 수 있습니다. – Darthfett

+0

그런데 파이썬의 ['struct'] (http://docs.python.org/library/struct.html) 모듈에서 이진 데이터의 압축을 처리 했습니까? – katrielalex

+0

@katrielalex 각 레코드 유형이 데이터를 다르게 해석하므로 각 레코드 하위 클래스의 생성자에서 struct 모듈을 사용하고 있습니다. – ACRL

답변

6

는 그냥 어딘가에 매핑

record_types = {SEN_RECORD: sen_record, 
       DSP_RECORD: dsp_record, 
       USO_RECORD: uso_record, 
       SDM_RECORD: sdm_record, 
       DOC_RECORD: doc_record, 
       DAT_RECORD: dat_record, 
       LAT_RECORD: lat_record, 
       SWI_RECORD: swi_record, 
       FTL_RECORD: ftl_record} 

를 저장하기 위해 간단하고 올바른 레코드 유형을 찾아 볼 것을 사용합니다. (클래스 단지는 객체이기 때문에 당신이 사전에 넣어 수 있도록, 당신은이 작업을 수행 할 수 없습니다.)

을 구체적으로 당신의 더 복잡한 방법이 있습니다

new_record = record_types[record_type](record_data) 

할 것 (서브 클래스가 생성 될 때 동적으로 생성되고 슈퍼 클래스에 자동으로 등록되기를 원한다면) 이렇게하면되지만, 상황에 따라 서브 클래스를 사용할 필요는 없습니다.

+1

나는 이것을 좋아한다. 완벽하지는 않지만 내가 가지고있는 것보다 훨씬 깨끗합니다. – ACRL

+0

"완벽하지 않다"?? !! 대안은 어떤 기본 클래스에서'__new__'을 구현 한 다음, 기본 클래스를 하위 클래스에 대한 지식으로 오염시켜 인스턴스화 할 유형을 알 수 있도록하는 것입니다. Python 용어로, 나는 더 가벼운 답을 모른다. – PaulMcG

+0

상수'SEN_RECORD '등이 0부터 10까지의 정수라고 말했기 때문에'record_types'는 다른 코드를 변경하지 않고 사전 대신 간단한 배열이 될 수 있습니다. 물론 상수를 변경하면'record_types'를 변경해야 할 수도 있습니다. – wberry

3

이렇게하는 방법이 있지만 사용하지 않는 것이 좋습니다. 단순히 팩토리 함수를 사용하여 레코드를 기반으로 올바른 유형의 객체를 생성하고 반환하는 것이 좋습니다.

def create_record(record_type): 
    if record_type == SEN_RECORD: 
    return sen_record(record_data) 
    ... 

는 객체 생성의 동작을 재정의하려면 클래스의 __new__ 방법을 제공 할 수있다. 자세한 내용은 official docs을 참조하십시오. 그러나 다시, 나는 이것을 추천하지 않는다; 가장 전문화 된 애플리케이션을 제외한 모든 애플리케이션에서이 기능을 사용하면 불이 나옵니다.

2

파이썬의 동적 인 기능을 배우는 것이 좋기 때문에 이것을 마술처럼 할 수 있습니다. 실제 코드에서 이런 종류의 일을하는 것은 좋은 생각이 아닙니다. 그것은 깨지기 쉽고 예기치 않은 행동으로 이어질 수 있습니다. 그것은 또한 나쁜 습관 인 putting data in your variable names입니다.

또한 작업 시퀀스에 문제가 있기 때문에 원하는 작업을 수행 할 수 없습니다. 특히, Record을 정의하면 해당 서브 클래스를 아직 정의 할 수 없습니다 (분명히). 따라서 당신은 그 시점에 디스패치 로직을 클래스에 넣을 수 없습니다. 그러나 이제는 "하위 클래스 정의, 디스패치 설정 완료"라고 말할 수있는 다른 시간이 없으므로 소스 코드에 모든 하위 클래스 정의를 하드 코딩해야합니다. 그렇다면 내 다른 대답과 마찬가지로 딕트를 하드 코딩 할 수도 있습니다.

어쨌든 그 면책 조항과 함께, 여기에 마술이 있습니다. (새로운 스타일의 클래스에서만 작동합니다.)

@classmethod 
def update_record_types(cls): 
    cls.records = {c.__name__.upper(): c for c in cls.__subclasses__()} 

그런 다음 Record.__init__ 그냥 당신이 Record.update_record_types()를 호출하여 언제든지 업데이트 할 수 있습니다 class 속성 records을 말한다.


편집 :이 사용법을 알려주세요.

>>> class Record(object): 
...  @classmethod 
...  def update_record_types(cls): 
...   cls.records = {c.__name__.upper(): c for c in cls.__subclasses__()} 
... 
>>> # define some record types, each with their own __init__ 
>>> class sen_record(Record): pass 
>>> class dsp_record(Record): pass 
>>> class uso_record(Record): pass 
>>> 
>>> # update the listing of record types 
>>> Record.update_record_types() 
>>> 
>>> # look up the one you want 
>>> Record.records["SEN_RECORD"] 
<class '__main__.sen_record'> 
+0

이것은 학습 연습으로 키치하고 재미 있습니다. +1 – wberry

1

다음은 eval을 사용하는 방법입니다. 귀하의 데이터에 대한 가정을해야만합니다. 귀하의 record_type 필드는 "SEN", "DSP"등의 값 중 하나입니다. 또한 파서가 데이터 확인을 수행한다고 가정합니다. 코드는 거대한 보안 구멍이 될 것입니다. (그대로,이 사전 또는 공장 기능) 대 (이러한 경우 성능이 저하이다, 그러나 당신이 원하는대로 "마법"의 일종이다.)

class SEN_record(record): 
    ... 

class DSP_record(record): 
    ... 

... # other record subclasses similarly defined here 

# read the input binary stream 
with open(filename, mode='rb') as trace_stream: 
    # create an empty list of trace records 
    trace = [] 
    # iterate over each record in the binary stream 
    for record_type, record_data in yield_record(trace_stream, 
               size=RECORD_LENGTH): 
     trace.append(eval("%s_record(record_data)" % (record_type,))) 
+0

'(globally()') 클래스를 찾아 보는 것만으로도 보안 성이 크게 떨어질 것 같지 않습니다. – katrielalex

+0

Slick, 그러나 record_type은 0에서 10 사이의 정수입니다. 그래서 나는 적절한 문자열을 얻기 위해 검색을해야 할 것입니다. 그럼에도 불구하고 멋지다. – ACRL

관련 문제