在python 2中,内置函数map似乎调用__len__时长度被覆盖.这是否正确 - 如果是这样,为什么我们计算迭代的长度来映射?Iterables不需要覆盖长度(例如),并且即使长度没有被iterable预定义,map函数也能工作.
地图在这里定义; 它确实指定在传递多个iterables的情况下存在与长度相关的功能.然而,
None
map(f, iterable)基本上相当于:
[f(x) for x in iterable]
但我遇到了一些简单的例子.
例如
class Iterable:
def __iter__(self):
self.iterable = [1,2,3,4,5].__iter__()
return self
def next(self):
return self.iterable.next()
#def __len__(self):
# self.iterable = None
# return 5
def foo(x): return x
print( [foo(x) for x in Iterable()] )
print( map(foo,Iterable()) )
Run Code Online (Sandbox Code Playgroud)
表现得如此,但如果你取消超载的话len,它就不会.
在这种情况下,它会引发AttributeError,因为iterable是None.虽然单位行为是愚蠢的,但我认为在len的规范中没有要求不变性.当然,最好不要在调用中修改状态len,但原因不应该是因为内置函数中存在意外行为.在更现实的情况下,我的len功能可能只是很慢,我不希望担心它被调用map,或者它可能不是线程安全的等等.
实施依赖?
由于map是内置函数,它可能在规范之外具有特定于实现的特性,但是cpython在bltinmodule.c的第918行实现它,它确实指出:
Run Code Online (Sandbox Code Playgroud)/* Do a first pass to obtain iterators for the arguments, and set len * to the largest of their lengths. */
然后调用_PyObject_LengthHint,在Object/abstract.c中定义,实际上似乎寻找覆盖len.这并没有向我澄清这是否只是依赖于实现,或者我是否缺少某种map有意识地根据我的直觉寻找迭代长度的理由.
(注意我没有在python 3中测试过这个,这就是为什么我指定了python 2.在python3中,map返回一个生成器,所以至少我的一些声明不是真的)
map(f, iterable)基本上相当于:
[f(x) for x in iterable]但我遇到了一些简单的例子,但事实并非如此。
但调用_PyObject_LengthHint应该基本上等同于不调用。一个对象的__len__或__length_hint__不应该像这样改变该对象。您也可以说 和map(f, iterable)是[f(x) for x in iterable]不等价的,因为 iff使用堆栈检查来确定它是否被调用map并执行不同的操作,这两个片段的行为不同。
至于为什么map这样做,它试图将列表预先分配到正确的大小,以避免需要调整列表的大小。调整大小只会使速度减慢一个常数因子,但如果您可以避免常数因子,为什么不呢?列表推导式在未来的 Python 版本中执行此操作是完全合理的。
| 归档时间: |
|
| 查看次数: |
143 次 |
| 最近记录: |