Python 扩展切片符号的正式语法?


例如,Numpy允许多维切片:
a[:, 0, 7:9]

这就提出了一个问题:还有什么可能?(想象一下可能性!)

根据这个答案和一些实验(见下文),如果有逗号,Python会构建一个对象元组,其中一些可能是切片对象,并将其(作为key)传递给a__getitem__(self, key)

__getitem__(..)的文档没有指定这种行为。有没有我遗漏的官方文件?特别是,这种语法向后兼容的程度如何?(在网上搜索"python扩展切片表示法"会得到"python 2.3的新增功能",但没有提及。)


实验

>>> class Test(object):
...     def __getitem__(self, x):
...         print repr(x)

>>> t = Test()

首先,Python发现可以识别多切片的东西:

>>> t[1]
1
>>> t['a':,]
(slice('a', None, None),)
>>> t['a':7:('b','c'),]
(slice('a', 7, ('b', 'c')),)
# Seems like it can be arbitrary objects?
>>> t[(t,t):[4,5]]
slice((<__main__.Test object at 0x07D04950>, <__main__.Test object at 0x07D04950>), [4, 5], None)
>>> t[::]
slice(None, None, None)
>>> t[:]
slice(None, None, None)
>>> t[::,1,::,::,:,:,:]
(slice(None, None, None), 1, slice(None, None, None), slice(None, None, None), slice(None, None, None),  slice(None, None, None), slice(None, None, None))
>>> t[...]
Ellipsis
>>> t[... , ...]
(Ellipsis, Ellipsis)
>>> t[  .   .      .    ]
Ellipsis

一些不允许的事情(SyntaxError):

# Semicolon delimiter
t['a':5; 'b':7:-7]
# Slice within a slice
t['a':7:(9:5),]
# Two trailing commas
t[5,,]
# Isolated comma
t[,]
# Leading comma
t[,5]
# Empty string
t[]
# Triple colon
t[:::]
# Ellipses as part of a slice
t[1:...]
t[1:2:...]
# Ellipses inside no-op parens:
t[(...)]
# Any non-zero and non-three number of dots:
t[.]
t[..]
t[ .  .  .  . ]

任何都是可能的,只要它是有效的Python表达式。由[...]之间的表达式生成的对象被传递给__getitem__方法。就是这样。

逗号产生元组,表达式中的:冒号产生slice()对象。除此之外,你想用什么就用什么。

这是因为语法允许在符号中使用任何expression_list。参见参考文件:

subscription ::=  primary "[" expression_list "]"

切片在切片部分中进一步指定:

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

因此,再次允许任意expressions,并且:触发proper_slice语法规则。

请注意,lower_boundupper_boundstride表达式结果用于构造slice()对象,该对象只能处理整数值。任何不能转换为整数的内容都将导致TypeError被引发。这与语法错误不是一回事;t[1:...]在语法上很好,但...不可转换为整数,因此会出现运行时TypeError异常。使用非整数切片值的两个示例在Python 2.4及更高版本上是不可能的。

实际的语法错误都源于无效的表达式。除了:proper_slice表示法之外,如果不能将[...]之间的部分放在赋值的右侧,则也不能在切片中使用它。

例如,;只能用于将多个简单语句放在一条逻辑线上。语句可以包含表达式,但表达式永远不能包含语句,从表达式中排除;(9:5),不是一个有效的表达式(在Python中,您不能在括号中使用:parenth_form规则不允许任何此类选项)。

用于切片的Python 2语法更为详细,因为...是语法中的一个特定符号,并且您实际上不能在切片之外使用...(在Python 3中,您可以在任何表达式有效的地方使用...),这就是为什么t[(...)]在Python 2中是一个语法错误,而在Python3中不是。

添加到前面的答案中。如果您定义

class Foo:
def __getitem__(self, key):
return key

Foo()[0, :, 1:2, 1:2:3],它会给你内部的表示,它是:

>>> Foo()[0, :, 1:2, 1:2:3]
(0, slice(None, None, None), slice(1, 2, None), slice(1, 2, 3))

最新更新