让你的 Python 代码更快的小技巧

news2024/11/24 17:49:34

我们经常听到 “Python 太慢了”,“Python 性能不行”这样的观点。但是,只要掌握一些编程技巧,就能大幅提升 Python 的运行速度。

今天就让我们一起来看下让 Python 性能更高的 9 个小技巧

python学习资料分享(无偿):

字符串拼接的技巧

如果有大量字符串等待处理,字符串连接将成为 Python 的瓶颈。

一般来讲,Python 中有两种字符串拼接方式:

  • 使用该 join() 函数将字符串列表合并为一个字符串
  • 使用 + or += 符号将每个字符串加成一个

那么哪种方式更快呢?我们一起来看一下

mylist = ["Yang", "Zhou", "is", "writing"]


# Using '+'
def concat_plus():
    result = ""
    for word in mylist:
        result += word + " "
    return result


# Using 'join()'
def concat_join():
    return " ".join(mylist)


# Directly concatenation without the list
def concat_directly():
    return "Yang" + "Zhou" + "is" + "writing"
import timeit

print(timeit.timeit(concat_plus, number=10000))
# 0.002738415962085128
print(timeit.timeit(concat_join, number=10000))
# 0.0008482920238748193
print(timeit.timeit(concat_directly, number=10000))
# 0.00021425005979835987

如上所示,对于拼接字符串列表, join() 方法比在 for 循环中逐个添加字符串更快。

原因很简单。一方面,字符串是 Python 中的不可变数据,每个 += 操作都会导致创建一个新字符串并复制旧字符串,这会导致非常大的开销。

另一方面,.join() 方法是专门为连接字符串序列而优化的。它预先计算结果字符串的大小,然后一次性构建它。因此,它避免了与循环中 += 操作相关的开销,因此速度更快。

但是,我们发现最快其实是直接用 + 拼接字符串,这是因为:

  • Python 解释器可以在编译时优化字符串的连接,将它们转换为单个字符串。因为没有循环迭代或函数调用,所以它是一个非常高效的操作。
  • 由于所有字符串在编译时都是已知的,因此 Python 可以非常快速地执行此操作,比循环中的运行时连接甚至优化 .join() 方法快得多。

总之,如果需要拼接字符串列表,请选择 join() ;如果直接拼接字符串,只需使用 + 即可。

创建列表的技巧

Python 中创建列表的两种常见方法是:

  • 使用函数 list()
  • [] 直接使用

我们来看下这两种方法的性能

import timeit

print(timeit.timeit('[]', number=10 ** 7))
# 0.1368238340364769
print(timeit.timeit(list, number=10 ** 7))
# 0.2958830420393497

结果表明,执行 list() 函数比直接使用 [] 要慢。

这是因为 是 [] 字面语法( literal syntax ),而 list() 是构造函数调用。毫无疑问,调用函数需要额外的时间。

同理,在创建字典时,我们也应该利用 {} 而不是 dict()

成员关系测试的技巧

成员关系测试的性能很大程度上取决于底层数据结构

import timeit

large_dataset = range(100000)
search_element = 2077

large_list = list(large_dataset)
large_set = set(large_dataset)


def list_membership_test():
    return search_element in large_list


def set_membership_test():
    return search_element in large_set


print(timeit.timeit(list_membership_test, number=1000))
# 0.01112208398990333
print(timeit.timeit(set_membership_test, number=1000))
# 3.27499583363533e-05

如上面的代码所示,集合中的成员关系测试比列表中的成员关系测试要快得多。

这是为什么呢?

  • 在 Python 列表中,成员关系测试 ( element in list ) 是通过遍历每个元素来完成的,直到找到所需的元素或到达列表的末尾。因此,此操作的时间复杂度为 O(n)。
  • Python 中的集合是作为哈希表实现的。在检查成员资格 ( element in set ) 时,Python 使用哈希机制,其时间复杂度平均为 O(1)。

这里的技巧重点是在编写程序时仔细考虑底层数据结构。利用正确的数据结构可以显著加快我们的代码速度。

