2011-02-06 91 views
9

我想成爲其中有槽優化一類能夠使用Python描述:使用Python描述與插槽

class C(object):  
    __slots__ = ['a'] 
    a = MyDescriptor('a') 
    def __init__(self, val): 
     self.a = val 

我的問題是如何實現的描述符類,以便能夠存儲調用描述符對象的類實例中的值。通常的解決方案看起來像下面的一個,但由於「字典」,將無法正常工作時,「槽」中的C級調用不再定義:

class MyDescriptor(object): 
    __slots__ = ['name']  
    def __init__(self, name_): 
     self.name = name_ 
    def __get__(self, instance, owner): 
     if self.name not in instance.__dict__: 
      raise AttributeError, self.name 
     return instance.__dict__[self.name]  
    def __set__(self, instance, value): 
     instance.__dict__[self.name] = value 

回答

10

不要聲明相同的名稱作爲插槽和作爲一個實例方法。使用不同的名稱,並將該插槽作爲屬性訪問,而不是通過__dict__

class MyDescriptor(object): 
    __slots__ = ['name'] 
    def __init__(self, name_): 
     self.name = name_ 
    def __get__(self, instance, owner): 
     return getattr(instance, self.name) 
    def __set__(self, instance, value): 
     setattr(instance, self.name, value) 

class C(object): 
    __slots__ = ['_a'] 
    a = MyDescriptor('_a') 
    def __init__(self, val): 
     self.a = val 

foo = C(1) 
print foo.a 
foo.a = 2 
print foo.a 
+0

執行後,我可以看到'C .__ dict__'被創建。這不是我們想要避免的嗎?無論如何,查看文檔'__slots__'已經在使用描述符接口來實現它自己的目的,所以這可能會非常困難。 – dhill

+0

使用Python 3,C只有一個mappinyproxy而不是字典。更重要的是,C的實例沒有'__dict__' – Oz123

2

雖然價值令人懷疑,下面的技巧會工作,如果它是確定把第一__slots__在子類中。

class A(object): 
    __slots__ = ('a',) 

class B(A): 
    __slots__ =() 

    @property 
    def a(self): 
     try: 
      return A.a.__get__(self) 
     except AttributeError: 
      return 'no a set' 

    @a.setter 
    def a(self, val): 
     A.a.__set__(self, val) 

(你可以用你自己的描述,而不是財產。)有了這些定義:

>>> b = B() 
>>> b.a 
'no a set' 
>>> b.a = 'foo' 
>>> b.a 
'foo' 

據我瞭解,__slots__與自己的描述來實現,所以另一個__slots__後描述符同一班只會覆蓋。如果您想詳細說明這項技術,您可以在self.__class__.__mro__(或從您自己的__get__中的instance開始)中搜索候選描述符。

後記

好......好吧,如果你真的想用一個類,你可以使用下面的改編:

class C(object): 
    __slots__ = ('c',) 

class MyDescriptor(object): 

    def __init__(self, slots_descriptor): 
     self.slots_descriptor = slots_descriptor 

    def __get__(self, inst, owner = None): 
     try: 
      return self.slots_descriptor.__get__(inst, owner) 
     except AttributeError: 
      return 'no c' 

    def __set__(self, inst, val): 
     self.slots_descriptor.__set__(inst, val) 

C.c = MyDescriptor(C.c) 

如果你堅持費解,可以使分配在元類或類裝飾器中。

1

@Glenn Maynard的答案是好的。

但我想在OP的問題一個問題點(因爲我沒有帶足夠的聲譽然而,我可以給他加的問題中留言):

下面的測試中引發錯誤時實例還沒有一個__dict__變量:

 if self.name not in instance.__dict__: 

所以,這裏是一個試圖進入電影到一個通用的解決方案__dict__可變第一(這是默認反正),如果失敗的話,使用getattrsetattr

class WorksWithDictAndSlotsDescriptor: 

    def __init__(self, attr_name): 
     self.attr_name = attr_name 

    def __get__(self, instance, owner): 
     try: 
      return instance.__dict__[self.attr_name] 
     except AttributeError: 
      return getattr(instance, self.attr_name) 

    def __set__(self, instance, value): 
     try: 
      instance.__dict__[self.attr_name] = value 
     except AttributeError: 
      setattr(instance, self.attr_name, value) 

(僅適用如果attr_name是不一樣的真正的實例變量的名字,或者你將有一個RecursionError作爲公認的答案指出)

(不會按預期工作,如果有__slots__ AND __dict__

希望這會有所幫助。