„moduł importu” vs. „z funkcji importu modułu”

143

Zawsze korzystałem z tej metody:

from sys import argv

i używaj argvz argv . Ale istnieje konwencja korzystania z tego:

import sys

i używając argv przez sys.argv

Druga metoda sprawia, że ​​kod sam się dokumentuje i ja (naprawdę) go przestrzegam. Ale wolę pierwszą metodę, ponieważ jest szybka, ponieważ importujemy tylko potrzebną funkcję, a nie cały moduł (który zawiera więcej bezużytecznych funkcji, które Python zmarnuje czas na ich importowanie). Zauważ, że potrzebuję tylko argv, a wszystkie inne funkcje sys są dla mnie bezużyteczne.

Więc moje pytania są. Czy pierwsza metoda naprawdę sprawia, że ​​skrypt jest szybki? Która metoda jest najbardziej preferowana? Dlaczego?

Santosh Kumar
źródło
2
Zobacz także stackoverflow.com/q/710551/321973
Tobias Kienzler

Odpowiedzi:

175

Importowanie modułu niczego nie marnuje ; moduł jest zawsze w pełni importowany (do sys.modulesmapowania), więc czy używasz go, import sysczy from sys import argvnie ma żadnych szans.

Jedyną różnicą między tymi dwoma instrukcjami jest to, jaka nazwa jest związana; import syswiąże nazwę sysz modułem (so sys-> sys.modules['sys']), a from sys import argvwiąże inną nazwę argv, wskazując prosto na atrybut zawarty w module (so argv-> sys.modules['sys'].argv). Reszta sysmodułu nadal tam jest, niezależnie od tego, czy używasz czegokolwiek innego z modułu, czy nie.

Nie ma również różnicy w wydajności między tymi dwoma podejściami. Tak, sys.argvmusi szukać dwóch rzeczy; musi wyszukać sysw twojej globalnej przestrzeni nazw (znajduje moduł), a następnie wyszukać atrybut argv. I tak, używając from sys import argvmożesz pominąć wyszukiwanie atrybutów, ponieważ masz już bezpośrednie odwołanie do atrybutu. Ale importoświadczenie wciąż musi wykonać tę pracę, wyszukuje ten sam atrybut podczas importowania i będziesz musiał użyć argv tylko raz . Gdybyś musiał użyć argvtysiące razy w pętli, mogłoby to mieć znaczenie, ale w tym konkretnym przypadku tak naprawdę nie jest.

Wybór między jednym a drugim powinien zatem opierać się na stylu kodowania .

W dużym module z pewnością skorzystałbym import sys; dokumentacja kodu ma znaczenie, a użycie sys.argvgdzieś w dużym module sprawia, że ​​jest o wiele bardziej zrozumiałe, o czym mówisz, niż argvkiedykolwiek.

Jeśli jedynym miejscem, z którego korzystasz, argvjest '__main__'blok do wywołania main()funkcji, z pewnością użyj tej opcji, from sys import argvjeśli czujesz się szczęśliwszy z tego powodu:

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

Nadal bym import systam używał . Wszystkie rzeczy są równe (i są one dokładnie pod względem wydajności i liczby znaków użytych do napisania tego), dla mnie jest to po prostu łatwiejsze.

Jeśli importujesz coś zupełnie innego , być może w grę wchodzi wydajność. Ale tylko jeśli wielokrotnie użyjesz określonej nazwy w module , na przykład w krytycznej pętli. Ale tworzenie nazwy lokalnej (w ramach funkcji) będzie jeszcze szybsze:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)
Martijn Pieters
źródło
1
Istnieje również sytuacja, w której masz pakiet z podpakietami lub modułami, który udostępnia atrybut jednego z tych podpakietów / modułów w pakiecie najwyższego poziomu. Użycie from...importpozwala package.attributeraczej zrobić , niż package.subpackage_or_module.attribute, co może być przydatne, jeśli masz logiczne lub koncepcyjne grupy w pakiecie, ale chcesz sprawić, by było trochę wygodniej dla użytkowników twojego pakietu. ( numpyjak sądzę robi coś takiego)
JAB
W Django masz mnóstwo miejsc, w których rzeczy takie from django.core.management.base import BaseCommandsą lepsze, a wszystko inne (szczególnie import django) prowadzi do nieczytelnego kodu. Chociaż podoba mi się ta odpowiedź, myślę, że istnieją pewne biblioteki (a zwłaszcza niektóre frameworki), w których konwencja ma naruszać zwykły import. Jak zawsze, wykorzystaj swoją ocenę tego, co jest najlepsze w danej sytuacji. Ale błąd po stronie jawnej (innymi słowy zgadzam się w przeważającej części).
neuronet
1
@JAB: można nadal używać import ... as, aby znaleźć pakiet do innej nazwy: import package.subpackage_or_module as shortname. from parent import subrobi zasadniczo to samo.
Martijn Pieters
43

Istnieją dwa powody przemawiające za używaniem import modulezamiast from module import function.

Pierwsza to przestrzeń nazw. Importowanie funkcji do globalnej przestrzeni nazw grozi kolizjami nazw.

Po drugie, nie jest to istotne dla standardowych modułów, ale jest istotne dla własnych modułów, szczególnie podczas programowania. Jest to opcja dla reload()modułu. Rozważ to:

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

Z drugiej strony

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

Co do prędkości ...

importujemy tylko potrzebną funkcję, a nie importujemy cały moduł (który zawiera więcej bezużytecznych funkcji, które Python zmarnuje czas na ich importowanie)

Niezależnie od tego, czy importujesz moduł, czy importujesz funkcję z modułu, Python przeanalizuje cały moduł. Tak czy inaczej moduł zostanie zaimportowany. „Importowanie funkcji” to nic innego jak powiązanie funkcji z nazwą. W rzeczywistości import modulejest mniej pracy dla tłumacza niż from module import func.

