python比心代码_你写的 Python 代码可以更“瘦”

2024-02-03 15:50
文章标签 python 代码 比心

本文主要是介绍python比心代码_你写的 Python 代码可以更“瘦”,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

format,png作者:intellimath  译者 :弯月,责编:郭芮出品:CSDN

在执行程序时,如果内存中有大量活动的对象,就可能出现内存问题,尤其是在可用内存总量有限的情况下。在本文中,我们将讨论缩小对象的方法,大幅减少 Python 所需的内存。

format,png为了简便起见,我们以一个表示点的 Python 结构为例,它包括 x、y、z 坐标值,坐标值可以通过名称访问。Dict在小型程序中,特别是在脚本中,使用 Python 自带的 dict 来表示结构信息非常简单方便:>>> ob = {'x':1, 'y':2, 'z':3}

>>> x = ob['x']

>>> ob['y'] = y由于在 Python 3.6 中 dict 的实现采用了一组有序键,因此其结构更为紧凑,更深得人心。但是,让我们看看 dict 在内容中占用的空间大小:>>> print(sys.getsizeof(ob))

240

如上所示,dict 占用了大量内存,尤其是如果突然虚需要创建大量实例时:实例数对象大小

1 000 000240 Mb

10 000 0002.40 Gb

100 000 00024 Gb类实例有些人希望将所有东西都封装到类中,他们更喜欢将结构定义为可以通过属性名访问的类:class Point:

#

def __init__(self, x, y, z):

self.x = x

self.y = y

self.z = z

>>> ob = Point(1,2,3)

>>> x = ob.x

>>> ob.y = y类实例的结构很有趣:字段大小(比特)

PyGC_Head24

PyObject_HEAD16

__weakref__8

__dict__8

合计:56在上表中,__weakref__ 是该列表的引用,称之为到该对象的弱引用(weak reference);字段 __dict__ 是该类的实例字典的引用,其中包含实例属性的值(注意在 64-bit 引用平台中占用 8 字节)。从 Python 3.3 开始,所有类实例的字典的键都存储在共享空间中。这样就减少了内存中实例的大小:>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__))

56 112因此,大量类实例在内存中占用的空间少于常规字典(dict):实例数大小

1 000 000168 Mb

10 000 0001.68 Gb

100 000 00016.8 Gb不难看出,由于实例的字典很大,所以实例依然占用了大量内存。带有 __slots__ 的类实例为了大幅降低内存中类实例的大小,我们可以考虑干掉 __dict__ 和__weakref__。为此,我们可以借助 __slots__:class Point:

__slots__ = 'x', 'y', 'z'

def __init__(self, x, y, z):

self.x = x

self.y = y

self.z = z

>>> ob = Point(1,2,3)

>>> print(sys.getsizeof(ob))

64如此一来,内存中的对象就明显变小了:字段大小(比特)

PyGC_Head24

PyObject_HEAD16

x8

y8

z8

总计:64在类的定义中使用了 __slots__ 以后,大量实例占据的内存就明显减少了:实例数大小

1 000 00064 Mb

10 000 000640 Mb

100 000 0006.4 Gb目前,这是降低类实例占用内存的主要方式。这种方式减少内存的原理为:在内存中,对象的标题后面存储的是对象的引用(即属性值),访问这些属性值可以使用类字典中的特殊描述符:>>> pprint(Point.__dict__)

