定义一个空切片_Python进阶:全面解读高级特性之切片
優質文章,第一時間送達!
眾所周知,我們可以通過索引值(或稱下標)來查找序列類型(如字符串、列表、元組…)中的單個元素,那么,如果要獲取一個索引區間的元素該怎么辦呢?
切片(slice)就是一種截取索引片段的技術,借助切片技術,我們可以十分靈活地處理序列類型的對象。通常來說,切片的作用就是截取序列對象,然而,對于非序列對象,我們是否有辦法做到切片操作呢?在使用切片的過程中,有什么要點值得重視,又有什么底層原理值得關注呢?本文將主要跟大家一起來探討這些內容,希望我能與你共同學習進步。
1、切片的基礎用法
列表是 Python 中極為基礎且重要的一種數據結構,也是最能發揮切片的用處的一種數據結構,所以在前兩節,我將以列表為例介紹切片的一些常見用法。
首先是切片的書寫形式:[i : i+n : m] ;其中,i 是切片的起始索引值,為列表首位時可省略;i+n 是切片的結束位置,為列表末位時可省略;m 可以不提供,默認值是1,不允許為0,當m為負數時,列表翻轉。注意:這些值都可以大于列表長度,不會報越界。
切片的基本含義是:從序列的第i位索引起,向右取到后n位元素為止,按m間隔過濾。
li = [1, 4, 5, 6, 7, 9, 11, 14, 16]# 以下寫法都可以表示整個列表,其中 X >= len(li)
li[0:X] == li[0:] == li[:X] == li[:]
== li[::] == li[-X:X] == li[-X:]
li[1:5] == [4,5,6,7] # 從1起,取5-1位元素
li[1:5:2] == [4,6] # 從1起,取5-1位元素,按2間隔過濾
li[-1:] == [16] # 取倒數第一個元素
li[-4:-2] == [9, 11] # 從倒數第四起,取-2-(-4)=2位元素
li[:-2] == li[-len(li):-2]
== [1,4,5,6,7,9,11] # 從頭開始,取-2-(-len(li))=7位元素
# 步長為負數時,列表先翻轉,再截取
li[::-1] == [16,14,11,9,7,6,5,4,1] # 翻轉整個列表
li[::-2] == [16,11,7,5,1] # 翻轉整個列表,再按2間隔過濾
li[:-5:-1] == [16,14,11,9] # 翻轉整個列表,取-5-(-len(li))=4位元素
li[:-5:-3] == [16,9] # 翻轉整個列表,取-5-(-len(li))=4位元素,再按3間隔過濾
# 切片的步長不可以為0
li[::0] # 報錯(ValueError: slice step cannot be zero)
上述的某些例子對于初學者(甚至很多老手)來說,可能還不好理解,但是它們都離不開切片的基本語法,所以為方便起見,我將它們也歸入基礎用法中。
對于這些樣例,我個人總結出兩條經驗:
(1)牢牢記住公式[i : i+n : m],當出現缺省值時,通過想象把公式補全;
(2)索引為負且步長為正時,按倒數計算索引位置;索引為負且步長為負時,先翻轉列表,再按倒數計算索引位置。
2、切片的高級用法
一般而言,切片操作的返回結果是一個新的獨立的序列。以列表為例,列表切片后得到的還是一個列表,占用新的內存地址。
當取出切片的結果時,它是一個獨立對象,因此,可以將其用于賦值操作,也可以用于其它傳遞值的場景。但是,切片只是淺拷貝,它拷貝的是原列表中元素的引用,所以,當存在變長對象的元素時,新列表將受制于原列表。
li = [1, 2, 3, 4]ls = li[::]
li == ls # True
id(li) == id(ls) # False
li.append(li[2:4]) # [1, 2, 3, 4, [3, 4]]
ls.extend(ls[2:4]) # [1, 2, 3, 4, 3, 4]
# 下例等價于判斷li長度是否大于8
if(li[8:]):
print("not empty")
else:
print("empty")
# 切片列表受制于原列表
lo = [1,[1,1],2,3]
lp = lo[:2] # [1, [1, 1]]
lo[1].append(1) # [1, [1, 1, 1], 2, 3]
lp # [1, [1, 1, 1]]
由于可見,將切片結果取出,它可以作為獨立對象使用,但是也要注意,是否取出了變長對象的元素。
切片既可以作為獨立對象被“取出”原序列,也可以留在原序列,作為一種占位符使用。
不久前,我介紹了幾種拼接字符串的方法,其中三種格式化類的拼接方法(即 %、format()、template)就是使用了占位符的思想。對于列表來說,使用切片作為占位符,同樣能夠實現拼接列表的效果。特別需要注意的是,給切片賦值的必須是可迭代對象。
li = [1, 2, 3, 4]# 在頭部拼接
li[:0] = [0] # [0, 1, 2, 3, 4]
# 在末尾拼接
li[len(li):] = [5,7] # [0, 1, 2, 3, 4, 5, 7]
# 在中部拼接
li[6:6] = [6] # [0, 1, 2, 3, 4, 5, 6, 7]
# 給切片賦值的必須是可迭代對象
li[-1:-1] = 6 # (報錯,TypeError: can only assign an iterable)
li[:0] = (9,) # [9, 0, 1, 2, 3, 4, 5, 6, 7]
li[:0] = range(3) # [0, 1, 2, 9, 0, 1, 2, 3, 4, 5, 6, 7]
上述例子中,若將切片作為獨立對象取出,那你會發現它們都是空列表,即 li[:0]==li[len(li):]==li[6:6]==,我將這種占位符稱為“純占位符”,對純占位符賦值,并不會破壞原有的元素,只會在特定的索引位置中拼接進新的元素。刪除純占位符時,也不會影響列表中的元素。
與“純占位符”相對應,“非純占位符”的切片是非空列表,對它進行操作(賦值與刪除),將會影響原始列表。如果說純占位符可以實現列表的拼接,那么,非純占位符可以實現列表的替換。
li = [1, 2, 3, 4]# 不同位置的替換
li[:3] = [7,8,9] # [7, 8, 9, 4]
li[3:] = [5,6,7] # [7, 8, 9, 5, 6, 7]
li[2:4] = ['a','b'] # [7, 8, 'a', 'b', 6, 7]
# 非等長替換
li[2:4] = [1,2,3,4] # [7, 8, 1, 2, 3, 4, 6, 7]
li[2:6] = ['a'] # [7, 8, 'a', 6, 7]
# 刪除元素
del li[2:3] # [7, 8, 6, 7]
切片占位符可以帶步長,從而實現連續跨越性的替換或刪除效果。需要注意的是,這種用法只支持等長替換。
li = [1, 2, 3, 4, 5, 6]li[::2] = ['a','b','c'] # ['a', 2, 'b', 4, 'c', 6]
li[::2] = [0]*3 # [0, 2, 0, 4, 0, 6]
li[::2] = ['w'] # 報錯,attempt to assign sequence of size 1 to extended slice of size 3
del li[::2] # [2, 4, 6]
3、自定義對象實現切片功能
切片是 Python 中最迷人最強大最 Amazing 的語言特性(幾乎沒有之一),以上兩小節雖然介紹了切片的基礎用法與高級用法,但這些還不足以充分地展露切片的魅力,所以,在接下來的兩章節中,我們將聚焦于它的更高級用法。
前兩節內容都是基于原生的序列類型(如字符串、列表、元組……),那么,我們是否可以定義自己的序列類型并讓它支持切片語法呢?更進一步,我們是否可以自定義其它對象(如字典)并讓它支持切片呢?
3.1、魔術方法:`getitem`
想要使自定義對象支持切片語法并不難,只需要在定義類的時候給它實現魔術方法 __getitem__即可。所以,這里就先介紹一下這個方法。
語法:object.__getitem__(self, key)
官方文檔釋義:Called to implement evaluation of self[key]. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence type) is up to the __getitem__method. If key is of an inappropriate type, TypeError may be raised; if of a value outside the set of indexes for the sequence (after any special interpretation of negative values), IndexError should be raised. For mapping types, if key is missing (not in the container), KeyError should be raised.
概括翻譯一下:__getitem__方法用于返回參數 key 所對應的值,這個 key 可以是整型數值和切片對象,并且支持負數索引;如果 key 不是以上兩種類型,就會拋 TypeError;如果索引越界,會拋 IndexError ;如果定義的是映射類型,當 key 參數不是其對象的鍵值時,則會拋 KeyError 。
3.2、自定義序列實現切片功能
接下來,我們定義一個簡單的 MyList ,并給它加上切片功能。(PS:僅作演示,不保證其它功能的完備性)。
import numbersclass MyList:
def __init__(self, anylist):
self.data = anylist
def __len__(self):
return len(self.data)
def __getitem__(self, index):
print("key is : " + str(index))
cls = type(self)
if isinstance(index, slice):
print("data is : " + str(self.data[index]))
return cls(self.data[index])
elif isinstance(index, numbers.Integral):
return self.data[index]
else:
msg = "{cls.__name__} indices must be integers"
raise TypeError(msg.format(cls=cls))
l = MyList(["My
總結
以上是生活随笔為你收集整理的定义一个空切片_Python进阶:全面解读高级特性之切片的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建行生活app是正规的吗
- 下一篇: 驾校一点通怎么刷学时(2022年驾校报名