【问题标题】:Inverse of `operator.itemgetter``operator.itemgetter` 的逆
【发布时间】:2012-10-09 04:11:40
【问题描述】:

我最近正在处理this question。从本质上讲,my answer 涉及对sorted 的调用,其中lambda 作为key

sorted(range(len(L)), key=lambda i : L[i])

鉴于性能是问题的核心,并且 lambda 本身就很慢,我可以通过定义一个函数并使用它来代替 lambda 来进行一些优化。

不过,我觉得我会重新发明轮子。在某处或某些importable 模块中必须有一个内置函数,它提供__getitem__ 的功能(我不想使用的唯一原因是使用重整方法并不是真正的pythonic )。

我知道operator.getitem,让我预定义一个索引i 并在任何输入序列中获取i 处的元素。但是有没有一个函数(比如foo)工作如下:

In [14]: g = operator.itemgetter(1)

In [15]: d = {'a':1, 'b':2, 'c':3, 'd':4}

In [16]: for i in d.iteritems():
   ....:     print g(i),
   ....:     
1 3 2 4

In [17]: L = list('abcd')

In [18]: g = foo(L)

In [19]: for i in range(4):
   ....:     print g(i),
   ....:     
'a' 'b' 'c' 'd'

对不起,如果这是一个重复的问题,但我能想到的搜索词没有产生结果。

【问题讨论】:

  • 当值不唯一时,您的预期行为是什么?
  • Lambda 与任何其他 Python 函数一样只是函数。它们并不比执行相同操作的“长格式”函数(带有def 语句)慢或快。
  • @wim:具有非唯一值的预期行为 - 返回与上次相同的答案,就像 L[i] 假设 L 在两次调用之间没有改变一样
  • 所以它似乎就像__getitem__ 一样,除非我错过了什么?你想要类似于 dict.get 但用于列表的东西吗?
  • 错误的地方使用__getitem__不是pythonic。这不是错误的地方之一。如果您需要将方法传递给函数,那么使用这些方法是非常明智的。

标签: python list indexing


【解决方案1】:

如果我正确理解了您想要的内容,则以下内容会这样做:

import functools
import operator

L = list('abcd')

def foo(indexable):
    return functools.partial(operator.__getitem__, indexable)

g = foo(L)

for i in xrange(len(L)):
    print g(i),

更新:

我进行了进一步的实验,并惊讶地发现了一个稍微快一点的解决方案,就是这样:

def foo2(indexable):
    return indexable.__getitem__

当使用我拼凑的一个小测试台运行时,产生了以下结果:

fastest to slowest *_test() function timings:
 10,000 elements, 1,000 timeit calls, best of 3

  foo2_test() : 1.46 (0.00 times slower)
lambda_test() : 4.15 (1.84 times slower)
   foo_test() : 4.28 (1.93 times slower)

使用的每个测试函数只是使用不同的技术在紧密循环中访问列表的每个元素。

很好奇这如何应用于您对链接问题的排序答案,我使用它对列表进行排序而不是仅访问列表的每个元素一次,从而获得了这些不同的结果:

fastest to slowest *_test() function timings:
 10,000 elements, 1,000 timeit calls, best of 3

  foo2_test() : 13.03 (0.00 times slower)
   foo_test() : 14.70 (0.13 times slower)
lambda_test() : 16.25 (0.25 times slower)

虽然foo2() 在这两种情况下都是最快的,但在排序版本中它只是很小的一部分。

以下是用于获取第一组结果以进行简单访问的完整测试平台列表:

import functools
import operator

import timeit
import types

N = 1000
R = 3
SZ = 10000
SUFFIX = '_test'
SUFFIX_LEN = len(SUFFIX)

def setup():
    import random
    global a_list
    a_list = [random.randrange(100) for _ in xrange(SZ)]

def lambda_test():
    global a_list
    f = lambda i: a_list[i]
    for i in xrange(len(a_list)): f(i)

def foo(indexable):
    return functools.partial(operator.__getitem__, indexable)

def foo_test():
    global a_list
    g = foo(a_list)
    for i in xrange(len(a_list)): g(i)

def foo2(indexable):
    return indexable.__getitem__

def foo2_test():
    global a_list
    g = foo2(a_list)
    for i in xrange(len(a_list)): g(i)

# find all the functions named *SUFFIX in the global namespace
funcs = tuple(value for id,value in globals().items()
            if id.endswith(SUFFIX) and type(value) is types.FunctionType)

# run the timing tests and collect results
timings = [(f.func_name[:-SUFFIX_LEN],
            min(timeit.repeat(f, setup=setup, repeat=R, number=N))
           ) for f in funcs]
timings.sort(key=lambda x: x[1])  # sort by speed (ironic use of lambda?)
fastest = timings[0][1]  # time fastest one took to run
longest = max(len(t[0]) for t in timings) # len of longest func name (w/o suffix)

print 'fastest to slowest *_test() function timings:\n' \
      ' {:,d} elements, {:,d} timeit calls, best of {:d}\n'.format(SZ, N, R)

def times_slower(speed, fastest):
    return speed/fastest - 1.0

for i in timings:
    print "{0:>{width}}{suffix}() : {1:.2f} ({2:.2f} times slower)".format(
                i[0], i[1], times_slower(i[1], fastest), width=longest, suffix=SUFFIX)

这是测试排序用法时不同的部分:

def setup():
    import random
    global a_list
    a_list = [random.randrange(100) for _ in xrange(SZ)]

def lambda_test():
    global a_list
    sorted(range(len(a_list)), key=lambda i:a_list[i])

def foo(indexable):
    return functools.partial(operator.__getitem__, indexable)

def foo_test():
    global a_list
    sorted(range(len(a_list)), key=foo(a_list))

def foo2(indexable):
    return indexable.__getitem__

def foo2_test():
    global a_list
    sorted(range(len(a_list)), key=foo2(a_list))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-19
    • 2013-09-06
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 2014-12-22
    • 2014-12-20
    相关资源
    最近更新 更多