'โมดูลนำเข้า' กับ 'จากฟังก์ชันนำเข้าโมดูล'


143

ฉันใช้วิธีนี้มาตลอด:

from sys import argv

และใช้argvมีเพียงargv แต่มีแบบแผนของการใช้สิ่งนี้:

import sys

และใช้ argv โดย sys.argv

วิธีที่สองทำให้รหัสตัวเองเอกสารและฉัน(จริงๆ) เป็นไปตามมัน แต่เหตุผลที่ฉันชอบวิธีแรกคือรวดเร็วเพราะเรานำเข้าเฉพาะฟังก์ชั่นที่ต้องการแทนที่จะนำเข้าโมดูลทั้งหมด (ซึ่งมีฟังก์ชั่นที่ไร้ประโยชน์มากกว่าซึ่งไพ ธ อนจะเสียเวลาในการนำเข้า) โปรดทราบว่าฉันต้องการเพียง argv และฟังก์ชั่นอื่น ๆ จาก sys นั้นไร้ประโยชน์สำหรับฉัน

ดังนั้นคำถามของฉันคือ วิธีแรกทำให้สคริปต์เร็วขึ้นจริง ๆ หรือไม่ วิธีใดที่เป็นที่ต้องการมากที่สุด ทำไม?



คำตอบ:


175

การนำเข้าโมดูลไม่เสียอะไร ; โมดูลจะถูกนำเข้าอย่างเต็มที่เสมอ(เข้าไปในการsys.modulesทำแผนที่) ดังนั้นคุณจึงไม่จำเป็นต้องใช้import sysหรือfrom sys import argvไม่ทำสิ่งต่อไป

ข้อแตกต่างระหว่างสองคำสั่งคือชื่อที่ถูกผูกไว้ import sysผูกชื่อsysกับโมดูล (ดังนั้นsys-> sys.modules['sys']) ในขณะที่from sys import argvผูกชื่อที่แตกต่างกันargvชี้ตรงไปที่แอตทริบิวต์ที่มีอยู่ภายในของโมดูล (ดังนั้นargv-> sys.modules['sys'].argv) ส่วนที่เหลือของsysโมดูลยังคงอยู่ที่นั่นไม่ว่าคุณจะใช้สิ่งอื่นจากโมดูลหรือไม่

นอกจากนี้ยังไม่มีความแตกต่างด้านประสิทธิภาพระหว่างสองวิธี ใช่sys.argvต้องค้นหาสองสิ่ง; ก็มีการมองขึ้นsysใน namespace โลกของคุณ (พบโมดูล) argvแล้วมองขึ้นแอตทริบิวต์ และใช่โดยใช้from sys import argvคุณสามารถข้ามการค้นหาแอตทริบิวต์เนื่องจากคุณมีการอ้างอิงโดยตรงไปยังแอตทริบิวต์ แต่importคำสั่งยังคงมีการทำผลงานนั้นก็เงยหน้าขึ้นมองแอตทริบิวต์เดียวกันเมื่อนำเข้าและคุณเท่านั้นที่เคยจะต้องใช้ครั้งเดียวargv หากคุณต้องใช้argvหลายพันครั้งในการวนซ้ำมันอาจจะสร้างความแตกต่างได้ แต่ในกรณีนี้มันไม่ได้เกิดขึ้นจริง

ตัวเลือกระหว่างอย่างใดอย่างหนึ่งนั้นควรขึ้นอยู่กับรูปแบบการเข้ารหัสแทน

ในขนาดใหญ่โมดูลแน่นอนฉันต้องการใช้import sys; เอกสารประกอบการเขียนโค้ดมีความสำคัญและการใช้ที่sys.argvใดที่หนึ่งในโมดูลขนาดใหญ่ทำให้ชัดเจนยิ่งกว่าสิ่งที่คุณอ้างถึงมากกว่าที่argvเคยเป็นมา

หากสถานที่เดียวที่คุณใช้argvอยู่ใน'__main__'บล็อกเพื่อเรียกใช้main()ฟังก์ชันโดยทั้งหมดใช้from sys import argvหากคุณรู้สึกมีความสุขมากขึ้นเกี่ยวกับสิ่งนั้น:

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

