Triển khai cắt trong __getitem__


112

Tôi đang cố gắng triển khai chức năng lát cắt cho một lớp tôi đang tạo để tạo ra một biểu diễn vectơ.

Tôi có mã này cho đến nay, mà tôi tin rằng sẽ triển khai đúng lát cắt nhưng bất cứ khi nào tôi thực hiện một lệnh gọi như v[4]v là vectơ python sẽ trả về lỗi không có đủ tham số. Vì vậy, tôi đang cố gắng tìm ra cách xác định getitemphương thức đặc biệt trong lớp của mình để xử lý cả chỉ mục đơn giản và cắt.

def __getitem__(self, start, stop, step):
    index = start
    if stop == None:
        end = start + 1
    else:
        end = stop
    if step == None:
        stride = 1
    else:
        stride = step
    return self.__data[index:end:stride]

Câu trả lời:


118

Các __getitem__()phương pháp sẽ nhận được một sliceđối tượng khi đối tượng được thái lát. Đơn giản chỉ cần nhìn vào start, stopstepcác thành viên của sliceđối tượng để có được các thành phần cho một slice.

>>> class C(object):
...   def __getitem__(self, val):
...     print val
... 
>>> c = C()
>>> c[3]
3
>>> c[3:4]
slice(3, 4, None)
>>> c[3:4:-2]
slice(3, 4, -2)
>>> c[():1j:'a']
slice((), 1j, 'a')

10
Lưu ý: để mở rộng các loại nội trang như danh sách hoặc tuple, bạn phải triển khai __getslice__cho phiên bản python 2.X. thấy docs.python.org/2/reference/datamodel.html#object.__getslice__
gregorySalvan

@gregorySalvan: Không phải ví dụ về khả năng tương thích bên dưới phần đó chỉ tái diễn?
Eric

3
@Eric: Không, vì sự hiện diện của dấu hai chấm thứ hai bỏ qua __get/set/delslice__. Tuy nhiên, nó khá tinh tế.
user2357112 hỗ trợ Monica

@ user2357112: Chà, hoàn toàn bỏ lỡ dấu hai chấm thứ hai - cảm ơn!
Eric

@alancalvitti IIRC, đó là tạo ra các lớp học kiểu mới bằng Python 2.
wjandrea

64

Tôi có một danh sách "tổng hợp" (một danh sách mà dữ liệu lớn hơn những gì bạn muốn tạo trong bộ nhớ) và danh sách của tôi __getitem__trông như thế này:

def __getitem__( self, key ) :
    if isinstance( key, slice ) :
        #Get the start, stop, and step from the slice
        return [self[ii] for ii in xrange(*key.indices(len(self)))]
    elif isinstance( key, int ) :
        if key < 0 : #Handle negative indices
            key += len( self )
        if key < 0 or key >= len( self ) :
            raise IndexError, "The index (%d) is out of range."%key
        return self.getData(key) #Get the data from elsewhere
    else:
        raise TypeError, "Invalid argument type."

Slice không trả về cùng một kiểu, đó là một điều không nên, nhưng nó phù hợp với tôi.


1
Nếu key> = len (self) không phải if key <0 hoặc key> = len (self)? Điều gì sẽ xảy ra nếu một khóa <-len (self) được truyền?
estan

20

Làm thế nào để xác định lớp getitem để xử lý cả chỉ mục đơn giản và cắt?

Các đối tượng Slice được tạo tự động khi bạn sử dụng dấu hai chấm trong ký hiệu chỉ số con - và đó là những gì được chuyển tới __getitem__. Sử dụng isinstanceđể kiểm tra xem bạn có đối tượng lát cắt không:

from __future__ import print_function

class Sliceable(object):
    def __getitem__(self, subscript):
        if isinstance(subscript, slice):
            # do your handling for a slice object:
            print(subscript.start, subscript.stop, subscript.step)
        else:
            # Do your handling for a plain index
            print(subscript)

Giả sử chúng tôi đang sử dụng một đối tượng phạm vi, nhưng chúng tôi muốn các lát cắt trả về danh sách thay vì các đối tượng phạm vi mới (như nó hiện có):

>>> range(1,100, 4)[::-1]
range(97, -3, -4)

Chúng tôi không thể phân cấp phạm vi vì các giới hạn nội bộ, nhưng chúng tôi có thể ủy quyền cho nó:

class Range:
    """like builtin range, but when sliced gives a list"""
    __slots__ = "_range"
    def __init__(self, *args):
        self._range = range(*args) # takes no keyword arguments.
    def __getattr__(self, name):
        return getattr(self._range, name)
    def __getitem__(self, subscript):
        result = self._range.__getitem__(subscript)
        if isinstance(subscript, slice):
            return list(result)
        else:
            return result

r = Range(100)

Chúng tôi không có đối tượng Phạm vi có thể thay thế hoàn hảo, nhưng nó khá gần:

>>> r[1:3]
[1, 2]
>>> r[1]
1
>>> 2 in r
True
>>> r.count(3)
1

Để hiểu rõ hơn về ký hiệu lát cắt, đây là ví dụ về cách sử dụng Sliceable:

>>> sliceme = Sliceable()
>>> sliceme[1]
1
>>> sliceme[2]
2
>>> sliceme[:]
None None None
>>> sliceme[1:]
1 None None
>>> sliceme[1:2]
1 2 None
>>> sliceme[1:2:3]
1 2 3
>>> sliceme[:2:3]
None 2 3
>>> sliceme[::3]
None None 3
>>> sliceme[::]
None None None
>>> sliceme[:]
None None None

Python 2, hãy lưu ý:

Trong Python 2, có một phương thức không dùng nữa mà bạn có thể cần ghi đè khi phân lớp con một số kiểu nội trang.

Từ tài liệu mô hình dữ liệu :

object.__getslice__(self, i, j)

Không được dùng nữa kể từ phiên bản 2.0: Hỗ trợ các đối tượng lát cắt làm tham số cho __getitem__()phương thức. (Tuy nhiên, các kiểu tích hợp sẵn trong CPython hiện vẫn được triển khai __getslice__(). Do đó, bạn phải ghi đè nó trong các lớp dẫn xuất khi triển khai quá trình cắt.)

Điều này đã biến mất trong Python 3.


7

Để mở rộng câu trả lời của Aaron, đối với những thứ như numpy, bạn có thể thực hiện cắt nhiều chiều bằng cách kiểm tra xem có phải giventuple:

class Sliceable(object):
    def __getitem__(self, given):
        if isinstance(given, slice):
            # do your handling for a slice object:
            print("slice", given.start, given.stop, given.step)
        elif isinstance(given, tuple):
            print("multidim", given)
        else:
            # Do your handling for a plain index
            print("plain", given)

sliceme = Sliceable()
sliceme[1]
sliceme[::]
sliceme[1:, ::2]

``

Đầu ra:

('plain', 1)
('slice', None, None, None)
('multidim', (slice(1, None, None), slice(None, None, 2)))

Dưới đây là một ví dụ về việc sử dụng điều này để ánh xạ giữa lập chỉ mục MATLAB và lập chỉ mục NumPy (hiện không được hỗ trợ trong MATLAB R2016b), với một ví dụ về cách sử dụng nó.
Eric Cousineau

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.