mappingproxy(

....................................

'x': 'x' of 'Point' objects>,

'y': 'y' of 'Point' objects>,

'z': 'z' of 'Point' objects>})为了自动化使用 __slots__ 创建类的过程,你可以使用库namedlist(https://pypi.org/project/namedlist)。namedlist.namedlist 函数可以创建带有 __slots__ 的类:>>> Point = namedlist('Point', ('x', 'y', 'z'))还有一个包 attrs(https://pypi.org/project/attrs),无论使用或不使用 __slots__ 都可以利用这个包自动创建类。元组Python 还有一个自带的元组(tuple)类型,代表不可修改的数据结构。元组是固定的结构或记录,但它不包含字段名称。你可以利用字段索引访问元组的字段。在创建元组实例时,元组的字段会一次性关联到值对象:>>> ob = (1,2,3)

>>> x = ob[0]

>>> ob[1] = y # ERROR元组实例非常紧凑:>>> print(sys.getsizeof(ob))

72由于内存中的元组还包含字段数,因此需要占据内存的 8 个字节,多于带有 __slots__ 的类:字段大小(字节)

PyGC_Head24

PyObject_HEAD16

ob_size8

[0]8

[1]8

[2]8

总计:72命名元组由于元组的使用非常广泛,所以终有一天你需要通过名称访问元组。为了满足这种需求,你可以使用模块 collections.namedtuple。namedtuple 函数可以自动生成这种类:>>> Point = namedtuple('Point', ('x', 'y', 'z'))如上代码创建了元组的子类,其中还定义了通过名称访问字段的描述符。对于上述示例,访问方式如下:class Point(tuple):

#

@property

def _get_x(self):

return self[0]

@property

def _get_y(self):

return self[1]

@property

def _get_z(self):

return self[2]

#

def __new__(cls, x, y, z):

return tuple.__new__(cls, (x, y, z))这种类所有的实例所占用的内存与元组完全相同。但大量的实例占用的内存也会稍稍多一些:实例数大小

1 000 00072 Mb

10 000 000720 Mb

100 000 0007.2 Gb记录类:不带循环 GC 的可变更命名元组由于元组及其相应的命名元组类能够生成不可修改的对象,因此类似于 ob.x 的对象值不能再被赋予其他值,所以有时还需要可修改的命名元组。由于 Python 没有相当于元组且支持赋值的内置类型,因此人们想了许多办法。在这里我们讨论一下记录类(recordclass,https://pypi.org/project/recordclass),它在 StackoverFlow 上广受好评(https://stackoverflow.com/questions/29290359/existence-of-mutable-named-tuple-in)。此外,它还可以将对象占用的内存量减少到与元组对象差不多的水平。recordclass 包引入了类型 recordclass.mutabletuple,它几乎等价于元组,但它支持赋值。它会创建几乎与 namedtuple 完全一致的子类,但支持给属性赋新值(而不需要创建新的实例)。recordclass 函数与 namedtuple 函数类似,可以自动创建这些类:>>>Point = recordclass('Point', ('x', 'y', 'z'))

>>>ob = Point(1, 2, 3)类实例的结构也类似于 tuple,但没有 PyGC_Head:字段大小(字节)

PyObject_HEAD16

ob_size8

x8

y8

z8

总计:48在默认情况下,recordclass 函数会创建一个类,该类不参与垃圾回收机制。一般来说,namedtuple 和 recordclass 都可以生成表示记录或简单数据结构(即非递归结构)的类。在 Python 中正确使用这二者不会造成循环引用。因此,recordclass 生成的类实例默认情况下不包含 PyGC_Head 片段(这个片段是支持循环垃圾回收机制的必需字段,或者更准确地说,在创建类的 PyTypeObject 结构中,flags 字段默认情况下不会设置 Py_TPFLAGS_HAVE_GC 标志)。大量实例占用的内存量要小于带有 __slots__ 的类实例:实例数大小

1 000 00048 Mb

10 000 000480 Mb

100 000 0004.8 Gbdataobjectrecordclass 库提出的另一个解决方案的基本想法为:内存结构采用与带 __slots__ 的类实例同样的结构,但不参与循环垃圾回收机制。这种类可以通过 recordclass.make_dataclass 函数生成:>>> Point = make_dataclass('Point', ('x', 'y', 'z'))这种方式创建的类默认会生成可修改的实例。另一种方法是从 recordclass.dataobject 继承:class Point(dataobject):

x:int

y:int

z:int这种方法创建的类实例不会参与循环垃圾回收机制。内存中实例的结构与带有 __slots__ 的类相同,但没有 PyGC_Head:字段大小(字节)

PyObject_HEAD16

ob_size8

x8

y8

z8

总计:48>>> ob = Point(1,2,3)

>>> print(sys.getsizeof(ob))

40如果想访问字段,则需要使用特殊的描述符来表示从对象开头算起的偏移量,其位置位于类字典内:mappingproxy({'__new__': 0x7f203c4e6be0>,

.......................................

'x': 0x7f203c55c690>,

'y': 0x7f203c55c670>,

'z': 0x7f203c55c410>})大量实例占用的内存量在 CPython 实现中是最小的:实例数大小

1 000 00040 Mb

10 000 000400 Mb

100 000 0004.0 GbCython还有一个基于 Cython(https://cython.org/)的方案。该方案的优点是字段可以使用 C 语言的原子类型。访问字段的描述符可以通过纯 Python 创建。例如:cdef class Python:

cdef public int x, y, z

def __init__(self, x, y, z):

self.x = x

self.y = y

self.z = z本例中实例占用的内存更小:>>> ob = Point(1,2,3)

>>> print(sys.getsizeof(ob))

32内存结构如下:字段大小(字节)

PyObject_HEAD16

x4

y4

z4

nycto4

总计:32大量副本所占用的内存量也很小:实例数大小

1 000 00032 Mb

10 000 000320 Mb

100 000 0003.2 Gb但是,需要记住在从 Python 代码访问时,每次访问都会引发 int 类型和 Python 对象之间的转换。Numpy使用拥有大量数据的多维数组或记录数组会占用大量内存。但是,为了有效地利用纯 Python 处理数据,你应该使用 Numpy 包提供的函数。>>> Point = numpy.dtype(('x', numpy.int32), ('y', numpy.int32), ('z', numpy.int32)])一个拥有 N 个元素、初始化成零的数组可以通过下面的函数创建:>>>points = numpy.zeros(N, dtype=Point)内存占用是最小的:实例数大小

1 000 00012 Mb

10 000 000120 Mb

100 000 0001.2 Gb一般情况下,访问数组元素和行会引发 Python 对象与 C 语言 int 值之间的转换。如果从生成的数组中获取一行结果,其中包含一个元素,其内存就没那么紧凑了:>>> sys.getsizeof(points[0])

  68因此,如上所述,在 Python 代码中需要使用 numpy 包提供的函数来处理数组。总结在本文中,我们通过一个简单明了的例子,求证了 Python 语言(CPython)社区的开发人员和用户可以真正减少对象占用的内存量。原文:https://habr.com/en/post/458518format,png

由于微信平台算法改版,公号内容将不再以时间排序展示,如果大家想第一时间看到我们的推送,强烈建议星标我们和给我们多点点【在看】。星标具体步骤为:(1)点击页面最上方“小詹学Python”,进入公众号主页。

(2)点击右上角的小点点,在弹出页面点击“设为星标”,就可以啦。

感谢支持,比心。

format,png

这篇关于python比心代码_你写的 Python 代码可以更“瘦”的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/674651

相关文章

Python将博客内容html导出为Markdown格式

《Python将博客内容html导出为Markdown格式》Python将博客内容html导出为Markdown格式,通过博客url地址抓取文章,分析并提取出文章标题和内容,将内容构建成html,再转... 目录一、为什么要搞?二、准备如何搞?三、说搞咱就搞!抓取文章提取内容构建html转存markdown

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Python Websockets库的使用指南

《PythonWebsockets库的使用指南》pythonwebsockets库是一个用于创建WebSocket服务器和客户端的Python库,它提供了一种简单的方式来实现实时通信,支持异步和同步... 目录一、WebSocket 简介二、python 的 websockets 库安装三、完整代码示例1.

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Python使用自带的base64库进行base64编码和解码

《Python使用自带的base64库进行base64编码和解码》在Python中,处理数据的编码和解码是数据传输和存储中非常普遍的需求,其中,Base64是一种常用的编码方案,本文我将详细介绍如何使... 目录引言使用python的base64库进行编码和解码编码函数解码函数Base64编码的应用场景注意