__Getitem__ içinde dilimleme uygulama


112

Yaptığım bir sınıf için vektör temsili oluşturan dilim işlevselliğini uygulamaya çalışıyorum.

Şimdiye kadar bu koda sahibim, dilimi düzgün bir şekilde uygulayacağına inanıyorum, ancak v[4]v'nin bir vektör python olduğu gibi bir çağrı yaptığımda yeterli parametrelere sahip olmama konusunda bir hata veriyor. Bu yüzden, getitemhem düz dizinleri hem de dilimlemeyi işlemek için sınıfımdaki özel yöntemi nasıl tanımlayacağımı anlamaya çalışıyorum .

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]

Yanıtlar:


118

__getitem__()Yöntem, bir alacak slicebir amacı dilimlendiğinde nesne. Basitçe bakmak start, stopve stepüyeleri slicedilim için bileşenleri almak için nesne.

>>> 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
Not: list veya tuple gibi yerleşik türleri genişletmek __getslice__için python 2.X sürümleri için uygulama yapmanız gerekir . bkz. docs.python.org/2/reference/datamodel.html#object.__getslice__
gregorySalvan

@gregorySalvan: Bu bölümün altındaki uyumluluk örneği tekrarlamıyor mu?
Eric

3
@Eric: Hayır, çünkü ikinci kolonun varlığı atlar __get/set/delslice__. Yine de oldukça ince.
user2357112

@ user2357112: Vay canına, ikinci iki nokta üst üste - teşekkürler!
Eric,

Python 2. yeni tarzı sınıfları oluşturmak için var @alancalvitti IIRC,
wjandrea

64

"Yapay" bir listem var (verilerin bellekte oluşturmak isteyeceğinizden daha büyük olduğu bir liste) ve şu şekilde __getitem__görünüyor:

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."

Dilim aynı türü döndürmüyor, bu hayır-hayır, ama benim için çalışıyor.


1
Anahtar> = len (self), anahtar <0 veya anahtar> = len (öz) olması gerekmez mi? Ya <-len (self) anahtarı geçirilirse?
estan

20

Hem düz dizinleri hem de dilimlemeyi işlemek için getitem sınıfı nasıl tanımlanır?

Alt simge gösteriminde iki nokta üst üste kullandığınızda dilim nesneleri otomatik olarak oluşturulur - ve bu da iletilir __getitem__. kullanımisinstanceBir dilim nesneniz olup olmadığını kontrol etmek için :

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)

Bir aralık nesnesi kullandığımızı varsayalım, ancak dilimlerin yeni aralık nesneleri yerine listeleri döndürmesini istiyoruz (yaptığı gibi):

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

İç sınırlamalar nedeniyle aralığı alt sınıflayamayız, ancak ona yetki verebiliriz:

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)

Tamamen değiştirilebilir bir Range nesnemiz yok, ancak oldukça yakın:

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

Dilim gösterimini daha iyi anlamak için Sliceable'ın örnek kullanımı:

>>> 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, dikkat edin:

Python 2'de, bazı yerleşik türleri alt sınıflara alırken geçersiz kılmanız gerekebilecek, kullanımdan kaldırılmış bir yöntem vardır.

Gönderen DataModel belgelerinde :

object.__getslice__(self, i, j)

Sürüm 2.0'dan beri önerilmiyor: Dilim nesneleri __getitem__()yönteme parametreler olarak destekleyin . (Bununla birlikte, CPython'daki yerleşik türler şu anda hala uygulanmaktadır __getslice__(). Bu nedenle, dilimlemeyi uygularken türetilmiş sınıflarda onu geçersiz kılmanız gerekir.)

Bu, Python 3'te gitti.


7

Aaron'un cevabını genişletmek için numpy, aşağıdaki gibi şeyler için , aşağıdakilerden hangisinin givena olup olmadığını kontrol ederek çok boyutlu dilimleme yapabilirsiniz tuple:

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]

`

Çıktı:

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

Küçük izlem gibi, burada bir örnek bir ile, MATLAB indeksleme ve (halen MATLAB R2016b desteklenmez) NumPy endeksleme arasında eşleştirmek için bu çalıştırma örneği kullanımının bunun.
Eric Cousineau

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.