2

メモリ効率の良い方法で変更可能なポイントを平面に格納するために、次のクラスを作成しましたnamedtuple('Point', 'x y')。インスタンス ディクショナリは大きいので、次のようにし__slots__ます。

from collections import Sequence

class Point(Sequence):
    __slots__ = ('x', 'y')

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __getitem__(self, item):
        return getattr(self, self.__slots__[item])

    def __setitem__(self, item, value):
        return setattr(self, self.__slots__[item], value)

    def __repr__(self):
        return 'Point(x=%r, y=%r)' % (self.x, self.y)

    def __len__(self):
        return 2

Python 3 でテストしたところ、すべて問題ないように見えました。

>>> pt = Point(12, 42)
>>> pt[0], pt.y
(12, 42)
>>> pt.x = 5
>>> pt
Point(x=5, y=42)
>>> pt.z = 6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'

ただし、Python 2 ではz、スロットにない場合でも属性を設定できます。

>>> pt = Point(12, 42)
>>> pt.z = 5
>>> pt.z
5
>>> pt.__slots__
('x', 'y')
>>> pt.__dict__
{'z': 5}

それはなぜですか? また、Python 2 と Python 3 の違いはなぜですか?

4

1 に答える 1

6

Python 2 データ モデルでは、次のように記述されています__slots__

  • のないクラスから継承すると、そのクラス__slots____dict__属性は常にアクセス可能になるため__slots__、サブクラスでの定義は意味がありません。

そして、それがここで起こっていることです。Python 2 では、モジュール内の抽象基本クラスにcollectionsはまったくありませんでした__slots__:

>>> from collections import Sequence
>>> Sequence.__slots__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Sequence' has no attribute '__slots__'

これはCPython issue tracker でissue 11333として報告され、Python 3.3 で修正されました。

Python 3.3+ では、Sequence抽象基本クラスが__slots__空のタプルに設定されました。

>>> from collections import Sequence
>>> Sequence.__slots__
()

したがって、Python 2 では、基本クラスから継承すると同時に、collectionsメモリ効率の高いストレージを使用することはできません。__slots__


ただし、collections抽象基本クラスに関するドキュメントでは次のように主張されていますが、

これらの ABC を使用すると、クラスまたはインスタンスが特定の機能を提供するかどうかを尋ねることができます。たとえば、次のようになります。

size = None
if isinstance(myvar, collections.Sized):
    size = len(myvar)

Sequence;の場合はそうではありません。に必要なすべてのメソッドを実装するだけでは、クラスのインスタンスがチェックSequenceに合格するわけではありません。isinstance

その理由は、クラスに;Sequenceがないためです。__subclasshook__存在しない場合は、代わりに親クラス__subclasshook__が参照されます。この場合Sized.__subclasshook__; NotImplementedそして、それは、テストされたクラスが正確 ではなかった場合に返されますSized

一方、マッピング型とシーケンス型をマジック メソッドで区別することはできませんでした。どちらもまったく同じマジック メソッドを持つことできるcollections.OrderedDictためです。シーケンスではありません。Sequence__reversed__

ただし、 returnSequenceを作成するために from を継承する必要はありません。次の例では、 Python 2の ではなくから派生したことを除いて、 は同じです。isinstance(Point, Sequence)TruePointobjectSequence

>>> pt = Point(12, 42)
>>> pt.z = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> isinstance(pt, Sequence)
False
>>> Sequence.register(pt)
>>> isinstance(pt, Sequence)
True

isinstanceチェックのために、任意のクラスを抽象基本クラスのサブクラスとして登録できます。追加の mix-in メソッドのうち、実際に実装する必要があるのはcountandのみindexです。その他の機能は、Python ランタイムによって埋められます。

于 2015-04-04T08:03:56.853 に答える