ฉันใช้ Python มากขึ้นเรื่อย ๆ และฉันก็เห็นการ__all__
ตั้งค่าตัวแปรใน__init__.py
ไฟล์ต่างๆ มีใครช่วยอธิบายสิ่งนี้ได้บ้าง
ฉันใช้ Python มากขึ้นเรื่อย ๆ และฉันก็เห็นการ__all__
ตั้งค่าตัวแปรใน__init__.py
ไฟล์ต่างๆ มีใครช่วยอธิบายสิ่งนี้ได้บ้าง
คำตอบ:
import *
มันเป็นรายการของวัตถุสาธารณะของโมดูลที่เป็นตีความโดย มันแทนที่ค่าเริ่มต้นของการซ่อนทุกอย่างที่ขึ้นต้นด้วยขีดล่าง
import *
(เช่นเช่นtk
) คำแนะนำที่ดีถ้าเป็นกรณีนี้คือการมี__all__
หรือชื่อขึ้นต้นด้วยขีดเส้นใต้ในรหัสของโมดูล
tk
ได้รับการปล่อยตัวในวันนี้ (หรือในปี 2012 แม้) from tk import *
การปฏิบัติที่แนะนำจะใช้ ฉันคิดว่าการฝึกเป็นที่ยอมรับเนื่องจากความเฉื่อยไม่ใช่การออกแบบโดยเจตนา
เชื่อมโยงกับ แต่ไม่ได้กล่าวถึงอย่างชัดเจนที่นี่คือเมื่อ__all__
มีการใช้งาน มันคือรายการของสตริงที่กำหนดว่าสัญลักษณ์ใดในโมดูลจะถูกส่งออกเมื่อfrom <module> import *
ใช้กับโมดูล
ตัวอย่างเช่นรหัสต่อไปนี้ในการfoo.py
ส่งออกสัญลักษณ์bar
และbaz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
สัญลักษณ์เหล่านี้สามารถนำเข้าได้เช่น:
from foo import *
print(bar)
print(baz)
# The following will trigger an exception, as "waz" is not exported by the module
print(waz)
หาก__all__
มีการใส่ความคิดเห็นด้านบนรหัสนี้จะดำเนินการให้เสร็จสมบูรณ์เนื่องจากพฤติกรรมเริ่มต้นของimport *
คือการนำเข้าสัญลักษณ์ทั้งหมดที่ไม่ได้เริ่มต้นด้วยการขีดเส้นใต้จากเนมสเปซที่กำหนด
การอ้างอิง: https://docs.python.org/tutorial/modules.html#importing-from-a-package
หมายเหตุ: __all__
มีผลกับfrom <module> import *
พฤติกรรมเท่านั้น สมาชิกที่ไม่ได้กล่าวถึงในยังคงสามารถเข้าถึงจากภายนอกโมดูลและสามารถนำเข้าด้วย__all__
from <module> import <member>
print(baz())
?
print(baz)
พิมพ์บางอย่างเช่น<function baz at 0x7f32bc363c10>
ในขณะที่print(baz())
พิมพ์baz
อธิบาย __all__ ใน Python ได้ไหม
ฉันเห็นตัวแปรที่
__all__
ตั้งไว้ใน__init__.py
ไฟล์ที่แตกต่างกันสิ่งนี้ทำอะไร
__all__
ทำอย่างไรมันประกาศชื่อ "สาธารณะ" ความหมายจากโมดูล หากมีชื่ออยู่__all__
ผู้ใช้จะถูกคาดหวังให้ใช้และพวกเขาสามารถคาดหวังว่ามันจะไม่เปลี่ยนแปลง
นอกจากนี้ยังจะมีผลกับโปรแกรม:
import *
__all__
ในโมดูลเช่นmodule.py
:
__all__ = ['foo', 'Bar']
หมายความว่าเมื่อคุณimport *
จากโมดูลเฉพาะชื่อเหล่านั้นใน__all__
การนำเข้า:
from module import * # imports foo and Bar
เอกสารและเครื่องมือการเติมข้อความอัตโนมัติรหัสอาจ (ในความเป็นจริงควร) นอกจากนี้ยังตรวจสอบ__all__
เพื่อกำหนดชื่อที่จะแสดงตามที่มีอยู่จากโมดูล
__init__.py
ทำให้ไดเรกทอรีเป็นแพ็คเกจ Pythonจากเอกสาร :
__init__.py
ไฟล์จะต้องทำให้งูหลามรักษาไดเรกทอรีที่เป็นแพคเกจที่มี; สิ่งนี้ถูกทำขึ้นเพื่อป้องกันไดเรกทอรีที่มีชื่อสามัญเช่นสตริงจากการซ่อนโมดูลที่ถูกต้องโดยไม่ตั้งใจซึ่งเกิดขึ้นในภายหลังบนเส้นทางการค้นหาโมดูลในกรณีที่ง่ายที่สุด
__init__.py
สามารถเป็นไฟล์ที่ว่างเปล่าได้ แต่สามารถรันโค้ดการเริ่มต้นสำหรับแพ็คเกจหรือตั้งค่า__all__
ตัวแปรได้
ดังนั้น__init__.py
สามารถประกาศ__all__
สำหรับแพคเกจ
โดยทั่วไปแพ็คเกจจะประกอบด้วยโมดูลที่อาจนำเข้าซึ่งกันและกัน แต่จำเป็นต้องเชื่อมโยงกับ__init__.py
ไฟล์ ไฟล์นั้นเป็นสิ่งที่ทำให้ไดเรกทอรีเป็นแพคเกจ Python ที่แท้จริง ตัวอย่างเช่นสมมติว่าคุณมีไฟล์ต่อไปนี้ในแพ็คเกจ:
package
├── __init__.py
├── module_1.py
└── module_2.py
มาสร้างไฟล์เหล่านี้ด้วย Python เพื่อให้คุณติดตามได้ - คุณสามารถวางสิ่งต่อไปนี้ลงใน Python 3 shell:
from pathlib import Path
package = Path('package')
package.mkdir()
(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")
package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")
package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")
และตอนนี้คุณได้แสดง API ที่สมบูรณ์ที่บุคคลอื่นสามารถใช้เมื่อพวกเขานำเข้าแพคเกจของคุณเช่น:
import package
package.foo()
package.Bar()
และแพ็คเกจจะไม่มีรายละเอียดการใช้งานอื่น ๆ ทั้งหมดที่คุณใช้เมื่อสร้างโมดูลของคุณที่ยุ่งเหยิงในpackage
เนมสเปซ
__all__
ใน __init__.py
หลังจากทำงานมากขึ้นคุณอาจตัดสินใจว่าโมดูลนั้นใหญ่เกินไป (เช่นหลายพันบรรทัด) และต้องแยกกัน ดังนั้นคุณทำดังต่อไปนี้:
package
├── __init__.py
├── module_1
│ ├── foo_implementation.py
│ └── __init__.py
└── module_2
├── Bar_implementation.py
└── __init__.py
ก่อนอื่นให้สร้างไดเร็กทอรีย่อยด้วยชื่อเดียวกับโมดูล:
subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()
ย้ายการใช้งาน:
package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')
สร้าง__init__.py
s สำหรับแพ็คเกจย่อยที่ประกาศ__all__
สำหรับแต่ละ:
(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")
และตอนนี้คุณยังมี API ที่กำหนดไว้ในระดับแพ็คเกจ:
>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>
และคุณสามารถเพิ่มสิ่งต่าง ๆ ลงใน API ของคุณที่คุณสามารถจัดการได้ที่ระดับแพ็คเกจย่อยแทนระดับโมดูลของแพ็คเกจย่อย หากคุณต้องการเพิ่มชื่อใหม่ให้กับ API คุณเพียงอัปเดต__init__.py
เช่นใน module_2:
from .Bar_implementation import *
from .Baz_implementation import *
__all__ = ['Bar', 'Baz']
และหากคุณไม่พร้อมที่จะเผยแพร่Baz
ใน API ระดับบนสุดในระดับสูงสุดของ__init__.py
คุณคุณสามารถมี:
from .module_1 import * # also constrained by __all__'s
from .module_2 import * # in the __init__.py's
__all__ = ['foo', 'Bar'] # further constraining the names advertised
และหากผู้ใช้ของคุณทราบถึงความพร้อมของBaz
พวกเขาก็สามารถใช้งานได้:
import package
package.Baz()
แต่ถ้าพวกเขาไม่รู้เกี่ยวกับมันเครื่องมืออื่น ๆ (เช่นpydoc ) จะไม่แจ้งให้ทราบ
คุณสามารถเปลี่ยนแปลงได้ในภายหลังว่าเมื่อBaz
พร้อมสำหรับเวลาที่ดี
from .module_1 import *
from .module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
_
กับ__all__
:_
โดยค่าเริ่มต้นงูใหญ่จะส่งออกชื่อทั้งหมดที่ไม่ได้เริ่มต้นด้วย แน่นอนคุณสามารถพึ่งพากลไกนี้ แพคเกจบางอย่างใน Python standard library จริง ๆแล้วพึ่งพาสิ่งนี้ แต่เมื่อต้องการทำเช่นนั้นพวกมันแทน alias import ของพวกเขาตัวอย่างเช่นในctypes/__init__.py
:
import os as _os, sys as _sys
การใช้_
แบบแผนอาจดูสง่างามกว่าเพราะจะลบความซ้ำซ้อนของการตั้งชื่ออีกครั้ง แต่มันเพิ่มความซ้ำซ้อนสำหรับการนำเข้า (ถ้าคุณมีจำนวนมาก) และมันง่ายที่จะลืมที่จะทำอย่างสม่ำเสมอ - และสิ่งสุดท้ายที่คุณต้องการคือการสนับสนุนสิ่งที่คุณตั้งใจจะมีเพียงรายละเอียดการใช้งานอย่างไม่มีกำหนด เพราะคุณลืมคำนำหน้า_
เมื่อตั้งชื่อฟังก์ชั่น
ฉันเขียน__all__
วงจรการพัฒนาของตัวเองแต่เนิ่นๆสำหรับโมดูลเพื่อให้ผู้อื่นที่อาจใช้รหัสของฉันรู้ว่าควรใช้อะไรและไม่ควรใช้
แพ็คเกจส่วนใหญ่ในไลบรารีมาตรฐานก็ใช้__all__
เช่นกัน
__all__
ทำให้รู้สึกมันเหมาะสมที่จะยึดติดกับ_
คำนำหน้าแทน__all__
เมื่อ:
export
มัณฑนากรข้อเสียของการใช้__all__
คือคุณต้องเขียนชื่อของฟังก์ชั่นและคลาสที่จะถูกส่งออกสองครั้ง - และข้อมูลจะถูกเก็บแยกต่างหากจากคำจำกัดความ เราสามารถใช้มัณฑนากรเพื่อแก้ปัญหานี้
ฉันมีความคิดสำหรับมัณฑนากรส่งออกจากการพูดคุยของ David Beazley บนบรรจุภัณฑ์ การใช้งานนี้ดูเหมือนจะทำงานได้ดีในผู้นำเข้าดั้งเดิมของ CPython หากคุณมีเบ็ดหรือระบบการนำเข้าพิเศษฉันไม่รับประกัน แต่ถ้าคุณนำมาใช้มันเป็นเรื่องที่ค่อนข้างง่ายที่จะถอยออก - คุณจะต้องเพิ่มชื่อกลับเข้าไปใน__all__
ดังนั้นในตัวอย่างเช่นยูทิลิตี้ไลบรารี่คุณจะต้องกำหนดมัณฑนากร:
import sys
def export(fn):
mod = sys.modules[fn.__module__]
if hasattr(mod, '__all__'):
mod.__all__.append(fn.__name__)
else:
mod.__all__ = [fn.__name__]
return fn
จากนั้นคุณจะกำหนดที่__all__
คุณทำสิ่งนี้:
$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.
@export
def foo(): pass
@export
def bar():
'bar'
def main():
print('main')
if __name__ == '__main__':
main()
และใช้งานได้ดีไม่ว่าจะเรียกใช้เป็นหลักหรือนำเข้าโดยฟังก์ชันอื่น
$ cat > run.py
import main
main.main()
$ python run.py
main
และการจัดสรร API ด้วยimport *
ก็จะใช้ได้เช่นกัน:
$ cat > run.py
from main import *
foo()
bar()
main() # expected to error here, not exported
$ python run.py
Traceback (most recent call last):
File "run.py", line 4, in <module>
main() # expected to error here, not exported
NameError: name 'main' is not defined
__init__.py
และการใช้งาน__all__
__all__
ถูกต้องด้วยตนเอง
__all__
ด้วย - แต่ฉันจะบอกว่าคุณมี API ที่ไม่เสถียร ... นี่เป็นสิ่งที่จะต้องมีการทดสอบการยอมรับที่ครอบคลุม
module_1
และmodule_2
; มันมีการตกลงที่จะรวมอย่างชัดเจนdel module_1
ใน__init__.py
? ฉันผิดที่คิดว่าสิ่งนี้มีค่าหรือไม่
ฉันแค่เพิ่มสิ่งนี้เพื่อความแม่นยำ:
คำตอบอื่น ๆ ทั้งหมดอ้างอิงถึงโมดูล คำถามเดิมกล่าวถึง explicitely __all__
ใน__init__.py
ไฟล์ดังนั้นนี้เป็นเรื่องเกี่ยวกับงูหลามแพคเกจ
โดยทั่วไป__all__
จะเข้ามาเล่นเมื่อมีการใช้from xxx import *
ชุดตัวเลือกของimport
คำสั่ง สิ่งนี้ใช้กับแพ็คเกจรวมถึงโมดูล
อธิบายพฤติกรรมของโมดูลในคำตอบอื่น ๆ พฤติกรรมที่แน่นอนสำหรับแพคเกจอธิบายไว้ที่นี่ในรายละเอียด
กล่าวโดยย่อ__all__
ในระดับแพ็คเกจจะทำสิ่งเดียวกันกับโมดูลยกเว้นจะเกี่ยวข้องกับโมดูลภายในแพ็คเกจ (ตรงกันข้ามกับการระบุชื่อภายในโมดูล ) ดังนั้น__all__
ระบุโมดูลทั้งหมดนั้นจะต้องถูกโหลดและนำเข้ามาใน namespace from package import *
ปัจจุบันเมื่อเราใช้
ความแตกต่างใหญ่คือเมื่อคุณละเว้นการประกาศ__all__
ในแพคเกจ__init__.py
คำสั่งfrom package import *
จะไม่นำเข้าอะไรเลย (มีข้อยกเว้นอธิบายไว้ในเอกสารประกอบให้ดูลิงค์ด้านบน)
ในทางกลับกันหากคุณไม่ใช้__all__
งานโมดูล "นำเข้าที่ติดดาว" จะนำเข้าชื่อทั้งหมด (ไม่เริ่มต้นด้วยขีดล่าง) ที่กำหนดไว้ในโมดูล
from package import *
จะยังคงนำเข้าทุกอย่างที่กำหนดไว้ในแม้ว่าจะไม่มี__init__.py
all
ความแตกต่างที่สำคัญคือถ้าไม่มี__all__
มันจะไม่นำเข้าโมดูลใด ๆ ที่กำหนดไว้ในไดเรกทอรีของแพ็คเกจโดยอัตโนมัติ
นอกจากนี้ยังเปลี่ยนแปลงสิ่งที่ pydoc จะแสดง:
module1.py
a = "A"
b = "B"
c = "C"
module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ pydoc module1
ช่วยในโมดูล module1: ชื่อ module1 ไฟล์ module1.py DATA a = 'A' b = 'B' c = 'C'
$ pydoc module2
ช่วยในโมดูล module2: ชื่อ module2 ไฟล์ module2.py DATA __all__ = ['a', 'b'] a = 'A' b = 'B'
ฉันประกาศ__all__
ในโมดูลทั้งหมดของฉันรวมถึงเน้นรายละเอียดภายในสิ่งเหล่านี้ช่วยได้มากเมื่อใช้สิ่งที่คุณไม่เคยใช้มาก่อนในเซสชันล่ามแบบสด
__all__
ปรับแต่ง*
ในfrom <module> import *
__all__
ปรับแต่ง*
ในfrom <package> import *
โมดูลเป็น.py
ไฟล์หมายความว่าจะต้องนำเข้า
แพคเกจเป็นไดเรกทอรีกับเป็น__init__.py
ไฟล์ แพคเกจมักจะมีโมดูล
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
ช่วยให้มนุษย์รู้ว่าคุณสมบัติ "สาธารณะ" ของโมดูล [ @AaronHall ] นอกจากนี้pydocยังจดจำพวกมันได้ [ @Longpoke ]
ดูวิธีการswiss
และcheddar
นำมาไว้ในเนมสเปซท้องถิ่น แต่ไม่ใช่gouda
:
>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined
หากไม่มี__all__
สัญลักษณ์ใด ๆ (ที่ไม่ได้ขึ้นต้นด้วยเครื่องหมายขีดเส้นใต้) จะสามารถใช้ได้
*
จะไม่ได้รับผลกระทบจาก__all__
>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)
>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)
>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)
ใน__init__.py
ไฟล์ของแพ็คเกจ __all__
เป็นรายการสตริงที่มีชื่อของโมดูลสาธารณะหรือวัตถุอื่น ๆ คุณลักษณะเหล่านั้นพร้อมใช้งานเพื่อนำเข้าสัญลักษณ์แทน เช่นเดียวกับโมดูล__all__
กำหนด*
เมื่อสัญลักษณ์แทนการนำเข้าจากแพคเกจ [ @MartinStettner ]
นี่เป็นข้อความที่ตัดตอนมาจากตัวเชื่อมต่อ Python MySQL __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
กรณีเริ่มต้นเครื่องหมายดอกจันที่ไม่มี__all__
สำหรับแพ็คเกจมีความซับซ้อนเนื่องจากพฤติกรรมที่เห็นได้ชัดอาจมีราคาแพง: การใช้ระบบไฟล์เพื่อค้นหาโมดูลทั้งหมดในแพ็คเกจ ในการอ่านเอกสารของฉัน__init__.py
มีการนำเข้าเฉพาะวัตถุที่กำหนดไว้เท่านั้น:
หาก
__all__
ไม่ได้กำหนดไว้คำสั่งfrom sound.effects import *
จะไม่นำเข้า submodules ทั้งหมดจากแพ็คเกจsound.effects
ไปยัง namespace ปัจจุบัน มันช่วยให้มั่นใจได้ว่าแพคเกจsound.effects
นั้นได้ถูกนำเข้าแล้ว (อาจเรียกใช้รหัสการเริ่มต้นใด ๆ ใน__init__.py
) จากนั้นจึงนำเข้าชื่อใด ๆ ซึ่งรวมถึงชื่อใด ๆ ที่กำหนดไว้ (และ submodules โหลดอย่างชัดเจน)__init__.py
โดย นอกจากนี้ยังรวมถึง submodules ใด ๆ ของแพคเกจที่โหลดอย่างชัดเจนโดยงบการนำเข้าก่อนหน้านี้
สัญลักษณ์แทนการนำเข้า ... ควรหลีกเลี่ยงเนื่องจากผู้อ่าน [สับสน] และเครื่องมืออัตโนมัติจำนวนมาก
[ PEP 8 , @ToolmakerSteve]
from <package> import *
ได้โดยไม่ต้อง__all__
อยู่ใน__init__.py
ที่ไม่ได้นำเข้าใด ๆ ของโมดูล
__init__.py
เป็นโมดูล แต่ฉันไม่แน่ใจว่าถูกต้องหรือโดยเฉพาะอย่างยิ่งถ้าวัตถุที่ขีดเส้นใต้คำนำหน้าถูกยกเว้น นอกจากนี้ฉันแยกส่วนต่าง ๆ อย่างชัดเจนเกี่ยวกับโมดูลและแพ็คเกจ ความคิดของคุณ?
จาก(อย่างไม่เป็นทางการ) Python อ้างอิง Wiki :
ชื่อสาธารณะที่กำหนดโดยโมดูลจะถูกกำหนดโดยการตรวจสอบ namespace ของโมดูลสำหรับตัวแปรชื่อ
__all__
; หากกำหนดไว้จะต้องเป็นลำดับของสตริงที่กำหนดชื่อหรือนำเข้าโดยโมดูลนั้น ชื่อที่ให้ไว้เป็นชื่อ__all__
สาธารณะทั้งหมดและจำเป็นต้องมีอยู่จริง หาก__all__
ไม่ได้กำหนดไว้ชุดของชื่อสาธารณะจะรวมชื่อทั้งหมดที่พบในเนมสเปซของโมดูลซึ่งไม่ได้ขึ้นต้นด้วยอักขระขีดล่าง ("_")__all__
ควรมี API สาธารณะทั้งหมด มีวัตถุประสงค์เพื่อหลีกเลี่ยงการส่งออกรายการที่ไม่ได้เป็นส่วนหนึ่งของ API โดยไม่ตั้งใจ (เช่นโมดูลห้องสมุดที่นำเข้าและใช้ภายในโมดูล)
__all__
ใช้เพื่อทำเอกสาร API สาธารณะของโมดูล Python แม้ว่ามันจะเป็นตัวเลือก__all__
ควรใช้
นี่คือข้อความที่ตัดตอนมาที่เกี่ยวข้องจากการอ้างอิงภาษา Python :
ชื่อสาธารณะที่กำหนดโดยโมดูลจะถูกกำหนดโดยการตรวจสอบ namespace ของโมดูลสำหรับตัวแปรชื่อ
__all__
; หากกำหนดไว้จะต้องเป็นลำดับของสตริงที่กำหนดชื่อหรือนำเข้าโดยโมดูลนั้น ชื่อที่ให้ไว้เป็นชื่อ__all__
สาธารณะทั้งหมดและจำเป็นต้องมีอยู่จริง หาก__all__
ไม่ได้กำหนดไว้ชุดของชื่อสาธารณะจะรวมชื่อทั้งหมดที่พบในเนมสเปซของโมดูลซึ่งไม่ได้ขึ้นต้นด้วยอักขระขีดล่าง ('_')__all__
ควรมี API สาธารณะทั้งหมด มีวัตถุประสงค์เพื่อหลีกเลี่ยงการส่งออกรายการที่ไม่ได้เป็นส่วนหนึ่งของ API โดยไม่ตั้งใจ (เช่นโมดูลห้องสมุดที่นำเข้าและใช้ภายในโมดูล)
PEP 8ใช้ถ้อยคำที่คล้ายกันแม้ว่าจะชัดเจนว่าชื่อที่นำเข้านั้นไม่ได้เป็นส่วนหนึ่งของ API สาธารณะเมื่อ__all__
ไม่อยู่:
เพื่อรองรับการวิปัสสนาที่ดีขึ้นโมดูลควรประกาศชื่ออย่างชัดเจนใน API สาธารณะโดยใช้
__all__
แอตทริบิวต์ การตั้งค่า__all__
เป็นรายการว่างบ่งชี้ว่าโมดูลไม่มี API สาธารณะ[ ... ]
ชื่อที่นำเข้าควรได้รับการพิจารณารายละเอียดการใช้งาน โมดูลอื่น ๆ จะต้องไม่พึ่งพาการเข้าถึงโดยอ้อมกับชื่อที่นำเข้าดังกล่าวเว้นแต่ว่าจะเป็นส่วนที่มีการบันทึกไว้อย่างชัดเจนของ API ของโมดูลที่มีเช่น
os.path
หรือ__init__
โมดูลของแพคเกจที่เปิดเผยการทำงานจาก submodules
นอกจากนี้ตามที่ระบุไว้ในคำตอบอื่น ๆ__all__
จะใช้ในการเปิดใช้งานสัญลักษณ์แทนการนำเข้าสำหรับแพ็คเกจ :
คำสั่งการนำเข้าใช้การประชุมดังต่อไปนี้: ถ้า
__init__.py
รหัสของแพ็คเกจกำหนดรายการชื่อ__all__
จะถูกนำไปเป็นรายการชื่อโมดูลที่ควรนำเข้าเมื่อfrom package import *
พบ
__all__
ส่งผลกระทบต่อfrom <module> import *
งบ
ลองพิจารณาตัวอย่างนี้:
foo
├── bar.py
└── __init__.py
ในfoo/__init__.py
:
(โดยปริยาย) ถ้าเราไม่กำหนด__all__
แล้วจะนำเข้าชื่อที่กำหนดไว้ในfrom foo import *
foo/__init__.py
(Explicit) ถ้าเรากำหนด__all__ = []
แล้วfrom foo import *
จะนำเข้าอะไร
(Explicit) ถ้าเรากำหนด__all__ = [ <name1>, ... ]
แล้วfrom foo import *
จะนำเข้าชื่อเหล่านั้น
_
ทราบว่าในกรณีนัยหลามจะไม่นำเข้าชื่อเริ่มต้นด้วย __all__
อย่างไรก็ตามคุณสามารถบังคับให้นำเข้าโดยใช้ชื่อดังกล่าว
__all__
ส่งผลกระทบต่อวิธีการfrom foo import *
ทำงาน
รหัสที่อยู่ภายในร่างกายโมดูล (แต่ไม่ใช่ในร่างกายของฟังก์ชั่นหรือคลาส) อาจใช้เครื่องหมายดอกจัน ( *
) ในfrom
คำสั่ง:
from foo import *
*
ขอให้ทุกแบบโมดูลfoo
(ยกเว้นผู้ที่มีจุดเริ่มต้นขีด) จะผูกพันเป็นตัวแปรระดับโลกในโมดูลนำเข้า เมื่อfoo
มีแอททริ__all__
บิวค่าของแอททริบิวจะเป็นรายการของชื่อที่ผูกพันกับfrom
คำสั่งประเภทนี้
ถ้าfoo
เป็นแพ็กเกจและ__init__.py
นิยามรายการที่มีชื่อ__all__
จะถูกใช้เป็นรายการของชื่อ submodule ที่ควรอิมพอร์ตเมื่อfrom foo import *
พบ หาก__all__
ไม่ได้กำหนดไว้คำสั่งจะfrom foo import *
อิมพอร์ตชื่อที่กำหนดไว้ในแพ็คเกจ ซึ่งรวมถึงชื่อใด ๆ ที่กำหนดไว้ (และ submodules โหลดอย่างชัดเจน) __init__.py
โดย
โปรดทราบว่า__all__
ไม่จำเป็นต้องเป็นรายการ ตามเอกสารในimport
คำสั่งถ้ากำหนด__all__
จะต้องเป็นลำดับของสตริงซึ่งเป็นชื่อที่กำหนดหรือนำเข้าโดยโมดูล ดังนั้นคุณอาจใช้ tuple เพื่อบันทึกหน่วยความจำและรอบการทำงานของ CPU อย่าลืมเครื่องหมายจุลภาคในกรณีที่โมดูลกำหนดชื่อสาธารณะเดียว:
__all__ = ('some_name',)
ดูเพิ่มเติมเหตุใด“ การนำเข้า *” จึงไม่ดี
สิ่งนี้กำหนดไว้ใน PEP8 ที่นี่ :
ชื่อตัวแปรส่วนกลาง
(หวังว่าตัวแปรเหล่านี้มีไว้สำหรับใช้ภายในโมดูลเดียวเท่านั้น) การประชุมมีความคล้ายคลึงกับฟังก์ชั่น
โมดูลที่ออกแบบมาเพื่อใช้งานผ่าน
from M import *
ควรใช้__all__
กลไกเพื่อป้องกันการส่งออก globals หรือใช้แบบแผนเก่าของ prefixing globals ดังกล่าวด้วยการขีดเส้นใต้ (ซึ่งคุณอาจต้องการทำเพื่อระบุว่า globals เหล่านี้เป็น
PEP8 จัดเตรียมข้อตกลงการเข้ารหัสสำหรับโค้ด Python ซึ่งประกอบด้วยไลบรารีมาตรฐานในการแจกแจง Python หลัก ยิ่งคุณติดตามสิ่งนี้มากเท่าไหร่คุณก็ยิ่งใกล้ชิดกับเจตนาดั้งเดิมมากขึ้นเท่านั้น
__all__
กรณีที่__all__
มีอยู่จะไม่ถูกซ่อนอย่างแน่นอน พวกเขาสามารถเห็นและเข้าถึงได้อย่างสมบูรณ์แบบตามปกติถ้าคุณรู้ชื่อของพวกเขา มันเป็นเพียงในกรณีของ "นำเข้า *" ซึ่งไม่แนะนำต่อไปว่าความแตกต่างมีน้ำหนักใด ๆ