Python的os.path.join函数速度慢吗?

3

我听说在Python中,os.path.join非常慢,应该使用字符串拼接('%s/%s' % (x, y))。这真的有很大的区别吗?如果是,我该如何追踪它呢?


6
你熟悉timeit吗? - S.Lott
这就是所谓的“字符串格式化”或“字符串插值”。而“字符串连接”则是 x + '/' + y - John Machin
7个回答

12
$ python -mtimeit -s 'import os.path' 'os.path.join("/root", "file")'
1000000 loops, best of 3: 1.02 usec per loop
$ python -mtimeit '"/root" + "file"'
10000000 loops, best of 3: 0.0223 usec per loop

所以是的,它慢了近50倍。尽管1微秒仍然很短暂,但我真的不认为这种差异是必须考虑的。使用os.path.join:它是跨平台的,更易读且更少出错。

编辑:现在有两个人评论说import解释了差异。这是不正确的,因为-s是设置标志,因此import不会计入报告的运行时间。请阅读文档


@marcog 我明白了,我错过了那个标志。不管怎样,你是对的,一纳秒也没有任何区别。 - Rafe Kettler
3
第一个表达式在此处生成 "/root/file",而第二个表达式生成 "/rootfile"。如果你想要后者,os.path.join 将无法帮助你。如果你想要前者,请注意即使这个一行的演示程序中也存在一个错误。 :-) - Ken
2
它是跨平台的,更易读且更少出现错误。对此赞同。 - Ben
2
@marcog:将两个字符串字面量拼接起来进行比较是一个非常糟糕的“稻草人”论证。自从Python 2.5以来,字符串字面量的拼接会在编译时被优化掉。尝试一下 python -c "import dis; dis.dis(lambda: 'fu' + 'bar')" ,亲眼看看吧。 - John Machin
1
@John,说实话,区别是5倍还是50倍有谁在意呢?结论都是一样的:对于几乎所有目的来说,这种差异都是无意义的,这就是我想表达的观点。 - moinudin
显示剩余5条评论

8

我不知道谁告诉你不要使用它,但他们是错的。

  1. 即使它很慢,它也不会到导致程序崩溃的程度。我从未注意到它变得缓慢。
  2. 这是跨平台编程的关键。行分隔符等因平台而异,而os.path.join将始终正确地连接路径,无论平台如何。
  3. 可读性。每个人都知道join在做什么。对于路径字符串拼接,人们可能需要再看一遍。

3
请注意,函数调用中的句号会导致速度变慢。请比较以下两种写法:
python -mtimeit -s "import os.path;x=range(10)" "os.path.join(x)"
1000000 loops, best of 3: 0.405 usec per loop

python -mtimeit -s "from os.path import join;x=range(10)" "join(x)"
1000000 loops, best of 3: 0.29 usec per loop

今日免费次数已满, 请开通会员/明日再来
$ python -mtimeit -s "from os.path import sep;join=sep.join;x=map(str,range(10))" "join(x)"
1000000 loops, best of 3: 0.253 usec per loop

$ python -mtimeit -s "from os.path import join;x=map(str,range(10))" "join(x)"
1000000 loops, best of 3: 0.285 usec per loop

我完全不知道这个!谢谢! - skjerns

1

为了实现可移植性,您应该简单地使用os.path.join

我不明白将os.path.join(它适用于任意数量或部分,在任何平台上)与像格式化两个路径这样微不足道的东西进行比较的意义所在。

要回答标题中的问题“Python的os.path.join慢吗?”,您至少必须将其与类似的函数进行比较,以找出可以从此类函数期望的速度。

如下所示,与类似的函数相比,os.path.join没有任何缓慢之处

python -mtimeit -s "x = tuple(map(str, range(10)))" "'/'.join(x)"
1000000 loops, best of 3: 0.26 usec per loop

python -mtimeit -s "from os.path import join;x = tuple(range(10))" "join(x)"
1000000 loops, best of 3: 0.27 usec per loop