ฉันยังคงใช้ที่import sysนั่นเอง ทุกสิ่งเท่าเทียมกัน (และแน่นอนในแง่ของประสิทธิภาพและจำนวนตัวอักษรที่ใช้ในการเขียน) นั่นเป็นเรื่องง่ายสำหรับฉัน

หากคุณกำลังนำเข้าสิ่งอื่นโดยสิ้นเชิงอาจเป็นการแสดงที่มีประสิทธิภาพ แต่ถ้าคุณใช้ชื่อเฉพาะในโมดูลหลาย ๆ ครั้งในลูปวิกฤติเช่น แต่จากนั้นการสร้างชื่อท้องถิ่น (ภายในฟังก์ชั่น) จะยังคงเร็วขึ้น:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)

1
นอกจากนี้ยังมีสถานการณ์ที่คุณมีแพ็กเกจที่มีแพ็กเกจย่อยหรือโมดูลที่เปิดเผยแอ็ตทริบิวต์ของหนึ่งในแพ็กเกจ / โมดูลเหล่านั้นในแพ็กเกจระดับบนสุด การใช้from...importช่วยให้คุณทำpackage.attributeมากกว่าpackage.subpackage_or_module.attributeซึ่งจะเป็นประโยชน์หากคุณมีการจัดกลุ่มเชิงตรรกะหรือแนวคิดภายในแพ็คเกจ แต่ต้องการทำให้ผู้ใช้แพ็คเกจของคุณสะดวกขึ้น ( numpyเชื่อว่าเป็นอย่างนี้ฉันเชื่อว่า)
JAB

ใน django คุณมีจุดที่สิ่งต่าง ๆfrom django.core.management.base import BaseCommandดีกว่าและอื่น ๆ (โดยเฉพาะimport django) จะนำไปสู่รหัสที่อ่านไม่ได้ ดังนั้นในขณะที่ฉันชอบคำตอบนี้ฉันคิดว่ามีห้องสมุด (และโดยเฉพาะอย่างยิ่งกรอบบางอย่าง) ซึ่งการประชุมคือการละเมิดการนำเข้าที่เปลือยเปล่า เช่นเคยใช้วิจารณญาณของคุณเกี่ยวกับสิ่งที่ดีที่สุดในสถานการณ์ที่กำหนด แต่ผิดที่ด้านชัดเจน (ในคำอื่น ๆ ที่ฉันเห็นด้วยส่วนใหญ่)
neuronet

1
@JAB: คุณยังสามารถใช้เพื่อหาแพคเกจเป็นชื่อที่แตกต่างกันimport ... as ไม่เป็นหลักสิ่งเดียวกัน import package.subpackage_or_module as shortnamefrom parent import sub
Martijn Pieters

43

มีสองเหตุผลในความโปรดปรานของการใช้มีมากกว่าimport modulefrom module import function

แรกคือเนมสเปซ การนำเข้าฟังก์ชั่นลงใน namespace ส่วนกลางเสี่ยงต่อการชนชื่อ

ข้อที่สองนั้นไม่เกี่ยวข้องกับโมดูลมาตรฐาน แต่มีความสำคัญสำหรับโมดูลของคุณโดยเฉพาะในระหว่างการพัฒนา มันเป็นตัวเลือกให้กับreload()โมดูล พิจารณาสิ่งนี้:

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

ในทางกลับกัน

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

สำหรับความเร็ว ...

เรากำลังนำเข้าเฉพาะฟังก์ชั่นที่จำเป็นมากกว่านำเข้าโมดูลทั้งหมด (ซึ่งมีฟังก์ชั่นที่ไร้ประโยชน์มากกว่าซึ่งไพ ธ อนจะเสียเวลาในการนำเข้า)

ไม่ว่าคุณจะนำเข้าโมดูลหรือนำเข้าฟังก์ชั่นจากโมดูล Python จะแยกโมดูลทั้งหมด วิธีการนำเข้าโมดูล "การนำเข้าฟังก์ชั่น" ไม่มีอะไรมากไปกว่าการผูกฟังก์ชั่นเข้ากับชื่อ ในความเป็นจริงคือการทำงานน้อยลงสำหรับล่ามกว่าimport modulefrom module import func