使用推导式而不是 for 循环

Python 中有四种类型的推导式:列表、字典、集合和生成器。它们不仅为创建相对数据结构提供了更简洁的语法,而且比使用 for 循环具有更好的性能。

因为它们在 Python 的 C 实现中进行了优化。

import timeit


def generate_squares_for_loop():
    squares = []
    for i in range(1000):
        squares.append(i * i)
    return squares


def generate_squares_comprehension():
    return [i * i for i in range(1000)]


print(timeit.timeit(generate_squares_for_loop, number=10000))
# 0.2797503340989351
print(timeit.timeit(generate_squares_comprehension, number=10000))
# 0.2364629579242319

上面的代码是列表推导式和 for 循环之间的简单速度比较。如结果所示,列表推导式速度更快。

访问局部变量速度更快

在 Python 中,访问局部变量比访问全局变量或对象的属性更快。

import timeit


class Example:
    def __init__(self):
        self.value = 0


obj = Example()


def test_dot_notation():
    for _ in range(1000):
        obj.value += 1


def test_local_variable():
    value = obj.value
    for _ in range(1000):
        value += 1
    obj.value = value


print(timeit.timeit(test_dot_notation, number=1000))
# 0.036605041939765215
print(timeit.timeit(test_local_variable, number=1000))
# 0.024470250005833805

原理也很简单:当编译一个函数时,它内部的局部变量是已知的,但其他外部变量需要时间来检索。

优先考虑内置模块和库

当我们讨论 Python 的时候,通常指的是 CPython,因为 CPython 是 Python 语言的默认和使用最广泛的实现。

考虑到它的大多数内置模块和库都是用C语言编写的,C语言是一种更快、更低级的语言,我们应该利用它的内置库,避免重复造轮子。

import timeit
import random
from collections import Counter


def count_frequency_custom(lst):
    frequency = {}
    for item in lst:
        if item in frequency:
            frequency[item] += 1
        else:
            frequency[item] = 1
    return frequency


def count_frequency_builtin(lst):
    return Counter(lst)


large_list = [random.randint(0, 100) for _ in range(1000)]

print(timeit.timeit(lambda: count_frequency_custom(large_list), number=100))
# 0.005160166998393834
print(timeit.timeit(lambda: count_frequency_builtin(large_list), number=100))
# 0.002444291952997446

上面的程序比较了计算列表中元素频率的两种方法。正如我们所看到的,利用 collections 模块的内置计数器比我们自己编写 for 循环更快、更简洁、更好。

使用缓存装饰器

缓存是避免重复计算和提高程序速度的常用技术。

幸运的是,在大多数情况下,我们不需要编写自己的缓存处理代码,因为 Python 提供了一个开箱即用的装饰器 — @functools.cache 。

例如,以下代码将执行两个斐波那契数生成函数,一个具有缓存装饰器,但另一个没有:

import timeit
import functools


def fibonacci(n):
    if n in (0, 1):
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


@functools.cache
def fibonacci_cached(n):
    if n in (0, 1):
        return n
    return fibonacci_cached(n - 1) + fibonacci_cached(n - 2)


# Test the execution time of each function
print(timeit.timeit(lambda: fibonacci(30), number=1))
# 0.09499712497927248
print(timeit.timeit(lambda: fibonacci_cached(30), number=1))
# 6.458023563027382e-06

可以看到 functools.cache 装饰器如何使我们的代码运行得更快。

缓存版本的速度明显更快,因为它缓存了先前计算的结果。因此,它只计算每个斐波那契数一次,并从缓存中检索具有相同参数的后续调用

while 1 VS while True

如果要创建无限 while 循环,我们可以使用 while True or while 1 .

它们的性能差异通常可以忽略不计。但有趣的是, while 1 稍微快一点。

这是因为是 1 字面量,但 True 是一个全局名称,需要在 Python 的全局作用域中查找。所以 1 的开销很小。

import timeit


