2012-05-03 4 views
8

일반적으로 Python 서술자는 클래스 속성으로 정의됩니다. 그러나 필자의 경우 모든 객체 인스턴스가 입력에 따라 다른 설명자 세트를 갖기를 바랍니다. 예를 들면 다음과 같습니다.인스턴스 별 속성 설명자를 만드시겠습니까?

class MyClass(object): 
    def __init__(self, **kwargs): 
    for attr, val in kwargs.items(): 
     self.__dict__[attr] = MyDescriptor(val) 

각 개체에는 인스턴스화 시간에 결정되는 서로 다른 속성 집합이 있습니다. 이것들은 일회용 객체이기 때문에 하위 클래스로 분류하는 것이 편리하지 않습니다.

tv = MyClass(type="tv", size="30") 
smartphone = MyClass(type="phone", os="android") 

tv.size # do something smart with the descriptor 

개체에 대한 설명자가 작동하지 않는 것 같습니다. 속성에 액세스하려고하면 다음과 같은 메시지가 표시됩니다.

<property at 0x4067cf0> 

이것이 작동하지 않는 이유는 무엇입니까? 주변에 어떤 문제가 있습니까?

+0

왜 MyClass의 다른 서브 클래스를 오버라이드 된 디스크립터와 함께 사용하지 않는가? – KurzedMetal

+0

더 많은 예제를 추가하기 위해 편집되었습니다. 나는 인스턴스 별 디스크립터를 수행 할 수 없다고 생각한다. 나는 __getattr__을 사용하여 그 주위에서 일했다. 그래도 기본 언어 제약 조건을 이해하지 못합니다. –

+0

설명자는 클래스 수준에서만 작동합니다. 죄송합니다. –

답변

2

개체 클래스에 설명자를 지정해야하므로이 기능이 작동하지 않습니다. 당신이

obj.attr 
=> type(obj).__getattribute__(obj, 'attr') is called 
=> obj.__dict__['attr'] is returned if there else: 
=> type(obj).__dict__['attr'] is looked up 
if this contains a descriptor object then this is used. 

를 작성하는 경우 유형 dictionairy이 디스크립터 고개를하지 개체 dictionairy 때문에

class Descriptor: 

    def __get__(...): 
     # this is called when the value is got 

    def __set__(... 
    def __del__(... 

그래서 그것은 작동하지 않습니다.

  1. 클래스에 기술자를 넣고는, 예를 들어 사용합니다

    가능한 작업 방법이 있습니다 obj.xxxattr 값을 저장합니다. 설명자 동작이 하나만있는 경우이 방법이 효과적입니다.

  2. 는 discriptors에 응답 않은 setattrgetattrdelattr을 덮어 씁니다.

  3. 개체 dictionairy에 저장된 설명자에 응답하는 클래스에 디스크リプタ를 넣습니다.

2

설명자를 잘못 사용하고 있습니다.

설명자는 인스턴스 수준에서 의미가 없습니다. 결국 __get__/__set__ 방법을 사용하면 클래스의 instance에 액세스 할 수 있습니다.

정확히 무엇을하고 싶은지 알지 못하면 __set__ 메서드 내에서 인스턴스 당 로직을 넣고 "발신자/인스턴스"를 확인하고 이에 따라 행동하는 것이 좋습니다.

그렇지 않으면 우리가 대안을 제안 할 수 있도록 달성하려는 내용을 알려주십시오.

1

는 설명을위한 파이썬 만 검사하지 않는 경우에, 클래스에 속성을 찾는 경우 때문에 작동하지 않는 이유에 대한 사용 사례처럼 보인다; 문제의 방법은 다음과 같습니다

인스턴스뿐만 아니라 클래스에 descriptor protocol을 구현하기 위해 클래스에 이러한 메소드를 오버라이드 (override) 할 수 있습니다 :

# do not use in production, example code only, needs more checks 
class ClassAllowingInstanceDescriptors(object): 
    def __delattr__(self, name): 
     res = self.__dict__.get(name) 
     for method in ('__get__', '__set__', '__delete__'): 
      if hasattr(res, method): 
       # we have a descriptor, use it 
       res = res.__delete__(name) 
       break 
     else: 
      res = object.__delattr__(self, name) 
     return res 
    def __getattribute__(self, *args): 
     res = object.__getattribute__(self, *args) 
     for method in ('__get__', '__set__', '__delete__'): 
      if hasattr(res, method): 
       # we have a descriptor, call it 
       res = res.__get__(self, self.__class__) 
     return res 
    def __setattr__(self, name, val): 
     # check if object already exists 
     res = self.__dict__.get(name) 
     for method in ('__get__', '__set__', '__delete__'): 
      if hasattr(res, method): 
       # we have a descriptor, use it 
       res = res.__set__(self, val) 
       break 
     else: 
      res = object.__setattr__(self, name, val) 
     return res 
    @property 
    def world(self): 
     return 'hello!' 

상기 클래스는 아래와 같이 사용되는 경우 :

huh = ClassAllowingInstanceDescriptors() 
print(huh.world) 
huh.uni = 'BIG' 
print(huh.uni) 
huh.huh = property(lambda *a: 'really?') 
print(huh.huh) 
print('*' * 50) 
try: 
    del huh.world 
except Exception, e: 
    print(e) 
print(huh.world) 
print('*' * 50) 
try: 
    del huh.huh 
except Exception, e: 
    print(e) 
print(huh.huh) 

결과는 :

안녕하십니까!

?


속성을

인사를 삭제할 수 없습니다!


정말 속성을 삭제할 수 없습니다 ?

1

작성한 클래스를 사용하여 인스턴스를 동적으로 생성합니다. 유스 케이스에 맞을 수 있습니다.

def make_myclass(**kwargs): 

    class MyDescriptor(object): 
     def __init__(self, val): 
      self.val = val 

     def __get__(self, obj, cls): 
      return self.val 

     def __set__(self, obj, val): 
      self.val = val 

    cls = 'class MyClass(object):\n{}'.format('\n'.join(' {0} = MyDescriptor({0})'.format(k) for k in kwargs)) 

    #check if names in kwargs collide with local names 
    for key in kwargs: 
     if key in locals(): 
      raise Exception('name "{}" collides with local name'.format(key)) 

    kwargs.update(locals()) 
    exec(cls, kwargs, locals()) 
    return MyClass() 

테스트;

In [577]: tv = make_myclass(type="tv", size="30") 

In [578]: tv.type 
Out[578]: 'tv' 

In [579]: tv.size 
Out[579]: '30' 

In [580]: tv.__dict__ 
Out[580]: {} 

그러나 인스턴스는 다른 클래스입니다.

In [581]: phone = make_myclass(type='phone') 

In [582]: phone.type 
Out[582]: 'phone' 

In [583]: tv.type 
Out[583]: 'tv' 

In [584]: isinstance(tv,type(phone)) 
Out[584]: False 

In [585]: isinstance(phone,type(tv)) 
Out[585]: False 

In [586]: type(tv) 
Out[586]: MyClass 

In [587]: type(phone) 
Out[587]: MyClass 

In [588]: type(phone) is type(tv) 
Out[588]: False 
+0

좋은면에서 성능은 [내 대답] (http://stackoverflow.com/a/35657853/208880)보다 낫습니다. 아래 쪽에서는 인스턴스가 같은 클래스가 아니며 클래스 이름이 모두 같기 때문에 인스턴스가 혼동 될 수 있습니다. 그래도 적절한 상황에서 좋은 해결책입니다. –