6
reload () เป็น builtin ใน Python 2 ไม่เป็นเช่นนั้นอีกต่อไปสำหรับ Python 3
André

ฉันคิดว่ามีความเกี่ยวข้องกับการพึ่งพาการนำเข้าแบบวงกลมด้วยหรือไม่
ADP

18

ฉันใช้from importทุกครั้งที่ปรับปรุงการอ่าน ตัวอย่างเช่นฉันชอบ (อัฒภาคเป็นเพียงเพื่อประหยัดพื้นที่ที่นี่):

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

แทน:

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

อันหลังนั้นยากต่อการอ่าน (และเขียน) สำหรับฉันเพราะมันมีข้อมูลซ้ำซ้อนมากมาย นอกจากนี้ยังมีประโยชน์ที่จะทราบล่วงหน้าว่าส่วนใดของโมดูลที่ฉันใช้

ฉันชอบimports ทั่วไปถ้าฉันใช้ชื่อย่อจำนวนมากจากโมดูล:

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

หรือถ้าชื่อสามัญมากจนไม่สมเหตุสมผลนอกเนมสเปซ:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

นี่คือคำตอบที่ฉันโปรดปราน 'ชัดเจนดีกว่านัย' บางครั้งขัดแย้งกับการอ่านง่ายและแห้ง โดยเฉพาะอย่างยิ่งเมื่อใช้กรอบเช่น Django
neuronet

18

ในความคิดของฉันใช้เป็นประจำimportช่วยเพิ่มความสามารถในการอ่าน เมื่อตรวจสอบรหัสไพ ธ อนฉันชอบเห็นว่าฟังก์ชั่นหรือคลาสที่ให้มานั้นมาจากที่ใด มันช่วยฉันจากการเลื่อนไปด้านบนของโมดูลเพื่อรับข้อมูลนั้น

สำหรับชื่อโมดูลขนาดยาวฉันเพิ่งใช้asคำหลักและให้นามแฝงสั้น ๆ แก่พวกเขา:

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

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

เป็นข้อยกเว้นฉันมักจะใช้from module import somethingสัญกรณ์เมื่อฉันจัดการกับ__future__โมดูล คุณไม่สามารถทำอย่างอื่นได้เมื่อคุณต้องการให้สายทั้งหมดเป็น Unicode ตามค่าเริ่มต้นใน Python 2 เช่น

from __future__ import unicode_literals
from __future__ import print_function

สาธุ! "นำเข้าเป็น" เป็นชุดค่าผสมที่ชนะ :-)
paj28

4

แม้ว่าimport sysและfrom sys import agrvทั้งคู่จะนำเข้าsysโมดูลทั้งหมดแต่อย่างหลังจะใช้การเชื่อมชื่อเพื่อให้เฉพาะargvโมดูลที่เหลือเท่านั้นที่สามารถเข้าถึงรหัสได้

สำหรับบางคนนี่จะเป็นรูปแบบที่ต้องการเพราะทำให้เข้าถึงฟังก์ชั่นที่คุณระบุไว้อย่างชัดเจนเท่านั้น

อย่างไรก็ตามจะทำให้เกิดข้อขัดแย้งของชื่อที่อาจเกิดขึ้น ถ้าคุณได้โมดูลอีกคนหนึ่งชื่อargv? หมายเหตุคุณยังสามารถนำเข้าฟังก์ชั่นและเปลี่ยนชื่อได้อย่างชัดเจนfrom sys import argv as sys_argvซึ่งเป็นข้อตกลงที่สอดคล้องกับการนำเข้าอย่างชัดเจนและมีโอกาสน้อยที่จะให้การชนกันของพื้นที่ชื่อ


2
แล้วจะมีวิธีif sys_argv:ไหนดีไปกว่านี้if sys.argv:? ฉันรู้ว่าข้อความที่สองหมายถึงฉันไม่รู้ว่ารูปแบบแรกหมายถึงอะไรโดยไม่ย้อนกลับไปที่การนำเข้าที่แปลกประหลาด
msw