vartec
źródło
6
reload () był wbudowany w Python 2; tak już nie jest w przypadku Python 3.
André
Myślałem, że są też implikacje związane z zależnością od importu cyklicznego?
ADP
18

Używam from imports, gdy poprawia to czytelność. Na przykład wolę (średniki mają tutaj tylko zaoszczędzić miejsce):

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

zamiast:

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

Ten ostatni jest dla mnie trudniejszy do odczytania (i napisania), ponieważ zawiera tyle zbędnych informacji. Warto też wiedzieć z góry, jakich części modułu używam.

Wolę zwykłe imports, jeśli używam wielu krótkich nazw z modułu:

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

Lub jeśli nazwa jest tak ogólna, że ​​nie ma sensu poza obszarem nazw:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

źródło
To moja ulubiona odpowiedź. „Jawne jest lepsze niż niejawne” czasami koliduje z czytelnością, prostotą i OSUSZANIEM. Zwłaszcza przy użyciu frameworka takiego jak Django.
neuronet
18

Moim zdaniem regularne importpoprawia czytelność. Podczas przeglądania kodu Python lubię widzieć, skąd dana funkcja lub klasa pochodzi właśnie tam, gdzie jest używana. Oszczędza mi to przewijania na górę modułu, aby uzyskać te informacje.

Jeśli chodzi o długie nazwy modułów, po prostu używam assłowa kluczowego i daję im krótkie aliasy:

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

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

Jako wyjątek zawsze używam from module import somethingnotacji, kiedy mam do czynienia z __future__modułem. Po prostu nie możesz tego zrobić w inny sposób, jeśli chcesz, aby wszystkie ciągi były domyślnie Unicode w Pythonie 2, np

from __future__ import unicode_literals
from __future__ import print_function
golem
źródło
Amen! „import as” to zwycięska kombinacja :-)
paj28
4

Mimo, import sysi from sys import agrvzarówno importować cały sysmoduł, korzysta on wiążący tak nazwać tylko argvmoduł dostępny jest do reszty kodu.

Dla niektórych osób byłby to preferowany styl, ponieważ udostępnia tylko funkcję, którą wyraźnie podałeś.

Wprowadza jednak potencjalne konflikty nazw. Co jeśli masz inny moduł o nazwie argv? Zauważ, że możesz również jawnie zaimportować funkcję i zmienić jej nazwę from sys import argv as sys_argv, zgodnie z konwencją, która spełnia jawny import i rzadziej powoduje kolizje przestrzeni nazw.

Duncan
źródło
2
Więc jak jest if sys_argv:coś lepszego niż if sys.argv:? Wiem, co oznacza drugie zdanie, nie mam pojęcia, co oznacza pierwsza forma bez cofania się do dziwnego importu.
msw
1

Niedawno zadałem sobie to pytanie. Zmierzyłem czas różnych metod.

biblioteka żądań

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

biblioteka beautifulsoup

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

biblioteka 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

biblioteka 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

Wydaje mi się, że istnieje niewielka różnica w wydajności.

tmthyjames
źródło
Dodajesz w wyszukiwaniu atrybutów. Porównać import modulez from module import nameprawidłowo dodać , że nazwa odnośnika do import modulesprawy. Np. Dodaj linię sys.argvdo artestu itp. Nadal będzie różnica, ponieważ wykonywana praca jest nieco inna, ponieważ generowany jest inny kod bajtowy i wykonywane są różne ścieżki kodowe.
Martijn Pieters
2
Zauważ, że bezpośrednio rozwiązuję tę różnicę w mojej odpowiedzi; będzie różnica między użyciem import sysa użyciem sys.argvtysięcy razy w pętli w porównaniu z from sys import argvużyciem tylko argv. Ale ty nie. W przypadku rzeczy, które robisz tylko raz na poziomie globalnym modułu, naprawdę powinieneś zoptymalizować czytelność, a nie mikroskopijne różnice w taktowaniu.
Martijn Pieters
1
Ahhh! I myślałem, że mam coś do roboty! :) Przejrzałem tylko twoją odpowiedź. Wygląda na to, że wskoczyłem na ten pistolet. Dobrze jest być upokorzonym.
tmthyjames
-1

Przeglądanie opublikowanych fragmentów kodu, importowanie całych modułów i odwoływanie się do nich module.functionjest prawie standardem, przynajmniej dla standardowych modułów. Wydaje się, że jedynym wyjątkiem jestdatetime

from datetime import datetime, timedelta

więc możesz powiedzieć datetime.now()raczej niż datetime.datetime.now().

Jeśli martwisz się wydajnością, zawsze możesz powiedzieć (na przykład)

argv = sys.argv

a następnie zrób kod krytyczny dla wydajności, ponieważ wyszukiwanie modułu jest już zakończone. Jednak chociaż będzie to działać z funkcjami / metodami, większość IDE będzie się mylić i nie będzie (na przykład) wyświetlać linku / podpisu źródłowego dla funkcji, gdy jest ona przypisana do zmiennej.

Austin
źródło
-2

Chcę tylko dodać, że jeśli robisz coś takiego

from math import sin

(lub dowolną inną wbudowaną bibliotekę, taką jak syslub posix), sinzostaną one dołączone do dokumentacji modułu (tj. kiedy to zrobisz >>> help(mymodule)lub $ pydoc3 mymodule. Aby tego uniknąć, importuj za pomocą:

import math
from math import sin as _sin

PS: biblioteka wbudowana to taka, która jest skompilowana z kodu C i dołączona do Pythona. argparse, osA ionie są wbudowane w pakietach

Epickie mrugnięcie
źródło