2014-11-05 1 views
1

사용자 시스템의 다양한 데이터 모음과 인터페이스하기 위해 여러 가지 사용자 지정 클래스가있는 프로젝트에서 작업하고 있습니다. 이 클래스는 사용자 지향 속성으로 만 properties입니다. 이러한 속성 중 일부는 상당한 리소스를 필요로하므로 생성 코드를 한 번만 실행하고 반환 된 값을 디스크에 저장 (즉, 캐시)하여 후속 실행에서 더 빠르게 검색 할 수 있습니다. 약자로, 이것이 내가이 달성하고 어떻게 : 나는 그것을 필요로하는디스크의 속성 사전을 자동으로 업데이트하는 클래스 데코레이터?

def stored_property(func): 
    """This ``decorator`` adds on-disk functionality to the `property` 
    decorator. This decorator is also a Method Decorator. 

    Each key property of a class is stored in a settings JSON file with 
    a dictionary of property names and values (e.g. :class:`MyClass` 
    stores its properties in `my_class.json`). 
    """ 
    @property 
    @functools.wraps(func) 
    def func_wrapper(self): 
     print('running decorator...') 
     try: 
      var = self.properties[func.__name__] 
      if var: 
       # property already written to disk 
       return var 
      else: 
       # property written to disk as `null` 
       return func(self) 
     except AttributeError: 
      # `self.properties` does not yet exist 
      return func(self) 
     except KeyError: 
      # `self.properties` exists, but property is not a key 
      return func(self) 
    return func_wrapper 

class MyClass(object): 
    def __init__(self, wf): 
     self.wf = wf 
     self.properties = self._properties() 

    def _properties(self): 
     # get name of class in underscore format 
     class_name = convert(self.__class__.__name__) 
     # this is a library used (in Alfred workflows) for interacted with data stored on disk 
     properties = self.wf.stored_data(class_name) 
     # if no file on disk, or one of the properties has a null value 
     if properties is None or None in properties.values(): 
      # get names of all properties of this class 
      propnames = [k for (k, v) in self.__class__.__dict__.items() 
         if isinstance(v, property)] 
      properties = dict() 
      for prop in propnames: 
       # generate dictionary of property names and values 
       properties[prop] = getattr(self, prop) 
      # use the external library to save that dictionary to disk in JSON format 
      self.wf.store_data(class_name, properties, 
           serializer='json') 
     # return either the data read from file, or data generated in situ 
     return properties 

    #this decorator ensures that this generating code is only run if necessary 
    @stored_property 
    def only_property(self): 
     # some code to get data 
     return 'this is my property' 

이 코드는 정확하게 작동하지만, 여전히 나는 현재 (이 기능이 필요 특징으로 수동으로 각 클래스에 _properties(self) 방법을 추가하는 저를 강제로, 나는 3)을 가지고있다. 내가 원하는 것은이 기능을 모든 클래스에 "삽입"하는 방법입니다. 나는 클래스 데코레이터가이 일을 끝낼 수 있다고 생각하지만, 내가 할 수도있는 것처럼 시도해 보았다. 어떻게 그 일을 처리 할지를 알 수 없다.

