2012-09-20 2 views
2

파이썬에서, 나는, 또는 속성의 상태를 변경 메서드 호출을 직접 속성에 할당하여 인스턴스의 상태를 변경할 수 있습니다 나 :파이썬 "호출"속성 (의사 속성)

foo.thing('baz') 

이 방식으로 작동하는 속성의 큰 숫자까지 확장 위의 양식을 모두 받아들이 클래스를 만들 수있는 좋은 방법이 있나요? (간단히 말해, 특별히 좋아하지 않는 구현 예를 보여줄 것입니다.) 이것이 바보 같은 API라고 생각하면 알려주세요.하지만 좀 더 구체적인 예가 있습니다. 내가 Document 클래스를 가지고 있다고 가정 해보십시오. Document의 속성은 title 일 수 있습니다. 그러나 title도 일부 상태 (글꼴, 글꼴 크기, 자리 표시, ...)를 갖고 싶어 할 수 있지만 평균 사용자는 제목을 문자열로 설정하고 끝내면 만족할 수 있습니다.

다음과 같이

class Title(object): 
    def __init__(self,text,font='times',size=12): 
     self.text = text 
     self.font = font 
     self.size = size 
    def __call__(self,*text,**kwargs): 
     if(text): 
      self.text = text[0] 
     for k,v in kwargs.items(): 
      setattr(self,k,v) 
    def __str__(self): 
     return '<title font={font}, size={size}>{text}</title>'.format(text=self.text,size=self.size,font=self.font) 

class Document(object): 
    _special_attr = set(['title']) 
    def __setattr__(self,k,v): 
     if k in self._special_attr and hasattr(self,k): 
      getattr(self,k)(v) 
     else: 
      object.__setattr__(self,k,v) 

    def __init__(self,text="",title=""): 
     self.title = Title(title) 
     self.text = text 

    def __str__(self): 
     return str(self.title)+'<body>'+self.text+'</body>' 

이 지금은이를 사용할 수 있습니다 :이 작업을 수행하는 한 가지 방법은하는 것

doc = Document() 
doc.title = "Hello World" 
print (str(doc)) 
doc.title("Goodbye World",font="Helvetica") 
print (str(doc)) 

이 구현은 (__special_attr으로)하지만 좀 지저분 보인다. 이것이 엉망인 API이기 때문에 그럴 수도 있습니다. 나는 잘 모르겠다. 더 좋은 방법이 있나요? 아니면 두드린 경로를이 곳으로 너무 멀리 두었습니까?

이 경우에도 @property을 사용할 수 있다는 것을 알았지 만,이 방법으로 동작하는 속성이 두 개 이상인 경우에는 확장이 잘되지 않습니다. getter를 작성해야합니다. 각각에 대한 세터.

+0

또한 실제 사용 사례에는 마크 업 언어가 포함되어 있지 않으므로 방금 더 많은 사용자에게 친숙한 예제로 사용했습니다. (제발'elementtree' 또는'xml.favorite.parser'를 사용하지 말아주세요 ...) – mgilson

답변

3

이전 답변보다 조금 어렵습니다.

설명자에 저장된 값은 모든 인스턴스간에 공유되므로 인스턴스 단위 데이터를 저장할 적절한 위치가 아닙니다. 또한, obj.attrib(...)는 두 단계로 수행됩니다

tmp = obj.attrib 
tmp(...) 

파이썬은 두 번째 단계는 따를 것을 사전에을 모르는 않습니다, 당신은 항상 호출하고 그 에 대한 참조를 가지고 뭔가를 반환해야하므로 부모 개체입니다.참조가 set 인수에 암시되어 다음과 같은 예에서

:

짧은에서
class CallableString(str): 
    def __new__(class_, set, value): 
     inst = str.__new__(class_, value) 
     inst._set = set 
     return inst 
    def __call__(self, value): 
     self._set(value) 

class A(object): 
    def __init__(self): 
     self._attrib = "foo" 
    def get_attrib(self): 
     return CallableString(self.set_attrib, self._attrib) 
    def set_attrib(self, value): 
     try: 
      value = value._value 
     except AttributeError: 
      pass 
     self._attrib = value 
    attrib = property(get_attrib, set_attrib) 

a = A() 
print a.attrib 
a.attrib = "bar" 
print a.attrib 
a.attrib("baz") 
print a.attrib 

: 당신이 투명하게 할 수 없습니다 원하는. 이 제한 사항을 해킹하지 말고 더 나은 Python 코드를 작성하십시오.

3

당신은 단순히 적절한 규칙을 따르는 descriptor class 작성하여 잠재적 수백 속성의@property를 사용하는 것을 방지 할 수 있습니다 기반으로 설명을 추가 할 수 그럼 당신은 Document__setattr__ 방법을 사용할 수 있습니다

# Warning: Untested code ahead 
class DocAttribute(object): 
    tag_str = "<{tag}{attrs}>{text}</{tag}>" 

    def __init__(self, tag_name, default_attrs=None): 
     self._tag_name = tag_name 
     self._attrs = default_attrs if default_attrs is not None else {} 

    def __call__(self, *text, **attrs): 
     self._text = "".join(text) 
     self._attrs.update(attrs) 
     return self 

    def __get__(self, instance, cls): 
     return self 

    def __set__(self, instance, value): 
     self._text = value 

    def __str__(self): 
     # Attrs left as an exercise for the reader 
     return self.tag_str.format(tag=self._tag_name, text=self._text) 

을 이 클래스에 승인 된 이름의 흰색 목록에있는 경우 (또는 도메인에 따라 금지 된 목록의 금지 목록에없는 경우) :

class Document(object): 
    # prelude 
    def __setattr__(self, name, value): 
     if self.is_allowed(name): # Again, left as an exercise for the reader 
      object.__setattr__(self, name, DocAttribute(name)(value)) 
+0

그 해결책은 디스크립터와 관련이 있다고 생각했습니다. *한숨*. 나는 그것이 정말로 파고 들어 그들을 사용하는 법을 배우려고 할 때라고 생각합니다. (그것은 내가 많은 시간을 보냈지 않은 파이썬의 모서리 중 하나입니다.) – mgilson

+0

그냥 명확하게 설명자가 인스턴스에 바인딩되어 있지 않으면 다른 클래스와 다르게 동작합니까? – mgilson

+0

@mgilson - 사소한 차이는 없습니다. '__get__','__set__','__delete__' 메쏘드는 클래스의 속성이나이 메쏘드를 가진 인스턴스에 접근하려고 시도 할 때 호출됩니다. 자세한 내용은 http://docs.python.org/howto/descriptor.html을 참조하십시오. –