2016-08-25 5 views
0

host_name, kernel_version, bios_version 등과 같은 컴퓨터에 대한 세부 정보를 검색하기위한 클래스를 만들고 있습니다. 일부 세부 정보는 다른 것보다 수집하는 데 더 많은 비용이 들기 때문에 get_ * 함수를 사용하여 검색하지만 개체가 필요할 경우 결과를 해당 캐시에 보관합니다. 나는 커널 버전은 그래서 검색 할 수 있도록 사전 객체처럼 보이게을 구현 고려하고 :고정 된 읽기 전용 필드에 대한 동적 특성 또는 사용자 지정 사전

system = System() 
kver = system['kernel_version'] 

이 데이터를 검색하기 위해 내부적으로 인스턴스 방법 get_kernel_version(self)를 호출합니다. 위의 인스턴스화 된 객체에서 두 번째로 커널 버전을 검색하면 원래 호출의 캐시 된 결과가 get_kernel_version(self)으로 반환됩니다. 이러한 모든 키/값 쌍은 읽기 전용이며 사용 가능한 get_ * 메서드에 따라 고정 된 수가 있으며 나중에 추가 할 수있는 새 키가 없으므로 일반 사전과 같지 않습니다. values() 함수와 같은 함수를 호출 할 필요가 없어야합니다.이 함수는 모든 get_ * 함수가 불필요하게 히트되도록합니다. 또한 구문은 내가 원하는 것보다 좀 더 장황하다. 대신 system.kernel_version을 사용하면이 사용 사례에 더 자연스럽게 보입니다.

더 나은 접근 방법이 클래스 인스턴스에 동적 속성을 사용할지 여부를 고려하고 있습니다. 그러나 모든 특성 목록을 검색하는 자연스러운 방법이 필요하지만이를 지원하는 내부 메서드는 필요하지 않습니다. 아마 __dir__ 특수 메서드를 사용하여 keys() 사전 목록과 비슷한 목록을 반환 할 것입니다. 목록에서 kernel_version 및 host_name을보고 싶지만 __class__ 또는 get_kernel_version이 아닙니다. 이것은 __dir__의 정의에 대한 권장 관행에 어긋나는 것 같습니다. 그래서 이것이 올바른 접근 방법인지 확실하지 않습니다.

적절한 속성이 이미 정의되어 있지 않은 경우 유일한 작업이 get_ * 함수를 사용하여 구체적인 클래스를 호출하는 프록시 클래스 인스턴스를 반환 할 수 있습니다. 여기

내가 사전 접근 방식을 구현하는 실험을하고있어 버전의 예입니다

['host_name', 'kernel_version'] 
host_name: localhost 
kernel_version: 4.7.0 
"'System' object has no attribute 'get_bios'" 
Read-only 

은 위의 코드는 아직 캐싱을 구현하지 않습니다

class System(dict): 
    def __getitem__(self, key): 
     try: 
      return getattr(self, 'get_'+key)() 
     except AttributeError as ex: 
      raise KeyError(ex.message) 
    def __setitem__(self, key, value): 
     raise Exception('Read-only') 
    def __delitem__(self, key, value): 
     raise Exception('Read-only') 
    def keys(self): 
     return [ x[4:] for x in dir(self) if x.startswith('get_') ] 
    def get_host_name(self): 
     return 'localhost' 
    def get_kernel_version(self): 
     return '4.7.0' 

system = System() 
print repr(system.keys()) 
for key in system.keys(): 
    print '{0}: {1}'.format(key, system[key]) 

try: 
    system['bios'] 
except Exception as ex: 
    print str(ex) 

try: 
    system['kernel_version'] = '5.0' 
except Exception as ex: 
    print str(ex) 

다음과 같은 출력을 생성 어떤 값은 아직 있지만 추가하기 쉽습니다. 그러나, 나는 이것을 속성으로해야하는 것처럼 느껴진다. 나는 단지 내가 그렇게했을 때 나는 keys()과 같은 기능을 에뮬레이트하기 위해 __dir__을 남용해야하는지 확신 할 수 없다.

