'módulo de importação' vs. 'da função de importação de módulo'

143

Eu sempre uso esse método:

from sys import argv

e use argvcom apenas argv . Mas há uma convenção de usar isso:

import sys

e usando o argumento sys.argv

O segundo método torna o código auto-documentado e eu (realmente) adiro a ele. Mas a razão pela qual prefiro o primeiro método é que é rápido porque estamos importando apenas a função necessária em vez de importar o módulo inteiro (que contém mais funções inúteis que o python perderá tempo importando). Note que eu preciso apenas do argv e todas as outras funções do sys são inúteis para mim.

Então, minhas perguntas são. O primeiro método realmente torna o script rápido? Qual método é o preferido? Por quê?

Santosh Kumar
fonte

Respostas:

175

A importação do módulo não desperdiça nada ; o módulo é sempre totalmente importado (para o sys.modulesmapeamento), portanto, se você usa import sysou from sys import argvnão faz nenhuma diferença.

A única diferença entre as duas instruções é qual nome é vinculado; import sysvincula o nome sysao módulo (então sys-> sys.modules['sys']), enquanto from sys import argvvincula um nome diferente argv, apontando diretamente para o atributo contido dentro do módulo (então argv-> sys.modules['sys'].argv). O restante do sysmódulo ainda está lá, se você usa mais alguma coisa do módulo ou não.

Também não há diferença de desempenho entre as duas abordagens. Sim, sys.argvtem que procurar duas coisas; ele tem que olhar-se sysno espaço de nomes global (encontra o módulo), em seguida, procurar o atributo argv. E sim, usando from sys import argvvocê pode pular a pesquisa de atributo, pois você já tem uma referência direta ao atributo. Mas a importinstrução ainda precisa fazer esse trabalho, ela procura o mesmo atributo ao importar e você só precisará usar argv uma vez . Se você tivesse que usar argvmilhares de vezes em um loop, talvez pudesse fazer a diferença, mas nesse caso específico, na verdade não.

A escolha entre um ou outro então deve basear-se no estilo de codificação .

Em um módulo grande , eu certamente usaria import sys; a documentação do código é importante e o uso sys.argvem algum lugar de um módulo grande torna muito mais claro o que você está se referindo do que argvjamais faria.

Se o único lugar que você usa argvé em um '__main__'bloco para chamar uma main()função, use-o from sys import argvse você se sentir mais feliz com isso:

if __name__ == '__main__':
    from sys import argv
    main(argv)

Eu ainda usaria import syslá. Todas as coisas são iguais (e são, exatamente, em termos de desempenho e número de caracteres usados ​​para escrevê-lo), isso é apenas mais fácil para mim.

Se você estiver importando outra coisa , talvez o desempenho entre em jogo. Mas somente se você usar um nome específico em um módulo várias vezes , em um loop crítico, por exemplo. Mas a criação de um nome local (dentro de uma função) será ainda mais rápida:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)
Martijn Pieters
fonte
1
Também existe a situação em que você possui um pacote com subpacotes ou módulos que expõe um atributo de um desses subpacotes / módulos no pacote de nível superior. Usando from...importpermite que você faça package.attributemais do que package.subpackage_or_module.attribute, o que pode ser útil se você tem agrupamentos lógicos ou conceituais dentro do pacote, mas quer fazer as coisas um pouco mais conveniente para os usuários de seu pacote. ( numpyFaz algo assim, eu acredito.)
JAB
No Django você tem toneladas de pontos onde as coisas como from django.core.management.base import BaseCommandsão melhores, e qualquer outra coisa (especialmente import django) levaria a um código ilegível. Então, enquanto eu gosto dessa resposta, acho que existem algumas bibliotecas (e especialmente algumas estruturas) nas quais a convenção é violar a importação simples. Como sempre, use seu julgamento sobre o que é melhor em uma determinada situação. Mas erre do lado explícito (em outras palavras, concordo na maior parte).
Neuronet
1
@JAB: você ainda pode usar import ... aspara encontrar o pacote para um nome diferente: import package.subpackage_or_module as shortname. from parent import subfaz, essencialmente, a mesma coisa.
Martijn Pieters
43

Há duas razões a favor do uso, em import modulevez de from module import function.

Primeiro é o espaço para nome. Importar uma função para o espaço para nome global corre o risco de colisões de nomes.

Segundo, isso não é relevante para os módulos padrão, mas é significativo para os seus próprios módulos, especialmente durante o desenvolvimento. É a opção para reload()um módulo. Considere isto:

from module import func
...
reload(module)
# func still points to the old code

Por outro lado

import module
...
reload(module)
# module.func points to the new code

Quanto à velocidade ...

estamos importando apenas a função necessária em vez de importar o módulo inteiro (que contém mais funções inúteis que o python perderá tempo importando)

Se você importa um módulo ou importa uma função de um módulo, o Python analisa o módulo inteiro. De qualquer maneira, o módulo é importado. "Importar uma função" nada mais é do que vincular a função a um nome. De fato, import moduleé menos trabalho para intérprete do que from module import func.

vartec
fonte
6
reload () foi incorporado no Python 2; que já não é o caso para Python 3.
André
Eu pensei que também havia implicações relacionadas às dependências de importação circular?
ADP
18

Uso from imports sempre que melhora a legibilidade. Por exemplo, eu prefiro (ponto e vírgula é apenas para economizar espaço aqui):

from collections import defaultdict
from foomodule import FooBar, FooBaz
from twisted.internet.protocol import Factory
defaultdict(); FooBar(); FooBaz(); Factory()

ao invés de:

import collections
import foomodule
import twisted.internet.protocol
collections.defaultdict(); foomodule.FooBar(); foomodule.FooBaz()
twisted.internet.protocol.Factory()