명확성을 위해 (그리고 내가 원하는 것을 얻기위한 데코레이터가 최선의 방법이 아닌 경우) 필자는 이후의 전반적인 기능을 설명하려고 노력할 것입니다. 몇 가지 속성이 포함 된 클래스를 작성하고 싶습니다. 이러한 속성의 값은 다양한 정도의 복잡한 코드를 통해 생성됩니다 (한 예로, 특정 앱의 기본 설정 파일을 검색 한 다음 3 가지 기본 설정 (검색 결과 중 하나는 존재할 수도 있고 존재하지 않을 수도 있음)을 검색하고 가장 우수한 단일 이러한 기본 설정의 결과). 속성 코드의 본문에 데이터 찾기 알고리즘 만 포함되기를 원합니다. 하지만 그 속성에 액세스 할 때마다 알고리즘 코드를 실행하고 싶지는 않습니다. 한 번 값을 생성하고 나면 디스크에 기록한 다음 모든 후속 호출에서이 값을 읽으 려합니다. 그러나 각 값을 자체 파일에 쓰지 않으려 고합니다. 위의 예에서 my_class.json에 하나의 키, 값 쌍이있는 JSON 사전이 포함될 수 있도록 단일 클래스의 모든 속성에 대한 모든 값의 사전을 하나의 파일에 작성해야합니다. 속성에 직접 액세스 할 때 먼저 디스크의 사전에 이미 있는지 확인해야합니다. 그렇다면 단순히 그 값을 읽고 리턴하십시오. 존재하지만 null 값을 가진 경우 생성 코드 (즉, 속성 메소드에 실제로 작성된 코드)를 실행하고 지금 찾을 수 있는지 확인하십시오 (그렇지 않은 경우 메소드는 None을 반환하고 다시 한번 파일에 기록). 사전이 존재하고 해당 속성이 키가 아닐 경우 (현재 코드는 실제로 가능하지 않지만 미안한 것보다 안전합니다) 생성 코드를 실행하고 키, 값 쌍을 추가하십시오. 사전이 없으면 (즉, 클래스의 첫 번째 인스턴스 생성시) 모든 속성에 대해 모든 생성 코드를 실행하고 JSON 파일을 만듭니다. 이상적으로 코드는 모든 생성 코드를 다시 실행하지 않고 (즉, _properties()을 다시 실행하지 않고) JSON 파일에서 하나의 속성을 업데이트 할 수 있습니다.

나는 이것이 약간 특이한 것을 알고 있지만, 나는 인간의 읽을 수있는 내용과 우아한 코드를 모두 필요로한다. 나는 정말로 내 목표를 타협하지 않아도됩니다. 바라기를 원하는 것의 설명은 충분히 명확합니다. 그렇지 않다면, 내가 이해하지 못하는 것에 대한 의견을 나에게 알려 주어 명확히하려고 노력할 것입니다. 하지만 클래스 데코레이터는 아마도 어떤 클래스에 _properties() 메서드를 삽입하고 인스턴스화시 실행하고 해당 값을 클래스의 properties 특성에 매핑하여 클래스 데코레이터를 얻을 수 있다고 생각합니다.

답변

1

어쩌면 내가 누락되었지만, 귀하의 _properties 메서드가 주어진 클래스의 속성과 관련이없는 것으로 보입니다. 나는 그것을 기본 클래스에 넣고 각각의 클래스를 @stored_property 메소드 서브 클래스로 가지고 있습니다. 그런 다음 _properties 메서드를 복제 할 필요가 없습니다.

class PropertyBase(object): 
    def __init__(self, wf): 
     self.wf = wf 
     self.properties = self._properties() 

    def _properties(self): 
     # As before... 

class MyClass(PropertyBase): 
    @stored_property 
    def expensive_to_calculate(self): 
     # Calculate it here 

직접 (어쩌면 당신은 이미 다른 기본 클래스가 필요합니다) 어떤 이유로 PropertyBase를 서브 클래 싱 할 수없는 경우

, 당신은 아마 믹스 인을 사용할 수 있습니다.실패하면 _properties에서 인스턴스/클래스 및 워크 플로 개체를 허용하고 각 클래스에 대해 __init__에 명시 적으로 호출합니다.

+0

위대한 포인트. 나는 단지'_properties()'메소드를 완전히 추상화 시켰을 뿐이며 일단 추상화하면 기본 클래스를 사용할 수 있다는 것을 깨닫지 못했습니다. 이것은 또한 클래스 장식자를 사용하는 것보다 더 깨끗하고 의사 소통이되는 것처럼 보일 것입니다. – smargh

관련 문제