1

ฉันเพิ่งถามคำถามนี้กับตัวเอง ฉันจับเวลาวิธีการต่าง ๆ

ขอห้องสมุด

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

ห้องสมุดที่สวยงาม

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

ห้องสมุด 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

ไลบรารี 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

สำหรับผมแล้วมันมีความแตกต่างกันเล็กน้อยในการแสดง


คุณกำลังเพิ่มในการค้นหาแอตทริบิวต์ เพื่อเปรียบเทียบimport moduleกับfrom module import nameอย่างถูกต้องเพิ่มการค้นหาชื่อนั้นไปยังimport moduleกรณี เช่นเพิ่มบรรทัดsys.argvลงในarการทดสอบ ฯลฯ จะยังคงมีความแตกต่างเพราะงานที่ทำจะแตกต่างกันเล็กน้อยเนื่องจากมีการสร้างไบต์ที่แตกต่างกันและมีการประมวลผล codepaths ที่แตกต่างกัน
Martijn Pieters

2
โปรดทราบว่าฉันตอบข้อแตกต่างนั้นโดยตรง จะมีความแตกต่างระหว่างการใช้import sysแล้วใช้sys.argvพันของเวลาในวงเทียบแล้วใช้เพียงfrom sys import argv argvแต่คุณทำไม่ได้ สำหรับสิ่งที่คุณทำเพียงครั้งเดียวในระดับโลกของโมดูลของคุณคุณควรปรับให้เหมาะสมสำหรับการอ่านได้จริง ๆ ไม่ใช่ความแตกต่างทางกล้องจุลทรรศน์ในการกำหนดเวลา
Martijn Pieters

1
Ahhhh! และฉันคิดว่าฉันกำลังจะทำอะไรบางอย่าง! :) ฉันแค่อ่านคำตอบของคุณ ดูเหมือนว่าฉันจะกระโดดปืนที่หนึ่ง รู้สึกดีที่ได้ถ่อมตน
tmthyjames

-1

ดูที่โค้ดที่เผยแพร่แล้วนำเข้าโมดูลทั้งหมดและอ้างถึงmodule.functionเป็นมาตรฐานค่อนข้างน้อยสำหรับโมดูลมาตรฐาน ดูเหมือนจะมีข้อยกเว้นหนึ่งข้อdatetime

from datetime import datetime, timedelta

เพื่อให้คุณสามารถพูดได้มากกว่าdatetime.now()datetime.datetime.now()

หากคุณกังวลเรื่องประสิทธิภาพคุณสามารถพูดได้เสมอ (เช่น)

argv = sys.argv

จากนั้นทำโค้ดประสิทธิภาพที่สำคัญของคุณเนื่องจากการค้นหาโมดูลเสร็จสิ้นแล้ว อย่างไรก็ตามแม้ว่าสิ่งนี้จะใช้งานได้กับฟังก์ชั่น / วิธีการ แต่ IDE ส่วนใหญ่จะสับสนและจะไม่แสดงตัวอย่างลิงค์ต้นทาง / ลายเซ็นสำหรับฟังก์ชั่นเมื่อมันถูกกำหนดให้กับตัวแปร


-2

ฉันแค่อยากจะเพิ่มว่าถ้าคุณทำอะไรเช่น

from math import sin

(หรือไลบรารีในตัวอื่น ๆ เช่นsysหรือposix) จากนั้นsinจะรวมอยู่ในเอกสารประกอบสำหรับโมดูลของคุณ (เช่นเมื่อคุณทำ>>> help(mymodule)หรือ$ pydoc3 mymoduleเพื่อหลีกเลี่ยงปัญหานี้ให้นำเข้าโดยใช้:

import math
from math import sin as _sin

PS: ไลบรารีในตัวเป็นสิ่งที่รวบรวมจากรหัส C และรวมอยู่ใน Python argparse, osและioไม่ได้สร้างขึ้นในแพคเกจ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.