關於python中slicing的探討

weixin_34353714發表於2018-07-21

1.overview

Python Reference Manual(2016)對slicing的定義如下:

a slicing selects a range of items in a sequence object (e.g. a string, tuple, or list).
slicing may be used as expressions or as targets in assignment or del statements.

The syntax for a slicing:
slicing      ::=  primary "[" slice_list "]"
slice_list   ::=  slice_item ("," slice_item)* [","]
slice_item   ::=  expression | proper_slice
proper_slice ::=  [lower_bound] ":" [upper_bound] [ ":" [stride] ]
lower_bound  ::=  expression
upper_bound  ::=  expression
stride       ::=  expression

There is ambiguity in the formal syntax here: anything that looks like an expression list also looks like a slice, so any subscription can be interpretated as a slicing. Rather than further complicating the syntax, this is disambiguated by difining that in this case the interpretation as a subscription takes priority over the interpretaton as a slicing (this is the case if the slice list contains no proper slice).

The semantics for a slicing are as follows. The primary must evaluate to a mapping object, and it is indexed (using the same __getitem__() method as normal subscription) with a key that is constructed from the slice list, as follows. 
If the slice list contains at least one comma, the key is a tuple containing the conversion of the slice items; otherwise, the conversion of the lone slice item is the key. The conversion of a slice item that is an expression is that expression.  
The conversion of a proper slice is a slice object (see section The standard type hierarchy) whose start,
stop and step attributes are the values of the
expressions given as lower bound, upper bound and stride, respectively, substituting None for missing expressions.

2.explanation

a slicing selects a range of items in a sequence object (e.g. a string, tuple, or list).

切片的原材料是序列物件,在python中常見為string, tuple和list,換言之,自定義的類如果是序列物件也可以有切片功能

slicing may be used as expressions or as targets in assignment or del statements.

切片可以用作表示式,或者賦值和刪除的作用物件

The syntax for a slicing:
slicing      ::=  primary "[" slice_list "]"
slice_list   ::=  slice_item ("," slice_item)* [","]
slice_item   ::=  expression | proper_slice
proper_slice ::=  [lower_bound] ":" [upper_bound] [ ":" [stride] ]
lower_bound  ::=  expression
upper_bound  ::=  expression
stride       ::=  expression
  • 主要語法是[],中間包含一個slice_list;
  • slice_list是由逗號分隔開的slice_item組成的;
  • slice_item可以是表示式或者是proper_slice;
  • proper_slice的結構是"[下限]:[上限]:[步長]";
  • 下限、上限和步長都是表示式組成的。
There is ambiguity in the formal syntax here: anything that looks like an expression list also looks like a slice, so any subscription can be interpretated as a slicing. Rather than further complicating the syntax, this is disambiguated by difining that in this case the interpretation as a subscription takes priority over the interpretaton as a slicing (this is the case if the slice list contains no proper slice).

表示式的列表和切片會看起來很像,因為都是中括號中間加入表示式。subscription的定義是“subscription ::= primary "[" expression_list "]"”
為了避免語義複雜化,如果沒有proper_slice,則下標的解析優先於切片。

>>> s = 'hello'
>>> print(s[2])  # 2優先解析為下標index
l
>>> print(s[::-1])  # ::-1解析為切片slice
olleh
The semantics for a slicing are as follows. The primary must evaluate to a mapping object, and it is indexed (using the same __getitem__() method as normal subscription) with a key that is constructed from the slice list, as follows. 
If the slice list contains at least one comma, the key is a tuple containing the conversion of the slice items; otherwise, the conversion of the lone slice item is the key. The conversion of a slice item that is an expression is that expression.  
The conversion of a proper slice is a slice object (see section The standard type hierarchy) whose start,
stop and step attributes are the values of the
expressions given as lower bound, upper bound and stride, respectively, substituting None for missing expressions.

primary是mapping object的說法值得商榷,或許sequence object更合適。index使用的是和subscription一樣的_getitem_()方法。如果slice中含有逗號,則key解析為tuple。如果只有一個slice_item,則生成一個slice object,start, stop, step由表示式賦值,預設為None。如果某個bound是負數,那麼這個bound將會轉化為(bound + sequence’s length)。對於Evaluation的結果,如果超出了[0: sequence's length],那麼會使用邊界值替代超出值。

3.lab

自定義一個可以slice的物件,根據定義,只要是序列物件即可。

class T1:
    def __getitem__(self, key):
        print(key)
        
>>> t = T1()
>>> t[:-1, ]  # slice_item有逗號,解析為一個slice tuple
(slice(None, None, -1),)
>>> t[1:2:3, 4:5:6]  # 每個slice_item賦值start, stop, step
(slice(1, 2, 3), slice(4, 5, 6))

使用built-in的list來實現自定義類的slicing:

class T2:
    def __init__(self, *args):
        self.l = [x for x in args]
    def __getitem__(self, key):
        print(key)
        return self.l[key]
>>> t = T2(1, 2, 3)
>>> print(t[int(2*0.5)])  # slice_item可以是表示式
1
2
>>> print(t[:-1])  # 預設的部分是None
slice(None, -1, None)
[1, 2]
>>> print(t[::-1])
slice(None, None, -1)
[3, 2, 1]
>>> print(t[:-1, ])
list indices must be integers or slices, not tuple
# sequence的slicing不支援逗號分隔的元組,僅支援單個slice的object。T1中的分隔方法在T2中不支援,因為T2的資料型別是list,屬於sequence。

4.example

>>> t = "I am a student."
>>> print(t[2])
a
>>> print(t[-1])  # 負數index相當於index + length,這裡是-1+15=14
.
>>> print(t[2:4])  # 從start開始到stop結束,包括start,不包括stop
am
>>>print(t[:4], t[7:], sep='|')  # 預設start則預設從頭開始,預設stop則預設到最後結束,預設step預設正向步長為1
I am|student.

5.take away points

字串倒序很常用:

>>> s = 'hello world'
>>> print(s[::-1])
dlrow olleh

相關文章