我正在使用Python 3.3。我想获得一个slice
对象并使用它来创建一个新的range
对象。
它是这样的:
>>> class A:
def __getitem__(self, item):
if isinstance(item, slice):
return list(range(item.start, item.stop, item.step))
>>> a = A()
>>> a[1:5:2] # works fine
[1, 3]
>>> a[1:5] # won't work :(
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
a[1:5] # won't work :(
File "<pyshell#9>", line 4, in __getitem__
return list(range(item.start, item.stop, item.step))
TypeError: 'NoneType' object cannot be interpreted as an integer
嗯,问题很明显 - range
不接受None
作为值:
>>> range(1, 5, None)
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
range(1, 5, None)
TypeError: 'NoneType' object cannot be interpreted as an integer
但对我来说不明显的是解决方案。我如何致电range
以便它在每种情况下都有效?
我正在寻找一种不错的pythonic方法。
答案 0 :(得分:9)
尝试
class A:
def __getitem__(self, item):
ifnone = lambda a, b: b if a is None else a
if isinstance(item, slice):
if item.stop is None:
# do something with itertools.count()
else:
return list(range(ifnone(item.start, 0), item.stop, ifnone(item.step, 1)))
else:
return item
如果.start
.step
,我们会相应地重新解释None
和.indices()
。
另一个选项可能是切片的None
方法。使用条目数调用它并将>>> a=slice(None, None, None)
>>> a.indices(1)
(0, 1, 1)
>>> a.indices(10)
(0, 10, 1)
>>> a=slice(None, -5, None)
>>> a.indices(100)
(0, 95, 1)
重新解释为适当的值,并在给定的长度参数周围包含负值:
{{1}}
这取决于你打算用负指数做什么...
答案 1 :(得分:9)
有一种更简单的方法(至少在3.4中,我目前没有3.3,而且我没有在更改日志中看到它。)
假设你的班级已经有一个已知长度,你可以只切片那个大小的范围:
>>> class A:
def __getitem__(self, item):
if isinstance(item, slice):
return list(range(item.stop)[item])
>>> a = A()
>>> a[1:5:2]
[1, 3]
>>> a[1:5]
[1, 2, 3, 4]
如果你不知道你必须做的先验长度:
[[l1[n] for n, l2s in enumerate(l2) if l2s == element] for element in set(l2)]
答案 2 :(得分:6)
切片由start
,stop
和step
参数组成,可以使用slice notation或使用slice
built-in创建。 start
,stop
和step
参数中的任何(或全部)都可以是None
。
# valid
sliceable[None:None:None]
# also valid
cut = slice(None, None, None)
sliceable[cut]
但是,正如原始问题所指出的,range
函数不接受None
个参数。你可以通过各种方式解决这个问题......
if item.start None:
return list(range(item.start, item.stop))
return list(range(item.start, item.stop, item.step))
...由于任何或所有参数可能为None
,因此可能会出现不必要的复杂情况。
start = item.start if item.start is None else 0
step = item.step if item.step is None else 1
return list(range(item.start, item.stop, item.step))
......这是明确的,但有点冗长。
return list(range(item.start if item.start else 0, item.stop, item.step if item.step else 1))
......这也是不必要的冗长。
ifnone = lambda a, b: b if a is None else a
range(ifnone(item.start, 0), item.stop, ifnone(item.step, 1)
......这很难理解。
return list(range(item.start or 0, item.stop or len(self), item.step or 1))
我发现使用or
来指定最简单的合理默认值。它明确,简单,清晰,简洁。
要完成实现,您还应该通过选中int
来处理整数索引(long
,isinstance(item, numbers.Integral)
等)(参见int vs numbers.Integral)。
定义__len__
以允许使用len(self)
作为默认停止值。
最后针对无效索引(例如字符串等)提出适当的TypeError
。
class A:
def __len__(self):
return 0
def __getitem__(self, item):
if isinstance(item, numbers.Integral): # item is an integer
return item
if isinstance(item, slice): # item is a slice
return list(range(item.start or 0, item.stop or len(self), item.step or 1))
else: # invalid index type
raise TypeError('{cls} indices must be integers or slices, not {idx}'.format(
cls=type(self).__name__,
idx=type(item).__name__,
))
答案 3 :(得分:3)
这里的所有其他答案都完全没有。
您只是不能将 slice
转换为 range
。信息不足。
您需要知道要切片的列表(或其他序列类型)的长度。一旦你有了它,你就可以在 Python 3 中使用 slice.indices()
轻松地创建一个范围按照您提供的示例:
class A:
def __init__(self, mylist):
self.mylist = mylist
def __getitem__(self, item):
if isinstance(item, slice):
mylen = len(self.mylist)
return list(range(*item.indices(mylen)))
mylist = [1, 2, 'abc', 'def', 3, 4, None, -1]
a = A(mylist)
a[1:5] # produces [1, 2, 3, 4]
答案 4 :(得分:1)
我会特别考虑item.step is None
分支:
def __getitem__(self, item):
if isinstance(item, slice):
if item.step is None:
return list(range(item.start, item.stop))
return list(range(item.start, item.stop, item.step))
并且您将处理需要正确倒计时的范围。
答案 5 :(得分:0)
在上一个示例a[1:5]
,item.step == None
中,您正在尝试range(1, 5, None)
,这当然会导致错误。快速解决方法:
class A:
def __getitem__(self, item):
if isinstance(item, slice):
return list(range(item.start, item.stop, item.step if item.step else 1)) #Changed line!
但它只是向您展示您的问题。这不是最好的方法。
答案 6 :(得分:0)
这样的事情怎么办?
>>> class A:
def __getitem__(self, item):
if isinstance(item, slice):
return list(range(item.start, item.stop, item.step if item.step else 1))
>>> a = A()
>>> a[1:5:2] # works fine
[1, 3]
>>> a[1:5] # works as well :)
[1, 2, 3, 4]