def loop_with_true():
    i = 0
    while True:
        if i >= 1000:
            break
        i += 1


def loop_with_one():
    i = 0
    while 1:
        if i >= 1000:
            break
        i += 1


print(timeit.timeit(loop_with_true, number=10000))
# 0.1733035419601947
print(timeit.timeit(loop_with_one, number=10000))
# 0.16412191605195403

正如我们所看到的,确实 while 1 稍微快一些。

然而,现代 Python 解释器(如 CPython )是高度优化的,这种差异通常是微不足道的。所以我们不需要担心这个可以忽略不计的差异。更不用说 while True 比 while 1 可读性更好。

按需导入 Python 模块

在 Python 脚本开头导入所有模块似乎是每个人都会这么做的操作,事实上我们没有必要导入全部的模块。如果模块太大,则根据需要导入它是一个更好的主意。

def my_function():
    import heavy_module
    # rest of the function

如上面的代码所示,heavy_module 在函数中导入。这是一种“延迟加载”的思想:只有 my_function 被调用的时候该模块才会被导入。

这种方法的好处是,如果 my_function 在脚本执行期间从未调用过,则 heavy_module 永远不会加载,从而节省资源并减少脚本的启动时间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1849758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PXE自动平台 搭建 银河麒麟 UEFI x86_64 ARM64

1. PXE自动化 原理 要实现PXE自动安装需要以下组件: DHCP服务:服务器通过网络启动时自动分配IP地址。TFTP服务:提供服务器启动下载启动引导EFI。HTTP服务:操作系统镜像下载。 各组件工作原理如下[1]: 开PXE后&…

Android-app自动更新总结(已适配9-0)(1)

} //检查版本号,第一次请求(post),,,UpdateAppBean根据服务器返回生成 private void requestAppUpdate(int version, final DataRequestListener listener) { OkGo.post(Const.HOST_URL Const.UPDATEAPP).params(“version”, v…

leetcode 动态规划 (基础版) 下降路径最小和

题目: 题解: 这题和三角型路径和相似,但这题无法在像哪一题一样通过换一个方向逃避下标特判。所以这道题就写一个下标特判的方案。特殊的下标是每一行的第一个元素和最后一个元素,它们由头上的一个元素和左上和右上中的其中一个…

ArcGIS与Excel分区汇总统计三调各地类面积!数据透视表与汇总统计!

​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 01 需求说明 介绍一下ArcGIS与Excel统计分区各地类的三调地类面积。 ArcGIS统计分析不会&#x…

Xshell7免费版下载安装使用

​一、下载安装​ 1.打开官网下载 https://www.xshell.com/zh/free-for-home-school/ 2.选择合适的下载路径,点击下载按钮,然后按照提示完成安装。 二、Xshell7的使用,Xhell连接Linux 1.连接之前,确保在Linux中开启SSH。参考&a…

大数据存储技术笔记

目录 大数据的特性 HDFS 读流程的基本步骤 HDFS 写流程的基本步骤 Mapreduce的执行过程 MapReduce 中 combiner 作用 hadoop 调度器及其工作方法 Hive 中内部表与外部表区别(创建删除角度) Hadoop 的 2 个主要组件及其功能 Hadoop MapReduce 的工作流程 正常工作的 ha…

百余App通过蚂蚁数科mPaaS启动鸿蒙开发测试

6月21日,在华为开发者大会主论坛上,蚂蚁数科mPaaS公布了三方生态共建进展:华夏银行、广发银行、中石油、中国移动等200余App启动鸿蒙开发测试。此前,该产品已全量适配鸿蒙100余个SDK,并提供20余项安全能力,…

vue3中h函数的使用

h函数是用于创建一个 vnodes ,它既可以用于创建原生元素,也可以创建组件,其渲染后的效果等同于使用模版语言来进行创建。 h函数的传参如下: // 完整参数签名 function h(type: string | Component,props?: object | null,child…

XMLXXE实体注入

