如果结果必须是int,我们可以将其扩展为[*map(int, map(truth, a))],但是int是一个构造函数,即使它返回单实例值(CPython缓存-5到256的单个副本作为实现细节),它仍然会支付构造函数开销(更糟的是,因为它可以接受关键字参数)。没有像bool那样的等效“convert to trueint”函数有operator.truth,但是您可以通过“adding to 0”将您的方式欺骗为一个:
>>> %%timeit -r5
... [int(bool(x)) for x in a]
...
...
585 µs ± 65.2 µs per loop (mean ± std. dev. of 5 runs, 1000 loops each)
>>> %%timeit -r5
... [*map(int, map(bool, a))]
...
...
363 µs ± 58.6 µs per loop (mean ± std. dev. of 5 runs, 1000 loops each)
>>> %%timeit -r5
... [*map((0).__add__, map(truth, a))]
...
...
168 µs ± 2.2 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)
>>> %%timeit -r5
... [not not x for x in a] # Worse than map
...
...
122 µs ± 6.6 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)
>>> %%timeit -r5
... [0 + (not not x) for x in a] # BETTER than map!!!
...
...
158 µs ± 22.4 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)
>>> %%timeit -r5
...: [0 + x for x in map(truth, a)] # Somehow not the best of both worlds...
...:
...:
177 µs ± 5.77 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)
虽然[not not x for x in a]输给了[*map(truth, a)],[0 + (not not x) for x in a]实际上打败了[*map((0).__add__, map(truth, a))](碰巧,通过tp_add槽周围的包装器调用(0).__add__会有一些开销,这可以通过在Python层实际使用+来避免)。不过,将每种解决方案中的最佳方案(map(truth与list comp中的0 +)混合使用实际上并没有给我们带来好处(读取字节码开销大致是一个固定成本,not not甚至比operator.truth还要快)。关键是,除非您真的需要,否则这些都不值得,而且性能可能是不直观的。我曾经有过需要它的代码,所以你可以从我的测试中受益。你知道吗
要转换为真正的布尔值,可以使用:
这是回报
如果您更喜欢0和1,那么:
将返回:
除非代码是程序中最热门的代码,否则实际上不建议这样做,但有一些方法可以改进:
首先,如果
a
足够大,因为int
/bool
是用C实现的内置程序,您可以使用map
删除字节码解释器开销:另一个节省可以来自不使用
bool
构造函数(C构造函数调用在CPython上有不可避免的开销,即使结果实际上没有“构造”任何东西),并且用operator.truth
(一个只接受一个参数的普通函数,CPython对其进行了大量优化)替换它可以显著减少开销,而且使用它可以将开销减少40%:map(bool
提高了大约45%的单子理解力,反过来又被map(truth
击败了40%(map(truth
几乎占据了单子理解力的三分之一)。你知道吗如果结果必须是
int
,我们可以将其扩展为[*map(int, map(truth, a))]
,但是int
是一个构造函数,即使它返回单实例值(CPython缓存-5到256的单个副本作为实现细节),它仍然会支付构造函数开销(更糟的是,因为它可以接受关键字参数)。没有像bool
那样的等效“convert to trueint
”函数有operator.truth
,但是您可以通过“adding to0
”将您的方式欺骗为一个:(0).__add__
只是利用了这样一个事实:将bool
添加到0
会产生0
或1
,__add__
的开销远远低于构造函数;在这种情况下,从列表理解切换到map
(甚至嵌套的map
)节省了近40%,从int
/bool
切换到(0).__add__
/truth
节省了几乎55%的剩余时间,运行时间减少了70%以上。你知道吗再说一次,要清楚,不要这样做,除非:
a
只是五个元素,那么调用map
的设置开销将超过避免每个循环使用字节码所节省的微小开销)但当它出现的时候,很高兴知道。
bool
是Python中速度最慢的东西之一间接费用:生产性工作类似于int
的事物的int
比率也同样糟糕。你知道吗不过,还有最后一件事要检查。也许把事情推进语法,避免函数调用,可以节省更多。碰巧,答案是“是的,对他们中的一个来说”:
虽然
[not not x for x in a]
输给了[*map(truth, a)]
,[0 + (not not x) for x in a]
实际上打败了[*map((0).__add__, map(truth, a))]
(碰巧,通过tp_add
槽周围的包装器调用(0).__add__
会有一些开销,这可以通过在Python层实际使用+
来避免)。不过,将每种解决方案中的最佳方案(map(truth
与list comp中的0 +
)混合使用实际上并没有给我们带来好处(读取字节码开销大致是一个固定成本,not not
甚至比operator.truth
还要快)。关键是,除非您真的需要,否则这些都不值得,而且性能可能是不直观的。我曾经有过需要它的代码,所以你可以从我的测试中受益。你知道吗您可以在列表理解中使用
and
运算符来保持代码的快速性和可读性:此方法比@ShadowRanger的最快方法更快,如下所示: https://repl.it/@blhsing/NeglectedClientsideLanserver
相关问题 更多 >
编程相关推荐