python -mtimeit -s "x = tuple(range(3))" "('/%s'*len(x)) % x"
1000000 loops, best of 3: 0.456 usec per loop

python -mtimeit -s "x = tuple(map(str, range(3)))" "'/'.join(x)"
10000000 loops, best of 3: 0.178 usec per loop

1

除非你在 CPU 绑定的紧密内部循环中进行操作,否则它可能快近 50 倍,但速度差异并不重要。另一方面,可移植性的差异将决定您的程序是否可以轻松地移植到非 Unix 平台。

因此,请使用 os.path.join,除非你已经进行了分析并发现它确实是你程序性能的主要障碍。


1
即使您已经进行了分析并发现“os.path.join”存在性能问题,仍应尝试使用类似“os.sep.join”或“a + os.sep + b”的东西,这仍将使用正确的分隔符,而不是硬编码的“/”。 - Rosh Oxymoron

0
在这场激烈的争议中,我敢提出以下建议:
(我知道,我知道,有timeit,但我对timeit不是很熟练,而clock()对于这种情况似乎已经足够了)
import os
from time import clock

separ = os.sep
ospath = os.path
ospathjoin = os.path.join

A,B,C,D,E,F,G,H = [],[],[],[],[],[],[],[]
n = 1000

for essays in xrange(100):

    te = clock()
    for i in xrange(n):
        xa = os.path.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')
    A.append(clock()-te)


    te = clock()
    for i in xrange(n):
        xb = ospath.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')
    B.append(clock()-te)


    te = clock()
    for i in xrange(n):
        xc = ospathjoin('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')
    C.append(clock()-te)


    te = clock()
    for i in xrange(n):
        xd = 'C:\WINNT\system32'+os.sep+'Microsoft\Crypto'+os.sep+'RSA\MachineKeys'
    D.append(clock()-te)


    te = clock()
    for i in xrange(n):
        xe = '%s\\%s\\%s' % ('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')
    E.append(clock()-te)


    te = clock()
    for i in xrange(n):
        xf = 'C:\WINNT\system32'+separ+'Microsoft\Crypto'+separ+'RSA\MachineKeys'
    F.append(clock()-te)


    te = clock()
    for i in xrange(n):
        xg = os.sep.join(('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys'))
    G.append(clock()-te)


    te = clock()
    for i in xrange(n):
        xh = separ.join(('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys'))
    H.append(clock()-te)

print min(A), "os.path.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')"
print min(B), "ospath.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')"
print min(C), "ospathjoin('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')"
print min(D), "'C:\WINNT\system32'+os.sep+'Microsoft\Crypto'+os.sep+'RSA\MachineKeys'"
print min(E), "'%s\\%s\\%s' % ('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')"
print min(F), "'C:\WINNT\system32'+separ+'Microsoft\Crypto'+separ+'RSA\MachineKeys'"
print min(G), "os.sep.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')"
print min(H), "separ.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')"
print 'xa==xb==xc==xd==xe==xf==xg==xh==',xa==xb==xc==xd==xe==xf==xg==xh

结果

0.0284533369465 os.path.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')

0.0277652606686 ospath.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')

0.0272489939364 ospathjoin('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')

0.00398598145854 'C:\WINNT\system32'+os.sep+'Microsoft\Crypto'+os.sep+'RSA\MachineKeys'

0.00375075603184 '%s\%s\%s' % ('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')

0.00330824168994 'C:\WINNT\system32'+separ+'Microsoft\Crypto'+separ+'RSA\MachineKeys'

0.00292467338726 os.sep.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')

0.00261401937956 separ.join('C:\WINNT\system32','Microsoft\Crypto','RSA\MachineKeys')

True

使用

separ = os.sep

ospath = os.path

ospathjoin = os.path.join


-2

每个人都应该知道os.path.join()的一个显而易见的特性。

os.path.join( 'a', 'b' ) == 'a/b'
os.path.join( 'a', '/b' ) == '/b'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接