XML&XXE实体注入 原理 XML被设计为传输和存储数据,XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素,其焦点是数据的内容,其把数据从HTML分离,是独立于软件和硬件的信息传输工具。等同于JSO…

h5兼容问题 复制粘贴移动端无法粘贴复制内容

const selectText (textbox, startIndex, stopIndex) > {if (textbox.createTextRange) {//ieconst range textbox.createTextRange();range.collapse(true);range.moveStart(character, startIndex);//起始光标range.moveEnd(character, stopIndex - startIndex);//结束光…

Web渗透:XSS-DOM-based XSS

DOM-based XSS(基于DOM的跨站脚本攻击)是一种XSS攻击类型,其特点是恶意脚本通过操作文档对象模型(DOM)直接在客户端执行,而无需经过服务器的处理。这种攻击主要利用客户端JavaScript代码中的漏洞&#xff0…

如何利用数据仓库进行业务分析:一名大数据工程师的视角

在大数据时代,数据的有效利用对企业的成功至关重要。 本文将基于上面的流程图,详细介绍如何利用数据仓库进行业务分析,并提供实际的例子和代码演示,以帮助读者更好地理解和应用相关技术。 数据仓库的基本流程 上图展示了一个典…

【计算机网络仿真】b站湖科大教书匠思科Packet Tracer——实验6 生成树协议STP的功能

一、实验目的 1.验证以太网交换机生成树协议的功能; 2.理解网络环路对网络的负面效应; 3.理解生成树协议的作用。 二、实验要求 1.使用Cisco Packet Tracer仿真平台; 2.观看B站湖科大教书匠仿真实验视频,完成对应实验。 三、实…

【2024最新精简版】网络_Linux操作系统面试篇

文章目录 简述 tcp 和 udp的区别?get 和 post 请求有哪些区别?常用HTTP请求方式有哪些 ?进程跟线程的区别?僵尸进程?IO的多路复用?虚拟内存什么是OSI模型说一说HTTP协议说一说HTTPS协议HTTPS协议和HTTP协议有什么区别…

使用阿里开源的Spring Cloud Alibaba AI开发第一个大模型应用

背景 前段时间看到Spring推出了SpringAI,可以方便快速的接入ChatGPT等国外的大模型,现在阿里巴巴也紧追脚步推出了Spring Cloud Alibaba AI,Spring Cloud Alibaba AI 目前基于 Spring AI 0.8.1 版本 API 完成通义系列大模型的接入。通义接入…

PHP发送HTML邮件的步骤?设置模板的技巧?

PHP发送HTML邮件怎么设置模板?如何用PHP群发邮件? PHP提供了强大的功能来发送HTML格式的电子邮件,这在需要发送格式化内容的邮件时特别有用。AokSend将详细介绍PHP发送HTML邮件的步骤,涵盖了必要的准备工作和实际操作过程。 PHP…

离线源码编译安装zabbix-agent

文章目录 1,先进zabbix官网下载zabbix的源码包2,把这个zabix源码包上传到需要安装的目标机器上去3,编译安装3.1 ,解压软件包得到一个zabbix-5.0.42的文件夹3.2,进入/vdb/zabbix-5.0.42文件夹,依次执行命令 …

STM32单片机USART串口收发数据包

文章目录 1. 串口通信 1.1 串口初始化 1.2 库函数 2. 串口收发HEX数据包 2.1 Serial.c 2.2 Serial.h 2.3 main.c 3. 串口收发文本数据包 3.1 Serial.c 3.2 Serial.h 3.3 main.c​​​​​​​ 1. 串口通信 对于串口通信的详细​​​​​​​解析可以看下面这篇文章…

锂磷硫(LPS)属于硫化物固态电解质 Li7P3S11是代表性产品

锂磷硫(LPS)属于硫化物固态电解质 Li7P3S11是代表性产品 锂磷硫(LPS),为非晶态材料,是硫化物固态电解质代表性产品之一,具有热稳定性好、成本较低等优点,在固态电解质中离子电导率较…