Absolute เทียบกับการนำเข้าแบบสัมพัทธ์อย่างชัดเจนของโมดูล Python


90

ฉันสงสัยเกี่ยวกับวิธีการนำเข้าแพ็คเกจในแอปพลิเคชัน Python ที่ต้องการ ฉันมีโครงสร้างแพ็คเกจดังนี้:

project.app1.models
project.app1.views
project.app2.models

project.app1.viewsการนำเข้าproject.app1.modelsและproject.app2.models. มีสองวิธีในการทำสิ่งนี้ที่ควรคำนึงถึง

ด้วยการนำเข้าที่แน่นอน:

import A.A
import A.B.B

หรือด้วยการนำเข้าสัมพัทธ์อย่างชัดเจนดังที่แนะนำในPython 2.5 ที่มี PEP 328 :

# explicit relative
from .. import A
from . import B

วิธีที่ยิ่งใหญ่ที่สุดในการทำเช่นนี้คืออะไร?


ตัวอย่าง "Explicit relative" เป็นข้อผิดพลาดทางไวยากรณ์ การนำเข้าสัมพัทธ์ต้องอยู่ในรูปแบบfrom _ import ...ดังนั้นตัวอย่างของคุณจะเป็นfrom .. import Aและfrom . import B
MestreLion

@MestreLion จับดีคุณพูดถูก! ฉันปรับปรุงคำถามของฉันจากไปimport ..A from .. import Aน่าทึ่งที่ใช้เวลาเพียง 9 ปีจนกว่าจะมีคนสังเกตเห็น;)
Daniel Hepper

คำตอบ:


56

การนำเข้าแบบสัมบูรณ์ จาก PEP 8:

ไม่แนะนำการนำเข้าสัมพัทธ์สำหรับการนำเข้าภายในแพ็คเกจ ใช้พา ธ แพ็กเกจสัมบูรณ์สำหรับการนำเข้าทั้งหมดเสมอ แม้ว่าตอนนี้ PEP 328 [7] จะถูกนำมาใช้อย่างสมบูรณ์ใน Python 2.5 แล้วก็ตาม การนำเข้าแบบสัมบูรณ์สามารถพกพาได้มากกว่าและมักจะอ่านได้มากกว่า

การนำเข้าแบบสัมพัทธ์ที่ชัดเจนเป็นคุณลักษณะภาษาที่ดี (ฉันเดา) แต่ก็ไม่ได้เกือบจะชัดเจนเท่ากับการนำเข้าแบบสัมบูรณ์ รูปแบบที่อ่านได้มากขึ้นคือ:

import A.A
import A.B.B

โดยเฉพาะอย่างยิ่งหากคุณนำเข้าเนมสเปซต่างๆ หากคุณดูโปรเจ็กต์ / แบบฝึกหัดที่เขียนไว้อย่างดีซึ่งรวมถึงการนำเข้าจากภายในแพ็คเกจพวกเขามักจะเป็นไปตามลักษณะนี้

การกดแป้นพิมพ์พิเศษสองสามครั้งที่คุณใช้เพื่อให้ชัดเจนยิ่งขึ้นจะช่วยให้ผู้อื่น (และบางทีคุณ) ประหยัดเวลาได้มากในอนาคตเมื่อพวกเขาพยายามหาเนมสเปซของคุณ (โดยเฉพาะอย่างยิ่งหากคุณย้ายไปที่ 3.x ซึ่งในบางแพ็คเกจ เปลี่ยนชื่อแล้ว)


@Rafe "ดูโปรเจ็กต์เขียนดี ๆ ... " มีข้อเสนอแนะหรือไม่?
denis

@ เดนิส: Rietveld เป็นโครงการของ Guido van Rossum ดังนั้นฉันคิดว่าน่าจะเป็นที่ที่ดี ( code.google.com/p/rietveld ) ไลบรารีมาตรฐานของ Python นั้นไม่ได้ยอดเยี่ยมมากโค้ดจำนวนมากนั้นไม่เป็นไปตามแบบแผน
Rafe Kettler

68
@Rafe: ส่วนนั้นของ PEP-8 ล้าสมัยตาม Guido mail.python.org/pipermail/python-dev/2010- ตุลาคม/104476.html
Brandon Rhodes

13
คำสั่งนั้นไม่อยู่ใน PEP-8 อีกต่อไปแล้ว ขณะนี้ระบุว่าแนะนำให้นำเข้าแบบสัมบูรณ์ แต่การนำเข้าแบบสัมพัทธ์เป็นทางเลือกที่ยอมรับได้
dano

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

125

การนำเข้าแบบสัมพัทธ์ของ Python ไม่ได้รับการสนับสนุนอย่างยิ่งอีกต่อไป แต่ขอแนะนำให้ใช้ absolute_import ในกรณีนี้

โปรดดูการสนทนาที่อ้างถึง Guido ตัวเอง:

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

OP เชื่อมโยงPEP 328ที่ระบุว่า:

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

นอกจากนี้ดูคำถามที่เกือบจะซ้ำกันเมื่อใดหรือทำไมจึงต้องใช้การนำเข้าแบบสัมพัทธ์ใน Python

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

