2017-01-27 11 views
2

私はusage of slotsの主な答えを読んで、どのようにそしてどこで__slots__を使うべきかという考えを得ました。属性の初期化で `__slots__`を使うには?

さて、私は以下のように似ているのPython 3へのPython 2からコードを移植しています:

class B(object): 
    __slots__ = ('_fields') 
    _fields = set() 

しかし、Pythonの2に細かい仕事をしながら、これはPythonの3エラーを与えている:

ValueError: '_fields' in __slots__ conflicts with class variable

は私が

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self): 
     _fields = set() 

にコードを変更し、それが正常に動作します。私の質問は、それは正しい変更ですか?

私は元のコードから得たので、__dict__をメモリや高速アクセスなどの理由で使用しないでください。同時に、属性のタイプをset()として指定しようとしています。 。上記の変更は、それを言う正しい方法であるか、またはいくつかの副作用を有する可能性があります。


さらなる実験

import pdb 

class A(object): 
    a = set() 

''' 
class B(object): 
    __slots__ = ('a') 
    a = set() 
''' 

class C(object): 
    __slots__ = ('a') 
    def __init__(self): 
     a = set() 

class D(object): 
    def __init__(self): 
     __slots__ = ('a') 
     a = set() 

if __name__ == '__main__': 
    #pdb.set_trace() 
    x = A(); print(dir(x)) 
    #y = B() 
    z = C(); print(dir(z)) 
    z1 = D(); print(dir(z1)) 

、それが出力以下与えた: 私は(Pythonの3の)以下のバリエーションで、さらに実験を行いました。

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a'] 


['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'a'] 


['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] 

我々は、Cのオブジェクト、すなわち無__dict__のみ__slots__正しいフットプリントを示すことがわかります。理想的には私たちが望むものではありませんか? __weakref__の説明も参考になります。

また、Python 2では、BオブジェクトとCオブジェクトの両方が同じフットプリントを示します。それに基づいて、Python 2とPython 3の両方でコンパイルしているので、Cを適切な方法で置くべきです。

答えて

3

しかし、Pythonの2に細かい仕事をしながら、これはPythonの3エラーを与えている:

ValueError: '_fields' in __slots__ conflicts with class variable

、あなたが実際に_fieldsの値を設定しようとすると、あなたがAttributeError: 'C' object attribute '_fields' is read-onlyを取得し、Py3kのように時間をコンパイル/クラスの作成時にPython2のエラーを取得していないものの:

>>> class C(object): 
... __slots__ = ('_fields') 
... _fields = set() 
... 
>>> 
>>> c = C() 
>>> c._fields 
set([]) 
>>> c._fields = 'foo' 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'C' object attribute '_fields' is read-only 
>>> 

も、第note in the slots documentation参照:

__slots__が記述子を作成することにより、クラスレベルで実装されている(実装を記述子)を指定します。その結果、クラス属性を使用して__slots__で定義されたインスタンス変数のデフォルト値を設定することはできません。それ以外の場合、クラス属性は記述子の割り当てを上書きします。


Wrtのあなたの修正:

私はそれだけでローカルだないself._fields、修正クラスBが__init__()内部_fieldsを持って

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self): 
     _fields = set() 

にコードを変更しますinitの変数で、クラス内の他の場所にはアクセスできません。

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self): 
     self._fields = set() 

正しく_fieldsを初期化するには、次の操作を行います:にその変更

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self, _fields=None): 
     if not _fields: 
      self._fields = set() 

Wrtのさらなる実験:

クラスDで

__slots__だけ__init()__内部変数です。 (特別な)クラス変数D.__slots__ではありません。インスタンス変数self.__slots__さえあります。だからそれは__dict__です。

クラスAはありません。また、__dict__もあります。

クラスCには__slots__が正しくあります。

+0

ありがとうございました。しかし、代わりに '__init__'の中で次のように言っていましたか? ' self._fields = _fieldsがない場合はelse '' boolean context 'の '_fields'をチェックしていなければ' – ViFI

+0

'を設定します(http:// stackoverflow .com/a/1452500/1431750)。 '_fields'が' None'の場合、その式は 'True'と評価され、' self._fields = set() 'という行が実行されます。これは、 '_fieldsがNoneの場合は、:self._fields = set()'とすることもできます。 Btw、あなたのコメントの例では、 'not None'は常にTrueです。もし_fieldsがNone else set() 'ならば' self._fields = _fields '、それ以外の場合は 'self._fields = _fields _fields else set()'のように置くことができます。 _Caveat:_空の 'set()'はFalseに評価されますので、適切なチェックを使用してください... – aneroid

+0

_Caveat:_空の 'set()'はFalseに評価されますので、 'set() 「なし」はありません。また、関数またはメソッドを[デフォルトの引数値として変更可能な型]で初期化しないでください(http://stackoverflow.com/q/1132941/1431750)。 'set'、' dict'、 'list'などのようなものです。[ここの別の例](http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments)。 – aneroid

関連する問題