【问题标题】:django/python imports performancedjango/python 导入性能
【发布时间】:2010-09-23 22:22:36
【问题描述】:

有人可以证明为什么使用这样的解决方案是一种不好的做法:

在 django 视图中 98% 的情况下你需要使用

  from django.http import HttpResponseRedirect
  from django.core.urlresolvers import reverse
  from django.utils.translation import ugettext as _

无论如何,在我的项目中,我的每个视图都有这些导入,并且几乎在视图的每一秒函数中都会用到所有内容:

from datetime import datetime
from django.conf import settings
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core import paginator
from django.db import connection
from django.db.models import Q
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.utils.translation import ugettext as _

现在添加一些模型和表格,我有 50 行根本无法阅读的废话。

我想到的第一件事当然是创建更多视图,拆分一些操作等等等等。但仍然有大约 30 行导入扼杀了我在代码中的方向。

然后我决定将 95% 的时间在视图中使用的所有内容都放在目录 /project/app/imports/view.py 中。现在我只有 ONE 导入了所有常见的东西,但是我的同事攻击我,这种代码很难阅读,因为你看不到导入了什么,以及为什么见鬼,在你的 IDE 中再打开一个标签太难了..??? [尤其是 vim 用户,他们有 FRAMES,而他正在使用 vim]

我对模型做了同样的事情,我的模型有它自己的目录,因为里面有 50 多个,而且这些文件不小 - 每个大约 150 行.. 即使这些文件里面的模型也很少.. 所以我'我只是在做类似的事情:

from myapp.models.mymodel import *

有些地方我只是在做:from myapp.models import * [init.py of myapp/imports dir 发生在这里]

问题:

1) 好的,所以第一个问题是命名空间,这种模型导入可能真的很荒谬.. 但是视图和表单的决定只是懒惰地在 IDE 中打开另一个选项卡

2) 性能问题?我的同事真的对这个论点争论了很多,“每次导入都需要 256kb 的内存”?? (通过运行编译的 .pyc 文件?不,我不相信;)

问题实际上是由于导入导致的性能问题。

附言我真的是 python 的新手(才 3 个月),我愿意接受客观的论点来讨论这个解决方案的所有问题和缺点。

更新

有一次我问了关于如何将导入移动到独立文件的问题,所以没有人抱怨这个 =) 问题是here

【问题讨论】:

    标签: python django


    【解决方案1】:

    1) 不给导入的名称加上它来自的模块前缀只是懒惰。不愿意滚动导入代码只是懒惰。在另一个文件中包含大量的导入究竟如何使它更容易阅读?我会将其保留在实际使用它们的原始文件中。这提高了可读性,因为如果我需要知道某些内容的来源,那么我可以直接转到文件顶部并检查它(使用 emacs 标记环直接返回)。它还使维护列表变得更容易,因为我只需快速搜索即可查看使用(或未使用)的位置。

    2) 在我的机器上,导入一个模块大约需要 812 微秒。

    $ python -mtimeit -s'import os' 'reload(os)'
    1000 loops, best of 3: 808 usec per loop
    

    这当然会随着你的 PYTHONPATH 的位置而有很大的不同。如果性能如此紧张,您可以通过杂耍来挤出一些。 YMMV。

    我不确定您的同事从哪里获得 256kb。这将取决于所涉及的代码对象的大小。

    >>> import sys
    >>> sys.getsizeof(sys)
    24
    >>> sys.getsizeof(sys.modules['__main__'])
    24
    

    如您所见,在我的 32 位机器上,实际的模块对象只占用 24 个字节。不过我有一种感觉,这将取决于系统。

    >>> def sizeofmodule(mod):
    ...     return sum(sys.getsizeof(getattr(mod, o)) for o in dir(mod))
    ... 
    >>> sizeofmodule(itertools)
    8662
    >>> sizeofmodule(sys)
    10275
    >>> sizeofmodule(operator)
    5230
    

    【讨论】:

    • 这不是写作的懒惰,而是阅读的懒惰,这只会让代码变得一团糟。所以这无论如何都不是争论。谈到性能,他还说因为这个项目每天都会有大量的游客[这将非常庞大],这可能是一个问题
    • 顺便说一句,我说的是把所有导入都放到独立文件中 =)
    • @holms,我知道你在说什么。我只是从你同事的角度说你很懒惰。
    • 您没有提到“显式优于隐式”的角度。每次导入都非常快(800 微秒),导入的优势在于它们是明确的。
    【解决方案2】:

    请记住,您可以导入一组子包。所以

    from django.conf import settings
    from django.contrib.auth.models import User
    from django.core.urlresolvers import reverse
    from django.core import paginator
    from django.db import connection
    from django.db.models import Q
    from django.http import HttpResponseRedirect, Http404, HttpResponse
    from django.shortcuts import render_to_response, get_object_or_404
    from django.template import RequestContext
    from django.utils.translation import ugettext as _
    

    可以变成

    from django import conf, contrib, db, http, shortcuts, template, utils
    from django.core import urlresolvers, paginator
    

    这很简短,可以让你避免在任何地方写django,并且很明显urlresolvers.reverse 之类的东西来自哪里。这还有一个优点,就是不会将像 reverse 这样的通用名称映射到高度具体的功能,从而为您留下更易读的代码。

    【讨论】:

      【解决方案3】:

      需要考虑的几点:

      在几乎所有情况下,导入一个模块所花费的时间是完全不相关的:它只发生一次。您的 Django 视图模块并未针对每个请求进行导入和重新评估;它被加载一次,然后重复使用。如果您的模块不断地重新加载,那么就会出现灾难性的错误。

      每次导入不会占用 256kb 的内存。也许每个单独的文件,加载一次,是(虽然我也怀疑),但重复导入同一个文件并不是每次都占用 256kb;它只是创建一个参考。如果内存使用存在问题,只需对其进行分析——加载 10000 个内容并查看使用了多少内存。

      对于 Django 模块,您并不总是需要为每个模块创建一个目录;我从models/__init__.py(例如from Customer import Customer)导入每个模型类,所以我可以说from myapp.models import Profile, Customer, Book, ...


      Django 视图在顶部需要十几行导入的趋势确实是一个问题。它变成了样板,即每次启动新文件时复制和粘贴的代码。需要样板的源代码是一个主要缺陷。

      同时,我强烈建议不要使用某些人可能会推荐的内容:import django,然后使用完全限定的模块名称。结果是输入像django.core.urlresolvers.reverse 这样的东西。当您发现自己经常复制和粘贴函数名称时,因为它们太长了,那就是出了问题。

      对此没有单一、清晰、明显正确的解决方案。将一直使用的东西放在另一个模块中是一个有效的解决方案,但它存在一些合理的问题:很难看到正在导入什么,以及导入的结果在哪里使用。

      如果您导入“模块集合”模块本身--import djangohelpersimport djangohelpers as dh,您可能会发现更少的直觉反对意见。然后,您正在编写dh.paginator 之类的内容。它为名称提供了一个明确的范围,并且可以更轻松地查看它的使用位置以及特定函数名称的来源,而“import *”则会丢失这些名称。

      (不过,您可能确实希望将 Q_ 之类的内容作为裸名导入。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-04
        • 1970-01-01
        • 1970-01-01
        • 2015-03-14
        • 1970-01-01
        • 1970-01-01
        • 2015-12-30
        • 1970-01-01
        相关资源
        最近更新 更多