49

之前已经提出了如何加快 Python 模块导入的问题(加速 python "import" loaderPython -- Speed Up Imports?),但没有具体示例,也没有产生公认的解决方案。因此,我将在这里再次讨论这个问题,但这次是一个具体的例子。

我有一个 Python 脚本,它从磁盘加载 3-D 图像堆栈,对其进行平滑处理并将其显示为电影。当我想快速查看我的数据时,我会从系统命令提示符调用此脚本。我对平滑数据所需的 700 毫秒感到满意,因为这与 MATLAB 相当。但是,导入模块需要额外的 650 毫秒。因此,从用户的角度来看,Python 代码以一半的速度运行。

这是我要导入的一系列模块:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import scipy.ndimage
import scipy.signal
import sys
import os

当然,并非所有模块的导入速度都一样慢。罪魁祸首是:

matplotlib.pyplot   [300ms]
numpy               [110ms]
scipy.signal        [200ms]

我已经尝试过 using from,但这并没有更快。由于 Matplotlib 是罪魁祸首,而且它以缓慢的屏幕更新而闻名,我寻找替代品。一种是 PyQtGraph,但导入需要 550 毫秒。

我知道一个明显的解决方案,即从交互式 Python 会话而不是系统命令提示符中调用我的函数。这很好,但它太像 MATLAB,我更喜欢从系统提示符中获得我的函数的优雅。

我是 Python 新手,目前不知道如何进行。由于我是新手,我很感激有关如何实施提议的解决方案的链接。理想情况下,我正在寻找一个简单的解决方案(不是我们所有人!),因为代码需要在多台 Mac 和 Linux 机器之间移植。

4

5 回答 5

60

不是对这个问题的实际答案,而是关于如何使用 Python 3.7 和金枪鱼(我的一个小项目)分析导入速度的提示:

python3 -X importtime -c "import scipy" 2> scipy.log
tuna scipy.log

在此处输入图像描述

于 2018-07-12T08:39:48.643 回答
23

您可以构建一个简单的服务器/客户端,服务器不断运行制作和更新绘图,客户端只是通信下一个要处理的文件。

我根据socket模块文档中的基本示例编写了一个简单的服务器/客户端示例:http: //docs.python.org/2/library/socket.html#example

这是server.py:

# expensive imports
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import scipy.ndimage
import scipy.signal
import sys
import os

# Echo server program
import socket

HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
while 1:
    conn, addr = s.accept()
    print 'Connected by', addr
    data = conn.recv(1024)
    if not data: break
    conn.sendall("PLOTTING:" + data)
    # update plot
    conn.close()

和client.py:

# Echo client program
import socket
import sys

HOST = ''    # The remote host
PORT = 50007              # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(sys.argv[1])
data = s.recv(1024)
s.close()
print 'Received', repr(data)

你只需运行服务器:

python server.py

哪个进行导入,然后客户端只需通过套接字发送新文件的文件名以进行绘图:

python client.py mytextfile.txt

然后服务器更新情节。

在我的机器上运行你的导入需要 0.6 秒,而运行client.py0.03 秒。

于 2013-05-08T00:45:13.833 回答
8

您可以改为手动导入模块,使用imp. 请参阅此处的文档

例如,import numpy as np可以写成

import imp
np = imp.load_module("numpy",None,"/usr/lib/python2.7/dist-packages/numpy",('','',5))

这将使 python 无需浏览整个文件sys.path以查找所需的包。

也可以看看:

手动导入 gtk 失败:找不到模块

于 2016-03-20T22:33:07.323 回答
2

1.35 秒并不长,但我想如果你习惯于“快速检查”将其减半,那么也许看起来确实如此。

Andrea 建议使用简单的客户端/服务器设置,但在我看来,您可以轻松地调用脚本的非常轻微的修改,并在工作时保持其控制台窗口打开:

  • 调用脚本,该脚本执行导入然后等待输入
  • 最小化控制台窗口,切换到你的工作,无论如何:*做工作*
  • 再次选择控制台
  • 为脚本提供某种输入
  • 无需导入开销即可接收结果
  • 在脚本愉快地等待输入时再次切换离开脚本

我假设您的脚本每次都是相同的,即您不需要每次都给它图像堆栈位置或任何特定命令(但这些也很容易做到!)。

RAAC's_Script.py 示例:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import scipy.ndimage
import scipy.signal
import sys
import os

print('********* RAAC\'s Script Now Running *********')

while True: # Loops forever
    # Display a message and wait for user to enter text followed by enter key.
    # In this case, we're not expecting any text at all and if there is any it's ignored
    input('Press Enter to test image stack...')

    '''
    *
    *
    **RAAC's Code Goes Here** (Make sure it's indented/inside the while loop!)
    *
    *
    '''

要结束脚本,请关闭控制台窗口或按 ctrl+c。

我已经把它做得尽可能简单,但它只需要很少的额外处理就可以很好地处理一些事情,比如很好地退出,根据输入做一些稍微不同的事情等等。

于 2014-08-28T10:51:07.710 回答
1

您可以使用惰性导入,但这取决于您的用例。

如果它是一个应用程序,您可以运行 GUI 所需的模块,然后在加载窗口后,您可以导入所有模块。

如果它是一个模块并且用户不使用所有依赖项,您可以导入内部函数。

[警告] 我认为它是针对 pep8 的,并且在某些地方不推荐使用,但这背后的所有原因主要是可读性(尽管我可能是错的......)和一些构建器(例如 pyinstaller)捆绑(可以通过添加来解决缺少对规范的依赖项参数)

如果您使用延迟导入,请使用注释,以便用户知道存在额外的依赖项。

例子:

import numpy as np

# Lazy imports
# import matplotlib.pyplot as plt

def plot():
    import matplotlib.pyplot as plt
    
    # Your function here
    # This will be imported during runtime 

对于某些特定的库,我认为这是必要的。

你也可以创建一些让我们称之为 api in__init__.py

例如在 scikit learn 上。如果您导入 sklearn 然后调用某个模型,则找不到它并引发错误。然后您需要更具体并直接导入子模块。尽管对用户来说可能不方便,但这是一种很好的做法,可以显着减少导入时间。

通常 10% 的导入库花费 90% 的导入时间。非常简单的分析工具是 line_profiler

import line_profiler
import atexit

profile = line_profiler.LineProfiler()
atexit.register(profile.print_stats)

@profile
def profiled_function():

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt


profiled_function()

这给出了结果

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    20                                               @profile
    21                                               def profiled_function():
    22
    23         1    2351852.0 2351852.0      6.5          import numpy as np
    24         1    6545679.0 6545679.0     18.0          import pandas as pd
    25         1   27485437.0 27485437.0     75.5          import matplotlib.pyplot as plt

三个库导入时间的 75% 是 matplotlib(这并不意味着它写得不好,它只是需要很多东西来进行图形输出)

笔记:

如果您在一个模块中导入库,则其他导入无需任何费用,它是全球共享的......

另一个注意事项:

如果直接从 python 使用导入(例如pathlibsubprocess等)不使用延迟加载,python 模块导入时间接近于零,不需要根据我的经验进行优化......

于 2021-12-27T14:14:49.283 回答