String字符串拼接join()和+效率问题和Python计时器计算函数消耗时间

1、使用操作符+连接字符串

当用操作符+连接字符串的时候,由于字符串是不可变对象,其工作原理实际上是这样的:
如果要连接如下字符串:S1+S2+S3+…+SN,执行一次+操作便会在内存中申请一块新的内存空间,
并将上一次操作的结果和本次操作的右操作数复制到新申请的内存空间,即当执行S1+S2的时候会申请一块内存,
并将S1、S2复制到该内存中,依次类推,如图1所示。

因此,在N个字符串连接的过程中,会产生N-1个中间结果,每产生一个中间结果都需要申请和复制一次内存,
总共需要申请N-1次内存,从而严重影响了执行效率。N越大,对内存的申请和复制的次数越多,+操作符的效率就越低。
因此,整个字符连接的过程中,相当于S1被复制N-1次,S2被复制N-2次…SN复制1次(并不完全等同于S1复制N-1次,
因为后续复制都是对中间结果的复制),所以字符串的连接时间复杂度近似为O(n^2)。

图1 操作符+连接字符串示意图
                                图1 操作符+连接字符串示意图

2、使用join()方法连接字符串

而当用join()方法连接字符串的时候,会首先计算需要申请的总的内存空间,然后一次性申请所需内存并将字符序列
中的每一个元素复制到内存中去,所以join操作的时间复杂度为O(n)。

3、总结

因此,字符串的连接,特别是大规模字符串的处理,应该尽量优先使用join而不是+。

4、源码:

测试环境:

Python3+
win10
PyCharm 2018.2

4.1 字符串连接比较的模块

joinAndPlus.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import timeit
import time

from timeCount.timeDecorator import clock       # 导入装饰器函数
"""
字符串拼接中:''.join() 和 + 的效率比较,数据量大时join明显占优
总结:
连接字符串时,应优先选择使用 join 而不是 +
"""

# 生成测试所需要的字符串列表, 10000为字符串连接的数目
str_list = ["it is a long value string will not keep in memory" for n in range(1000)]


def join_test():
    """ join对应的测试数据 """
    return ''.join(str_list)


def plus_test():
    """ 直接使用 + 连接符 """
    result = ''
    for i, v in enumerate(str_list):
        result += v
    return result


@clock
def time_test():
    """ 时间测试函数,睡眠1s """
    time.sleep(1)


if __name__ == "__main__":
    join_timer = timeit.Timer("join_test()", "from __main__ import join_test")
    print(join_timer.timeit(number=1000))

    plus_timer = timeit.Timer("plus_test()", "from __main__ import plus_test")
    print(plus_timer.timeit(number=1000))

    """ 这里还使用了计时装饰器,所以显示两个时间 """
    time_timer = timeit.Timer("time_test()", "from __main__ import time_test")
    print(time_timer.timeit(number=1))
   

4.2 计时装饰器模块

timeCount/timeDecorator.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time
import timeit
"""
装饰器实现程序函数的⏲计时器功能
"""


def clock(func):
    """ 计时装饰器 """
    def clocked(*args):
        t0 = timeit.default_timer()
        result = func(*args)
        elapsed = timeit.default_timer() - t0
        name = func.__name__
        arg_str = ', '.join(repr(arg) for arg in args)
        print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
        return result

    return clocked


@clock
def run(seconds):
    """ 测试计时的函数 """
    time.sleep(seconds)
    return time


@clock
def time_test():
    """ 测试计时的函数 """
    time.sleep(2)


if __name__ == '__main__':
    run(1.22)
    time_test()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值