2011-02-06 8 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はmippinyproxyしかなく、dictではありません。しかしもっと重要なことに、Cのインスタンスには '__dict__'がありません – Oz123

2

サブクラスで最初__slots__を入れてOKであれば怪しげな価値が、以下のトリックは、動作しますけど。

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で開始してください。

ポストスクリプト

[OK]を...あなたは本当に1つのクラスを使用する場合だけでなく、あなたが使用することができ、次の適応:

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) 

あなたはinscrutabilityを主張する場合は、割り当てを行うことができますメタクラスやクラスデコレータで。

1

@Glennメイナードの答えは良いものです。

しかし、私は(私はまだ十分な評判をやりなさいので、私は彼の質問にコメントを追加することはできません)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____dict__

これが役に立ちます。

関連する問題