Google App Engine:在处理程序范围内导入模块与全局范围

时间:2017-10-12 15:45:40

标签: python google-app-engine

我的项目包含大量进口,这些进口位于“全球”范围内。

from google.appengine.ext import ndb
from handlers import SomeHandler
import logging
import datetime  # will only use this module ONCE

我想在特定处理程序中使用{strong>一次的datetime模块。我应该在处理程序中导入日期时间还是应该将其保留在全局范围内?

import datetime  # Should we import here?

class BookHandler(webapp2.RequestHandler):
    import datetime   # Or, should we import here?
    def get(self):
        today = datetime.datetime.now() 

似乎本地导入显示更清晰的依赖关系。是否存在性能问题或其他缺点需要考虑?

3 个答案:

答案 0 :(得分:1)

你应该在文件的开头挑出模块,将模块绑定到该文件的范围。我认为你正在做的事情被称为“延迟加载”模块,如果未正确安装或导入模块,它可能会在运行时导致错误。

顺便说一句,python的工作方式是每次导入模块时,解释器会查看模块是否已导入。如果已导入,则会设置对它的引用。换句话说,它不会创建它的新实例。

我建议为您的处理程序类创建一个文件,并在该文件的开头导入日期时间和其他任何内容。

答案 1 :(得分:1)

在处理程序内部导入没有问题(如果您愿意,甚至可以在get()函数内部导入) - 我正在大量使用它。

延迟加载的优点:

  • 减少了应用加载时间
  • 如果在初始时加载所有模块,应用程序的平均内存占用量可能远远低于所需的总内存占用量,即使是那些很少使用的模块 - 成本更低

延迟加载的缺点:

  • 非确定性应用加载时间
  • 由于确切的条件未知,潜在的难以重现的错误会在延迟模块加载时受到影响

相关(仅在延迟加载意义上):App Engine: Few big scripts or many small ones?

答案 2 :(得分:1)

隐藏这样的进口是一种优化;无论何时考虑是否优化,都要验证所提议的优化是否真的有效。

让我们首先考虑datetime的具体示例。这是一个简单的应用程序:

import sys

import webapp2


class Handler(webapp2.RequestHandler):

    def get(self):
        if 'datetime' in sys.modules:
            self.response.write('The datetime module is already loaded.\n')
        else:
            self.response.write('The datetime module is not already loaded\n.')
        self.response.write('%d modules have been loaded\n' % len(sys.modules))
        count = sum(1 for x in sys.modules.values() if '/lib64' in repr(x))
        self.response.write('%d standard library modules have been loaded\n' % count)
        gcount = sum(1 for x in sys.modules.values() if 'appengine' in repr(x))
        self.response.write('%d appengine modules have been loaded\n' % gcount)

application = webapp2.WSGIApplication([('/', Handler)])

如果我们访问'/'网址,我们会看到此输出:

  

日期时间模块已加载。

     

已加载706个模块

     

已加载95个标准库模块

     

已加载207个appengine模块

即使在这个最小的应用中,SDK {sup> * 也已导入datetime。 Python导入模块后,进一步导入只需要一次字典查找,因此隐藏导入没有任何好处。鉴于SDK已经导入了95个标准库模块和207个SDK模块,因此隐藏了常用标准库或SDK模块的导入不太可能有很多好处。

这就留下了导入应用程序代码的问题。处理程序可以通过在路由中将它们声明为字符串来延迟加载,以便在访问路径之前不会导入它们:

app = webapp2.Application([('/foo', 'handlers.FooHandler')])

如果您发现有必要,此技术允许优化启动时间而不隐藏类或方法中的导入。

其他答案指出,延迟加载的成本可能是意外的运行时错误。此外,如果您选择隐藏导入,它也会降低代码可读性,可能导致结构问题,例如屏蔽循环依赖关系,并为经验不足的开发人员设置一个糟糕的示例,他们可能认为隐藏导入是惯用的而不是优化。 / p>

因此,在考虑以这种方式进行优化时:

  • 验证是否有必要进行优化:并非所有应用程序都需要绝对最高性能
  • 验证正确的问题正在得到解决; App Engine上的RPC调用往往会主导响应时间
  • 配置文件以验证优化是否有效
  • 考虑代码可维护性的成本

* 我希望,依赖于SDK的sys.modules类似于云运行时,是一个合理的假设。