web-dev-qa-db-ja.com

Pythonでリストオブジェクトを継承および拡張する方法は?

python listオブジェクトを使用することに興味がありますが、機能がわずかに変更されています。特に、0インデックスではなく1インデックスが必要です。例:

_>> mylist = MyList()
>> mylist.extend([1,2,3,4,5])
>> print mylist[1]
_

出力は次のようになります。1

しかし、これを行うために__getitem__()および__setitem__()メソッドを変更すると、_RuntimeError: maximum recursion depth exceeded_エラーが発生しました。私はこれらのメソッドをいじくり回しましたが、これは基本的にそこにあったものです:

_class MyList(list):
    def __getitem__(self, key):
        return self[key-1]
    def __setitem__(self, key, item):
        self[key-1] = item
_

問題は、_self[key-1]_自体が、定義しているのと同じメソッドを呼び出していることだと思います。もしそうなら、list()メソッドの代わりにMyList()メソッドを使用するにはどうすればよいですか? _super[key-1]_の代わりに_self[key-1]_を使用しようとしましたが、それが不満の原因になりました_TypeError: 'type' object is unsubscriptable_

何か案は?また、このための良いチュートリアルを教えていただければ、それは素晴らしいことです!

ありがとう!

44
mindthief

super()関数を使用して、基本クラスのメソッドを呼び出すか、メソッドを直接呼び出します。

class MyList(list):
    def __getitem__(self, key):
        return list.__getitem__(self, key-1)

または

class MyList(list):
    def __getitem__(self, key):
        return super(MyList, self).__getitem__(key-1)

ただし、他のリストメソッドの動作は変更されません。たとえば、インデックスは変更されないままであるため、予期しない結果が生じる可能性があります。

numbers = MyList()
numbers.append("one")
numbers.append("two")

print numbers.index('one')
>>> 1

print numbers[numbers.index('one')]
>>> 'two'
52

代わりに、同じメソッドを使用して整数をサブクラス化し、すべての数値を設定した値から1を引いた値として定義します。出来上がり。

すみません、私はしなければなりませんでした。これは、Microsoftが暗闇を標準として定義しているというジョークのようなものです。

26
Binary Phile

抽象クラスであるcollections.MutableSequenceから継承するクラスを作成することにより、リスコフ置換の原則に違反することを回避できます。次のようになります。

class MyList(collections.MutableSequence):
def __init__(self, l=[]):
    if type(l) is not list:
        raise ValueError()

    self._inner_list = l

def __len__(self):
    return len(self._inner_list)

def __delitem__(self, index):
    self._inner_list.__delitem__(index - 1)

def insert(self, index, value):
    self._inner_list.insert(index - 1, value)

def __setitem__(self, index, value):
    self._inner_list.__setitem__(index - 1, value)

def __getitem__(self, index):
    return self._inner_list.__getitem__(index - 1)

ここには1つの問題があります(ただし、さらに問題がある場合もあります)。このように新しいリストにインデックスを付ける場合:

l = MyList()
l[0]

実際に呼び出します:

self._inner_list[-1]

最後の要素を取得します。そのため、リストでその機能を使用する場合は、メソッドで追加のチェックを行い、逆インデックスを保持する必要があります。

編集:

ここに新しいコードがありますが、問題ないはずです。

def indexing_decorator(func):

    def decorated(self, index, *args):
        if index == 0:
            raise IndexError('Indices start from 1')
        Elif index > 0:
            index -= 1

        return func(self, index, *args)

    return decorated


class MyList(collections.MutableSequence):
    def __init__(self):
        self._inner_list = list()

    def __len__(self):
        return len(self._inner_list)

    @indexing_decorator
    def __delitem__(self, index):
        self._inner_list.__delitem__(index)

    @indexing_decorator
    def insert(self, index, value):
        self._inner_list.insert(index, value)

    @indexing_decorator
    def __setitem__(self, index, value):
        self._inner_list.__setitem__(index, value)

    @indexing_decorator
    def __getitem__(self, index):
        return self._inner_list.__getitem__(index)

    def append(self, value):
        self.insert(len(self) + 1, value)
16