内存管理

psutil

psutil.virtual_memory()可以查看本机的内存使用,其中total表示总的物理内存,available表示可以立即分配给进程而无需系统进入交换的内存。(但我认为他讲的也没有特别清楚,比如availablefree之间的关系)

具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import psutil
import os

if __name__ == '__main__':
mem = psutil.virtual_memory()
# 系统总计内存
zj = float(mem.total) / 1024 / 1024 / 1024
# 系统已经使用内存
ysy = float(mem.used) / 1024 / 1024 / 1024
# 系统空闲内存
kx = float(mem.available) / 1024 / 1024 / 1024

print('系统总计内存:%.4fGB' % zj)
print('系统已经使用内存:%.4fGB' % ysy)
print('系统空闲内存:%.4fGB' % kx)
print(u'当前进程的内存使用:%.4f GB' % (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 / 1024))
print(u'cpu个数:', psutil.cpu_count())

运行结果:

1
2
3
4
5
系统总计内存:11.7141GB
系统已经使用内存:4.1249GB
系统空闲内存:7.0015GB
当前进程的内存使用:0.0112 GB
cpu个数: 6

memory_profile

除此之外python查看内存还可以通过memory_profiler,在测试函数前加上@profile,在运行代码时加上-m memory_profiler就可以了解函数每一步代码的内存占用。

guppy

如果我们关注对象的内存占用,可以使用guppy。guppy直接打印出对应各种python类型(list、tuple、dict等)分别创建了多少对象,占用了多少内存。

具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
from guppy import hpy
import random
def my_func():
result = []
def _get():
return chr(random.randint(0,100))
for i in range(10):
mk = [_get() for i in range(10)]
result.extend(mk)
return "".join(result)
if __name__ == '__main__':
profile = hpy()
print(profile.iso(my_func()))

参考文章:

https://psutil.readthedocs.io/en/latest/

https://blog.csdn.net/zhou_xiong1130/article/details/108990167

读取文件

在python读取文件一般采用下述代码

1
2
3
with open(fname, 'r', encoding='utf-8') as f:  # 打开文件
lines = f.readlines() # 读取指针位置之后所有行
line = f.readline() # 读取指针位置所在行

f其实是文件指针位置,一个文件只有一个指针,readlines是将文件从指针位置读到结尾,读完后指针在结尾,此时再使用readline就没有东西可以读了。此时,我们可以使用seek来帮助文件指针移动到指定位置。

以读取文件首行和尾行的代码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
fname = 'test.txt'

with open(fname, 'r', encoding='utf-8') as f: # 打开文件
lines = f.readlines() # 读取所有行
first_line = lines[0] # 取第一行
last_line = lines[-1] # 取最后一行

print('文件' + fname + '第一行为:'+ first_line)
print('文件' + fname + '最后一行为:' + last_line)

# 第二种
with open(fname, 'rb') as f: # 打开文件
# 在文本文件中,没有使用b模式选项打开的文件,只允许从文件头开始,只能seek(offset,0)

first_line = f.readline() # 取第一行
offset = -50 # 设置偏移量

while True:
"""
file.seek(off, whence=0):从文件中移动off个操作标记(文件指针),正往结束方向移动,负往开始方向移动。
如果设定了whence参数,就以whence设定的起始位为准,0代表从头开始,1代表当前位置,2代表文件最末尾位置。
"""
f.seek(offset, 2) # seek(offset, 2)表示文件指针:从文件末尾(2)开始向前50个字符(-50)
lines = f.readlines() # 读取文件指针范围内所有行

if len(lines) >= 2: # 判断是否最后至少有两行,这样保证了最后一行是完整的
last_line = lines[-1] # 取最后一行
break

# 如果off为50时得到的readlines只有一行内容,那么不能保证最后一行是完整的
# 所以off翻倍重新运行,直到readlines不止一行
offset *= 2

print('文件' + fname + '第一行为:' + first_line.decode())
print('文件' + fname + '最后一行为:' + last_line.decode())

python 环境搭建

创建虚拟环境+安装库

  1. 用 txt

    1
    2
    3
    conda create -n myenv python=3.10
    conda activate myenv
    pip3 install -r requirements.txt

    requirements.txt example :

    1
    2
    3
    4
    numpy==1.24.0
    pandas>=1.5.0
    requests
    flask==2.3.0
  2. 用yml

    1
    conda env create -f environment.yml

    environment.yml example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    name: aloha
    channels:
    - pytorch
    - nvidia
    - conda-forge
    dependencies:
    - python=3.9
    - pip=23.0.1
    - pyquaternion=0.9.9
    - pyyaml=6.0
    - rospkg=1.5.0
    - pexpect=4.8.0
    - mujoco=2.3.7
    - py-opencv=4.7.0
    - pip:
    - dm_control==1.0.14
  3. 以“可编辑模式”安装当前目录下的 Python 包 (开发过程中安装项目自身作为一个包)

    1
    2
    3
    4
    5
    my_project/
    ├── my_package/
    │ └── __init__.py
    ├── setup.py
    └── ...
    1
    2
    cd my_projectq
    pip install -e .

    会运行 setup.py example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from distutils.core import setup
    from setuptools import find_packages

    setup(
    name='detr',
    version='0.0.0',
    packages=find_packages(),
    license='MIT License',
    long_description=open('README.md').read(),
    )

    Python 会把 my_package 当作一个可导入的包注册到环境中,路径会被链接到源代码目录。

参考文章:https://zhuanlan.zhihu.com/p/98007747

“editable install” 可编辑安装

当前环境中创建一个指向你的源码目录的软链接(而不是复制一份)。这使得你修改源代码后立即生效,不需要重新安装。

  • 文件结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    my_project/           <-- 项目根目录
    ├── __init__.py
    ├── aa/
    │ ├── __init__.py <-- ✅ 被认为是包
    │ ├── cli.py
    │ └── util
    │ ├── __init__.py <-- ✅ 递归识别为子包
    │ └── box.py
    ├── bb/
    │ └── module2.py <-- ❌ 没有 __init__.py,不会被识别
    ├── setup.py
    ├── README.md
    └── environment.yml
  • setup.py example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from setuptools import setup, find_packages

    setup(
    name="my_package",
    version="0.1.0",
    description="A sample Python package for demonstration",
    author="Your Name",
    author_email="you@example.com",
    packages=find_packages(), # 自动查找所有含 __init__.py 的当前目录和子目录
    install_requires=[
    "numpy>=1.21",
    "opencv-python",
    ],
    entry_points={
    "console_scripts": [
    "mytool=mypkg.cli:main", # 安装后可用 `mytool` 命令运行 mypkg/cli.py 的 main()
    ],
    },
    classifiers=[
    "Programming Language :: Python :: 3",
    ],
    python_requires=">=3.7",
    )

  • 运行

    1
    2
    cd my_project/
    pip install -e .

    被安装的包有['my_project', 'aa', 'aa.util']

  • 查看查看 setup.pyfind_packages() 实际找到了哪些包

    1
    python -c "from setuptools import find_packages; print(find_packages())"

Python 会把 my_package 当作一个可导入的包注册到当前环境中,路径会被链接到源代码目录,只要在当前环境下,都可以使用该包。