在函数内部导入一个python模块和/或函数的优点和缺点是什么,关于速度和内存的效率?
它是在每次运行函数时重新导入,还是在开始时只导入一次,不管函数是否运行?
- 没有速度优势(调用import非常昂贵,即使模块已经加载)。如果您希望获得速度优势,那么将模块分配给局部变量(如果您至少访问该模块4-5次)会更快(因为局部变量查找速度非常快),这是您在函数中首先要做的事情,然后通过该局部变量访问它。
- (如果您想要一个有趣的例子来说明在函数中导入有多糟糕,那么可以在us.pycon.org/2010/conference/schedule/event/71的视频中快进到26:30)
- @尼克:听这个的时候,似乎重复导入很慢,因为每次你都在尝试检查它是否被导入。您是说在函数外部导入它并将其设置为全局变量,然后在函数内部获取全局变量吗?
- @蒂姆:加快模块访问速度的最佳方法(假设这正是您要做的,并且您访问的模块足以使本地分配有价值)是将模块像往常一样在文件级别分配给import,然后在函数内部将模块分配给本地变量。为了使分配值得,您可能需要在函数内部访问模块至少4次-如果您使用模块的频率低于此频率,则在全局级别执行直接的module.symbol查找不会比本地分配/查找慢。
- @尼克:这和优化避免使用周期有什么不同吗?如果在n大的地方调用函数utility.foo()n time,那么最好先执行我的foo=utility.foo(),然后再执行我的foo()n次?实用程序可以是一个模块、一个类或任何您想要的。
- @蒂姆:这个概念是一样的,尽管在封面下发生的事情在模块之间稍有不同,比如说InstanceMethod。不过,作为一般实践,您确实应该对这些事情执行neither—您应该在模块级别导入,并在它们当前所在的命名空间中使用方法/函数,并且只有在极端性能情况下才应该考虑微优化,如重新分配到本地。
- @Nickbastin 5年半后将模块分配给局部变量是否仍然是一种优化?
- @2RS2TS:是的-局部变量是最快的访问速度。我仍然不建议这样做,除非你有一个循环,它有大量的迭代需要花费大量的时间。
- @你的皮孔连接断了。
Does it re-import every time the function is run?
不;或者更确切地说,每次导入python模块时,它们基本上都是缓存的,因此导入第二次(或第三次或第四次…)实际上并不强制它们再次执行整个导入过程。一
Does it import once at the beginning whether or not the function is run?
不,只有在执行函数时才导入。2, 3
至于好处:我想要视情况而定。如果您可能只运行很少的函数,并且不需要将模块导入到任何其他地方,那么只将它导入到该函数中可能是有益的。或者,如果存在名称冲突或其他不希望模块中的模块或符号在任何地方都可用的原因,则可能只希望将其导入到特定函数中。(当然,对于这些情况,总是有from my_module import my_function as f。)
在一般的实践中,这可能没有那么有益。事实上,大多数Python风格的指南都鼓励程序员将所有导入放置在模块文件的开头。
- 在相同的思路中,如果导入被塞进辅助函数中,那么可以使depedency成为可选的。
- 当我为自己编写库模块时,我使用它作为可选的依赖项。我使库模块内的每个函数依赖于最少数量的导入。
- 谢谢!它通过将慢模块'import plotly'放入调用它的函数中,节省了我的web2py应用程序大量的加载时间。
第一次从任何地方(函数内部或外部)加载import goo时,加载goo.py或其他可导入形式,并将sys.modules['goo']设置为这样构建的模块对象。在同一个程序运行中的任何未来导入(再次,无论是在函数内部还是外部)只需查找sys.modules['goo'],并在适当的范围内将其绑定到barename goo。dict查找和名称绑定是非常快速的操作。
假设第一个import在程序运行期间完全摊销,那么"适当范围"是模块级意味着每次使用goo.this、goo.that等都是两个dict查找——一个用于goo,一个用于属性名。如果它是"函数级",则每次运行函数都要额外支付一个局部变量设置(甚至比字典查找部分更快!)但是为每个goo.this访问节省了一个dict查找(将其替换为局部变量查找,速度极快),基本上将这种查找所花费的时间减半。
我们谈论的是以某种方式的几纳秒,所以这几乎不是一个有价值的优化。在一个函数中使用import的一个潜在的实质性优势是,在给定的程序运行中可能根本不需要该函数,例如,该函数通常处理错误、异常和罕见情况;如果是这种情况,任何不需要该功能的运行都不会执行T(这是一个微秒的节省,而不仅仅是纳秒),只运行那些确实需要该功能的,将支付(适度但可测量的)价格。
这仍然是一个只有在非常极端的情况下才有价值的优化,在尝试以这种方式挤出微秒之前,还有许多其他的我会考虑的。
- 这种优化实际上是不值得的——即使模块已经加载,快速的局部变量访问也无法弥补调用import的惊人开销。检查模块是否已加载是一个非常昂贵的操作(相对于一些全局字典查找)。
- 第一次导入模块很昂贵。尝试运行一个空脚本与一个只包含import string,itertools,fractions,heapq,re,array,bisect,collections,math,os的脚本。第一个平均需要180毫秒,第二个平均需要230毫秒,所以对于初学者来说这不是微秒。这是几十毫秒(可能会发生光盘访问?)。这对于运行多次的小脚本(比如服务Web请求)非常重要。
- @在这种情况下,通常有一个服务器一直在运行,因此它不会反复导入。
- @Tobiaskienzler仍然适用于FAAS(功能即服务)环境和/或一些低性能(如嵌入式)设备。其理由是,延迟的差异是显著的,不能简单地忽略。
它在函数第一次执行时导入一次。
赞成的意见:
欺骗:
- 如果您执行类似于grep import /path/to/module的操作,它将向您显示它导入的所有模块。
在函数内部导入将有效地导入模块一次。第一次运行函数时。
不管您是在顶部导入,还是在函数运行时导入,它都应该以同样快的速度导入。这通常不是导入def的好理由。赞成的意见?如果不调用函数,则不会导入它。如果您的模块只要求用户在使用您的特定功能时安装某个模块,那么这实际上是一个合理的原因…
如果这不是你这么做的原因,那几乎肯定是个糟糕的主意。
我可以建议大家不要问"X能提高我的表现吗?"您可以使用分析来确定您的程序实际花费的时间在哪里,然后根据您将从中获得最大好处的地方应用优化?
然后,您可以使用概要分析来确保您的优化实际上也使您受益。
- 我同意,这更像是一个好奇的问题。我想知道python的import方法如何更为详细地工作,而不是试图过早地进行性能增强。不过,谢谢:)
- 啊。好吧,我希望这里的优秀答案能满足你的好奇心!effbot有一些可能对您有用的信息:effbot.org/zone/import-confusion.htm向下滚动到"Python如何导入模块?"
- 谢谢你提供的信息,答案非常好,帮助很大。
当第一次调用函数时,它导入一次。
我可以想象这样做,如果我在一个导入的模块中有一个函数,这个模块非常独立地使用,并且是唯一需要导入的模块。不过看起来有点牵强…