O último é mais difícil de ler (e escrever) para mim porque contém muitas informações redundantes. Além disso, é útil saber com antecedência quais partes de um módulo estou usando.

Prefiro imports regulares se estiver usando muitos nomes abreviados de um módulo:

import sys
sys.argv; sys.stderr; sys.exit()

Ou, se um nome é tão genérico que não faz sentido fora do seu espaço para nome:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

fonte
Esta é a minha resposta favorita. 'Explícito é melhor que implícito' às vezes entra em conflito com legibilidade, simplicidade e DRY. Especialmente ao usar um framework como o Django.
Neuronet
18

Na minha opinião, o uso regular importmelhora a legibilidade. Ao revisar o código Python, gosto de ver de onde vem a determinada função ou classe, exatamente onde é usada. Isso me impede de rolar para a parte superior do módulo para obter essas informações.

Quanto aos nomes longos dos módulos, apenas uso a aspalavra - chave e dou a eles apelidos curtos:

import collections as col
import foomodule as foo
import twisted.internet.protocol as twip

my_dict = col.defaultdict()
foo.FooBar()
twip_fac = twip.Factory()

Como exceção, eu sempre uso a from module import somethingnotação ao lidar com o __future__módulo. Você não pode fazer isso de outra maneira quando deseja que todas as strings sejam unicode por padrão no Python 2, por exemplo

from __future__ import unicode_literals
from __future__ import print_function
golem
fonte
Amém! "import as" é uma combinação vencedora :-)
paj28
4

Embora import syse from sys import agrvtanto importar todo o sysmódulo, este último usa o nome de ligação tão somente o argvmódulo é acessível para resto do código.

Para algumas pessoas, esse seria o estilo preferido, pois apenas torna acessível a função que você declarou explicitamente.

No entanto, apresenta possíveis conflitos de nome. E se você tivesse outro módulo chamado argv? Observe que você também pode importar explicitamente a função e renomear com from sys import argv as sys_argv, uma convenção que atende à importação explícita e é menos provável que tenha causado colisões no espaço de nome.

Duncan
fonte
2
Então, como é if sys_argv:melhor do que if sys.argv:? Eu sei o que a segunda declaração significa, não tenho idéia do que a primeira forma significa sem voltar para a importação bizarra.
msw
1

Recentemente, fiz essa pergunta para mim mesmo. Eu cronometrei os diferentes métodos.

biblioteca de solicitações

def r():
    import requests
    return 'hello'
timeit r() # output: 1000000 loops, best of 3: 1.55 µs per loop

def rg():
    from requests import get
    return 'hello'
timeit rg() # output: 100000 loops, best of 3: 2.53 µs per loop

biblioteca bonita

def bs():
    import bs4
    return 'hello' 
timeit bs() # output: 1000000 loops, best of 3: 1.53 µs per loop

def be():
    from bs4 import BeautifulSoup
    return 'hello'
timeit be() # output: 100000 loops, best of 3: 2.59 µs per loop

biblioteca json

def js():
    import json
    return 'hello'
timeit js() # output: 1000000 loops, best of 3: 1.53 µs per loop

def jl():
    from json import loads
    return 'hello'
timeit jl() # output: 100000 loops, best of 3: 2.56 µs per loop

biblioteca sys

def s():
    import sys
    return 'hello'
timeit s() # output: 1000000 loops, best of 3: 1.55 µs per loop

def ar():
    from sys import argv
    return 'hello'
timeit ar() # output: 100000 loops, best of 3: 2.87 µs per loop

Parece-me que uma pequena diferença no desempenho.

tmthyjames
fonte
Você está adicionando uma pesquisa de atributo. Para comparar import modulecom from module import namecorretamente, adicione essa pesquisa de nome ao import modulecaso. Por exemplo, adicione a linha sys.argvao arteste, etc. Ainda haverá uma diferença, porque o trabalho realizado é um pouco diferente, pois diferentes bytes de código são gerados e diferentes caminhos de código são executados.
Martijn Pieters
2
Observe que eu dirijo essa diferença diretamente na minha resposta; haverá uma diferença entre usar import syse usar sys.argvmilhares de vezes em um loop from sys import argve usar apenas argv. Mas você não. Para coisas que você faz apenas uma vez no nível global do seu módulo, você realmente deve otimizar a legibilidade, não as diferenças microscópicas nos tempos.
Martijn Pieters
1
Ahhhh! E eu pensei em algo! :) Eu só dei uma olhada na sua resposta. Parece que eu pulei a arma naquele. É bom ser humilhado.
precisa saber é o seguinte
-1

Analisar fragmentos de código publicados, importar módulos inteiros e fazer referência a eles module.functioné praticamente o padrão, pelo menos para módulos padrão. A única exceção parece serdatetime

from datetime import datetime, timedelta

então você pode dizer em datetime.now()vez de datetime.datetime.now().

Se você está preocupado com o desempenho, sempre pode dizer (por exemplo)

argv = sys.argv

e faça seu código crítico de desempenho, já que a pesquisa do módulo já foi concluída. No entanto, embora isso funcione com funções / métodos, a maioria dos IDEs ficará confusa e não exibirá (por exemplo) um link / assinatura de origem para a função quando ela estiver atribuída a uma variável.

Austin
fonte
-2

Eu só quero acrescentar que se você fizer algo como

from math import sin

(ou qualquer outra biblioteca interna como sysou posix), sinserá incluída na documentação do seu módulo (por exemplo, quando você faz >>> help(mymodule)ou $ pydoc3 mymodule.) Para evitar isso, importe usando:

import math
from math import sin as _sin

PS: uma biblioteca interna é aquela que é compilada a partir do código C e incluída no Python. argparse, osE ionão são built-in pacotes

Wink épico
fonte