ในการบังคับให้ใช้พฤติกรรมใหม่จาก PEP 328:

from __future__ import absolute_import

ในกรณีนี้การนำเข้าสัมพัทธ์โดยปริยายจะไม่สามารถทำได้อีกต่อไป (เช่นimport localfileจะไม่ทำงานอีกต่อไปเท่านั้นfrom . import localfile) สำหรับพฤติกรรมที่สะอาดและพิสูจน์ได้ในอนาคตขอแนะนำให้ใช้ absolute_import

ข้อแม้ที่สำคัญคือเนื่องจาก PEP 338และPEP 366การนำเข้าแบบสัมพัทธ์ต้องการให้นำเข้าไฟล์ python เป็นโมดูล - คุณไม่สามารถเรียกใช้ file.py ที่มีการนำเข้าสัมพัทธ์ไม่เช่นนั้นคุณจะได้รับไฟล์ValueError: Attempted relative import in non-package.

ข้อ จำกัด นี้ควรนำมาพิจารณาในการประเมินแนวทางที่ดีที่สุด Guido ต่อต้านการเรียกใช้สคริปต์จากโมดูลในทุกกรณี:

ฉัน -1 ในเรื่องนี้และในคู่อื่น ๆ ที่เสนอของเครื่องจักร __main__ กรณีการใช้งานเดียวดูเหมือนจะเรียกใช้สคริปต์ที่อยู่ในไดเร็กทอรีของโมดูลซึ่งฉันมักจะเห็นว่าเป็นแอนติแพตเทิร์น เพื่อทำให้ฉันเปลี่ยนใจคุณต้องทำให้ฉันมั่นใจว่ามันไม่ใช่

การอภิปรายอย่างละเอียดถี่ถ้วนเกี่ยวกับเรื่องนี้สามารถพบได้ใน SO; อีกครั้ง Python 3 นี้ค่อนข้างครอบคลุม:


9
Guido เขียนว่าในปี 2010 และยังอยู่ใน PEP? เราจะเชื่อถือ PEP ได้อย่างไรหากล้าสมัยไปแล้ว?
Jabba

2
PEP เปรียบเสมือนการแก้ไขของสหรัฐฯในแง่ที่คุณสามารถแก้ไขสิ่งต่างๆได้ มี PEPS ที่ถูกปฏิเสธจำนวนมากเช่นกัน PEP เป็นข้อเสนอ พวกเขาสามารถได้รับการยอมรับปฏิเสธหรือล้าสมัยซึ่งมักหมายถึง PEP ใหม่ PEP 8 เป็นคำแนะนำรูปแบบเพื่อให้สามารถแก้ไขได้
CppLearner

2
ฉันสับสนเกี่ยวกับส่วน "โมดูลภายในแพ็กเกจไม่สามารถนำเข้าเองได้อย่างง่ายดาย ... " ฉันไม่เคยได้ยินเกี่ยวกับการนำเข้าโมดูลมาก่อน
matiascelasco

2
ตัวอย่างหนึ่งที่เป็นไปได้ @matiascelasco: ถ้าคุณมี foo / bar.py และ foo / baz.py แต่ยังมี baz.py ที่อื่นด้วย หากคุณต้องการนำเข้า foo.baz จากแถบคุณอาจต้องแน่ใจในสิ่งที่คุณกำลังนำเข้าเช่น import .baz- นี่เป็นเพียงสถานการณ์ง่ายๆอย่างหนึ่งของสถานการณ์ที่คล้ายคลึงกันมากมายที่อธิบายไว้ใน PEP
Stefano

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

33

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


4
นี่คือลักษณะที่ท้อถอยตามคำแนะนำสไตล์ Python ความสามารถในการอ่านบนคลาวด์อย่างรุนแรงและไม่คุ้มกับ "ความสะดวกสบาย" ที่คุณพูดถึง หากคุณจำเป็นต้องใช้การนำเข้าแบบสัมพัทธ์เพื่อแก้ปัญหาแสดงว่าคุณทำผิด
Rafe Kettler

14
สังเกตความคิดเห็นของเขา (แบรนดอนโรดส์) ในคำตอบอื่นพร้อมลิงก์ที่แสดงว่าไม่ท้อถอยอีกต่อไป
Jon Coombs

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

3
@meowsqueak ฉันเห็นด้วยบางแพ็คเกจไม่สามารถติดตั้งได้อย่างง่ายดาย (ไม่ได้อยู่ใน pip คุณไม่ต้องการใช้python setup.py installหรือpython setup.py developด้วยเหตุผลใดก็ตาม) ในกรณีเหล่านี้ฉันแยกซอร์สโค้ดและเพิ่มเป็นโมดูลย่อย git เมื่อแพ็กเกจเหล่านั้นใช้การนำเข้าแบบสัมบูรณ์ในชื่อแพ็กเกจของตนเองการนำเข้าจะล้มเหลว ทางออกเดียวคือใช้การนำเข้าสัมพัทธ์ที่ชัดเจน นั่นคือสิ่งที่ควรให้กำลังใจฉันคิดว่า
CMCDragonkai
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.