읽기 전용 사전을 에뮬레이션하거나 동적 속성이있는 클래스 인스턴스를 제공해야합니까?

+0

당신은 당신을 허용 것이다 (https://code.activestate.com/recipes/576972-attrdict/)이 ['AttrDict' 레시피] 같은 것을 사용할 수 있습니다 필드에 접근하기 위해 도트 표기법을 사용하고 수정을 통해 읽기 전용으로 만드는 또 다른 접근법은 내용에 대한 액세스를 제어하기 위해'__getattr __() '을 사용하는 것입니다. 두 기술 모두 캐싱을 지원할 수도 있습니다. 편집] 귀하의 질문에 좀 더 구체적으로, 특히 샘플 코드를 추가하여, 나는 아마도 추가 세부 사항을 제공 할 수 있습니다. – martineau

+0

@martineau 내가 뭘하는지에 대한 기본 아이디어의 예제를 추가했습니다 에 대한. – penguin359

+0

코드를보고 좀 더 생각해 보면, 기본적으로 괜찮은 것 같아요. 당신이 가지고있는 keys() 메소드에 대한 데이터를 자동으로 생성하도록 향상시킬 수 있습니다. 아래 [내 대답] (http://stackoverflow.com/a/39171820/355230)을 참조하십시오. – martineau

답변

0

내가 사용하는 읽기 전용 사전 식 하위 클래스 접근법을 고수하는 것이 좋습니다. 그러나 특정 하위 클래스를 파생시키는 일반 읽기 전용 사전 슈퍼 클래스를 만들고 keys() 메서드에서 반환 된 값을 만들기 위해 메타 클래스를 사용하여 구현을 다소 향상시킬 수 있습니다. 두 가지 모두하는 것은 아래에 나와 있습니다.

즉, (더 이상은 __dir__ 속성이 아닙니다)을 "악용 할 필요가 없습니다. 일반 MetaReadonlyDictReadonlyDict 클래스를 재사용하여 다른 유사한 유형을 생성 할 수도 있습니다.

class MetaReadonlyDict(type): 
    def __new__(mcls, classname, bases, classdict): 
     classobj = type.__new__(mcls, classname, bases, classdict) 
     prefix = classdict['prefix'] 
     _keys = set(name[len(prefix):] for name in classdict 
             if name.startswith(prefix)) 
     setattr(classobj, 'keys', lambda self: list(_keys)) # define keys() 
     return classobj 

class ReadonlyDict(dict): 
    def __getitem__(self, key): 
     try: 
      return getattr(self, self.prefix + key)() 
     except AttributeError as ex: 
      raise Exception(
       "{} object has no {!r} key".format(self.__class__.__name__, key)) 
    def __setitem__(self, key, value): 
     verb = "redefined" if key in self else "defined" 
     raise Exception(
      "{} object is read-only: {!r} " 
      "key can not be {}".format(self.__class__.__name__, key, verb)) 
    def __delitem__(self, key): 
     raise Exception(
      "{} object is read-only: {!r} " 
      "key can not be deleted".format(self.__class__.__name__, key)) 
    def __contains__(self, key): 
     return key in self.keys() 

class System(ReadonlyDict): 
    __metaclass__ = MetaReadonlyDict 
    prefix = '_get_' 

    def _get_host_name(self): 
     return 'localhost' 
    def _get_kernel_version(self): 
     return '4.7.0' 

system = System() 
print('system.keys(): {!r}'.format(system.keys())) 
print('values associated with system.keys():') 
for key in system.keys(): 
    print(' {!r}: {!r}'.format(key, system[key])) 

try: 
    system['bios'] 
except Exception as ex: 
    print(str(ex)) 

try: 
    system['kernel_version'] = '5.0' 
except Exception as ex: 
    print(str(ex)) 

try: 
    del system['host_name'] 
except Exception as ex: 
    print(str(ex)) 

출력 :

system.keys(): ['kernel_version', 'host_name'] 
values associated with system.keys(): 
    'kernel_version': '4.7.0' 
    'host_name': 'localhost' 
System object has no 'bios' key 
System object is read-only: 'kernel_version' key can not be redefined 
System object is read-only: 'host_name' key can not be deleted