سؤال

لقد كنت أستخدم بايثون أكثر فأكثر، وما زلت أرى المتغير __all__ مجموعة مختلفة __init__.py ملفات.هل يمكن لأحد أن يشرح ماذا يفعل هذا؟

هل كانت مفيدة؟

المحلول

إنها قائمة بالكائنات العامة لتلك الوحدة، كما تم تفسيرها بواسطة import *.إنه يتجاوز الإعداد الافتراضي لإخفاء كل ما يبدأ بشرطة سفلية.

نصائح أخرى

مرتبط بـ، ولكن لم يتم ذكره صراحةً هنا، هو متى بالضبط __all__ يستخدم.إنها قائمة من السلاسل التي تحدد الرموز الموجودة في الوحدة النمطية التي سيتم تصديرها ومتى from <module> import * يستخدم على الوحدة النمطية.

على سبيل المثال، الكود التالي في a 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>.

أنا فقط أضيف هذا ليكون دقيقًا:

جميع الإجابات الأخرى تشير إلى وحدات.السؤال الأصلي المذكور صراحة __all__ في __init__.py الملفات، لذلك هذا يتعلق ببايثون الحزم.

عمومًا، __all__ لا يدخل حيز التنفيذ إلا عندما from xxx import * البديل من import يستخدم البيان.وهذا ينطبق على الحزم وكذلك على الوحدات.

تم شرح سلوك الوحدات في الإجابات الأخرى.تم وصف السلوك الدقيق للحزم هنا بالتفصيل.

باختصار، __all__ على مستوى الحزمة يفعل نفس الشيء تقريبًا كما هو الحال مع الوحدات النمطية، باستثناء أنه يتعامل مع وحدات داخل الحزمة (على النقيض من التحديد الأسماء داخل الوحدة).لذا __all__ يحدد جميع الوحدات التي سيتم تحميلها واستيرادها إلى مساحة الاسم الحالية عندما نستخدمها from package import *.

الفرق الكبير هو أنه عندما حذف إعلان __all__ في الحزمة __init__.py, ، البيان from package import * لن يقوم باستيراد أي شيء على الإطلاق (مع الاستثناءات الموضحة في الوثائق، راجع الرابط أعلاه).

ومن ناحية أخرى، إذا حذفت __all__ في الوحدة النمطية، سيؤدي "الاستيراد المميز بنجمة" إلى استيراد جميع الأسماء (لا تبدأ بشرطة سفلية) المحددة في الوحدة النمطية.

اشرح __all__ في بايثون؟

أستمر في رؤية المتغير __all__ مجموعة مختلفة __init__.py ملفات.

ماذا يفعل هذا؟

ماذا فعلت __all__ يفعل؟

يعلن عن الأسماء "العامة" دلاليًا من الوحدة النمطية.إذا كان هناك اسم في __all__, ، من المتوقع أن يستخدمه المستخدمون، ويمكنهم أن يتوقعوا أنه لن يتغير.

سيكون له أيضًا تأثيرات برمجية:

import *

__all__ في وحدة نمطية، على سبيل المثال. module.py:

__all__ = ['foo', 'Bar']

يعني أنه عندما كنت import * من الوحدة، فقط تلك الأسماء الموجودة في ملف __all__ يتم استيرادها:

from module import *               # imports foo and Bar

أدوات التوثيق

قد تقوم أدوات التوثيق والإكمال التلقائي للتعليمات البرمجية (في الواقع، ينبغي) أيضًا بفحص ملف __all__ لتحديد الأسماء التي سيتم إظهارها على أنها متاحة من الوحدة النمطية.

__init__.py يجعل الدليل حزمة بايثون

من مستندات:

ال __init__.py الملفات مطلوبة لجعل بايثون تتعامل مع المجلدات على أنها تحتوي على حزم؛يتم ذلك لمنع الدلائل ذات الاسم الشائع، مثل السلسلة، من إخفاء الوحدات النمطية الصالحة التي تحدث لاحقًا في مسار بحث الوحدة عن غير قصد.

في أبسط الحالات، __init__.py يمكن أن يكون مجرد ملف فارغ، ولكن يمكنه أيضًا تنفيذ رمز التهيئة للحزمة أو تعيين ملف __all__ عامل.

لذلك __init__.py يمكن أن يعلن __all__ ل طَرد.

إدارة واجهة برمجة التطبيقات:

تتكون الحزمة عادةً من وحدات قد تستورد بعضها البعض، ولكنها بالضرورة مرتبطة ببعضها البعض __init__.py ملف.هذا الملف هو ما يجعل الدليل حزمة بايثون فعلية.على سبيل المثال، لنفترض أن لديك ما يلي:

 package/
   |-__init__.py # makes directory a Python package
   |-module_1.py
   |-module_2.py

في ال __init__.py انت تكتب:

from module_1 import *
from module_2 import *

و في module_1 لديك:

__all__ = ['foo',]

و في module_2 لديك:

__all__ = ['Bar',]

والآن قد قدمت واجهة برمجة تطبيقات كاملة يمكن لشخص آخر استخدامها عند استيراد الحزمة الخاصة بك، كما يلي:

import package
package.foo()
package.Bar()

ولن تحتوي على جميع الأسماء الأخرى التي استخدمتها عند إنشاء الوحدات النمطية الخاصة بك التي تشوش package مساحة الاسم.

__all__ في __init__.py

بعد المزيد من العمل، ربما تكون قد قررت أن الوحدات كبيرة جدًا ويجب تقسيمها.لذلك عليك القيام بما يلي:

 package/
   |-__init__.py
   |-module_1/
   |  |-__init__.py
   |  |-foo_implementation.py
   |-module_2/
      |-__init__.py
      |-Bar_implementation.py

وفي كل __init__.py تعلن __all__, ، على سبيل المثال.في الوحدة_1:

from foo_implementation import *
__all__ = ['foo']

وmodule_2 __init__.py:

from Bar_implementation import *
__all__ = ['Bar']

ويمكنك بسهولة إضافة أشياء إلى واجهة برمجة التطبيقات (API) الخاصة بك والتي يمكنك إدارتها على مستوى الحزمة الفرعية بدلاً من مستوى الوحدة النمطية للحزمة الفرعية.إذا كنت تريد إضافة اسم جديد إلى واجهة برمجة التطبيقات (API)، فما عليك سوى تحديث ملف __init__.py, ، على سبيل المثال.في الوحدة_2:

from Bar_implementation import *
from Baz_implementation import *
__all__ = ['Bar', 'Baz']

وإذا لم تكن مستعدًا للنشر Baz في واجهة برمجة التطبيقات ذات المستوى الأعلى، في المستوى الأعلى لديك __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 بتصدير جميع الأسماء التي لا تبدأ بـ _.أنت بالتأكيد استطاع الاعتماد على هذه الآلية.في الواقع، بعض الحزم الموجودة في مكتبة بايثون القياسية، يفعل يعتمدون على هذا، ولكن للقيام بذلك، يقومون بتسمية وارداتهم، على سبيل المثال، في ctypes/__init__.py:

import os as _os, sys as _sys

باستخدام _ يمكن أن تكون الاتفاقية أكثر أناقة لأنها تزيل التكرار في تسمية الأسماء مرة أخرى.ولكنه يضيف التكرار للواردات (إذا كان لديك الكثير منها) وهو كذلك سهل أن تنسى القيام بذلك باستمرار - وآخر شيء تريده هو أن تدعم إلى أجل غير مسمى شيئًا كنت تنوي أن يكون مجرد تفاصيل تنفيذ، فقط لأنك نسيت البادئة _ عند تسمية وظيفة.

أنا شخصيا أكتب __all__ في وقت مبكر من دورة حياة تطوير الوحدات النمطية حتى يعرف الآخرون الذين قد يستخدمون الكود الخاص بي ما يجب عليهم استخدامه وما لا يستخدمونه.

تستخدم أيضًا معظم الحزم الموجودة في المكتبة القياسية __all__.

عند تجنب __all__ من المنطقي

فمن المنطقي التمسك بها _ اتفاقية البادئة بدلاً من __all__ متى:

  • أنت لا تزال في وضع التطوير المبكر وليس لديك أي مستخدمين، وتقوم باستمرار بتعديل واجهة برمجة التطبيقات (API) الخاصة بك.
  • ربما يكون لديك مستخدمون، ولكن لديك اختبارات وحدات تغطي واجهة برمجة التطبيقات، وما زلت تضيف بنشاط إلى واجهة برمجة التطبيقات وتقوم بالتعديل في التطوير.

ان export مصمم ديكور

الجانب السلبي للاستخدام __all__ هو أنه يتعين عليك كتابة أسماء الوظائف والفئات التي يتم تصديرها مرتين - ويتم الاحتفاظ بالمعلومات منفصلة عن التعريفات.نحن استطاع استخدم الديكور لحل هذه المشكلة.

خطرت لي فكرة مصمم الديكور التصديري هذا من حديث ديفيد بيزلي عن التغليف.يبدو أن هذا التنفيذ يعمل بشكل جيد في مستورد 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

كما أنه يغير ما سيظهره pydoc:

Module1.py

a = "A"
b = "B"
c = "C"

Module2.py

__all__ = ['a', 'b']

a = "A"
b = "B"
c = "C"

$ وحدة pydoc1

Help on module module1:

اسم
    module1

ملف
    module1.py

بيانات
    أ = 'A'
    ب = 'B'
    ج = 'C'

$ وحدة pydoc2

Help on module module2:

اسم
    module2

ملف
    module2.py

بيانات
    __الجميع__ = ['a', 'b']
    أ = 'A'
    ب = 'B'

أعلن __all__ في جميع وحداتي، بالإضافة إلى الشرطة السفلية على التفاصيل الداخلية، فهي تساعد حقًا عند استخدام الأشياء التي لم تستخدمها من قبل في جلسات الترجمة الفورية المباشرة.

من (غير رسمي) مرجع بيثون ويكي:

يتم تحديد الأسماء العامة التي تحددها الوحدة عن طريق التحقق من مساحة اسم الوحدة لمتغير مسمى __all__;إذا تم تعريفها، فيجب أن تكون عبارة عن سلسلة من السلاسل التي هي أسماء محددة أو مستوردة بواسطة تلك الوحدة.الأسماء الواردة في __all__ تعتبر جميعها عامة ويلزم وجودها.لو __all__ لم يتم تعريفها، فإن مجموعة الأسماء العامة تتضمن جميع الأسماء الموجودة في مساحة اسم الوحدة والتي لا تبدأ بحرف سفلي ("_"). __all__ يجب أن يحتوي على واجهة برمجة التطبيقات العامة بالكامل.الغرض منه هو تجنب تصدير العناصر التي لا تشكل جزءًا من واجهة برمجة التطبيقات (API) عن طريق الخطأ (مثل وحدات المكتبة التي تم استيرادها واستخدامها داخل الوحدة).

__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__ يتيح للبشر معرفة الميزات "العامة" لـ a وحدة.[@ آرون هول] كما يتعرف عليهم pydoc.[@ لونجبوك]

من وحدة يستورد *

أنظر كيف 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__ تخصيص * عند استيراد حرف البدل من الحزمة.[@ مارتن ستيتنر]

وهنا مقتطف من موصل بايثون 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 * يفعل لا استيراد كافة الوحدات الفرعية من الحزمة sound.effects في مساحة الاسم الحالية؛فهو يضمن فقط أن الحزمة sound.effects تم استيراده (ربما يقوم بتشغيل أي رمز تهيئة في __init__.py) ثم يقوم باستيراد أي أسماء محددة في الحزمة.يتضمن هذا أي أسماء محددة (والوحدات الفرعية التي تم تحميلها بشكل صريح) بواسطة __init__.py.ويتضمن أيضًا أي وحدات فرعية من الحزمة تم تحميلها بشكل صريح بواسطة عبارات الاستيراد السابقة.


واردات البدل...يجب تجنبها لأنها [تربك] القراء والعديد من الأدوات الآلية.

[بيب 8, @ToolmakerSteve]

اجابة قصيرة

__all__ يؤثر from <module> import * صياغات.

اجابة طويلة

خذ بعين الاعتبار هذا المثال:

foo
├── bar.py
└── __init__.py

في foo/__init__.py:

  • (ضمنيًا) إذا لم نحدد __all__, ، ثم from foo import * سيتم فقط استيراد الأسماء المحددة في foo/__init__.py.

  • (صريح) إذا حددنا __all__ = [], ، ثم from foo import * لن يستورد شيئا

  • (صريح) إذا حددنا __all__ = [ <name1>, ... ], ، ثم from foo import * سيتم استيراد تلك الأسماء فقط.

لاحظ أنه في الحالة الضمنية، لن تقوم بايثون باستيراد الأسماء التي تبدأ بـ _.ومع ذلك، يمكنك فرض استيراد هذه الأسماء باستخدام __all__.

يمكنك عرض مستند بايثون هنا.

__all__ يتم استخدامه لتوثيق واجهة برمجة التطبيقات العامة لوحدة بايثون.على الرغم من أنه اختياري، __all__ يجب أن تستخدم.

هنا المقتطف ذو الصلة من مرجع لغة بايثون:

يتم تحديد الأسماء العامة التي تحددها الوحدة عن طريق التحقق من مساحة اسم الوحدة لمتغير مسمى __all__;إذا تم تعريفها، فيجب أن تكون عبارة عن سلسلة من السلاسل التي هي أسماء محددة أو مستوردة بواسطة تلك الوحدة.الأسماء الواردة في __all__ تعتبر جميعها عامة ويلزم وجودها.لو __all__ لم يتم تعريفها، فإن مجموعة الأسماء العامة تتضمن جميع الأسماء الموجودة في مساحة اسم الوحدة والتي لا تبدأ بحرف سفلي ('_'). __all__ يجب أن يحتوي على واجهة برمجة التطبيقات العامة بالكامل.الغرض منه هو تجنب تصدير العناصر التي لا تشكل جزءًا من واجهة برمجة التطبيقات (API) عن طريق الخطأ (مثل وحدات المكتبة التي تم استيرادها واستخدامها داخل الوحدة).

بيب 8 يستخدم صياغة مماثلة، على الرغم من أنه يوضح أيضًا أن الأسماء المستوردة ليست جزءًا من واجهة برمجة التطبيقات العامة __all__ غائب:

لدعم الاستبطان بشكل أفضل، يجب أن تعلن الوحدات بشكل صريح عن الأسماء في واجهة برمجة التطبيقات العامة الخاصة بها باستخدام __all__ يصف.جلسة __all__ إلى قائمة فارغة يشير إلى أن الوحدة لا تحتوي على واجهة برمجة تطبيقات عامة.

[...]

يجب دائمًا اعتبار الأسماء المستوردة بمثابة تفاصيل تنفيذ.يجب ألا تعتمد الوحدات الأخرى على الوصول غير المباشر إلى هذه الأسماء المستوردة ما لم تكن جزءًا موثقًا بشكل صريح من واجهة برمجة التطبيقات (API) للوحدة التي تحتوي على، مثل os.path أو الحزمة __init__ الوحدة التي تعرض الوظائف من الوحدات الفرعية.

علاوة على ذلك ، كما هو موضح في الإجابات الأخرى ، __all__ يستخدم لتمكين استيراد أحرف البدل للحزم:

يستخدم بيان الاستيراد الاتفاقية التالية:إذا كانت الحزمة __init__.py يحدد الكود قائمة مسماة __all__, ، فهي تعتبر قائمة بأسماء الوحدات التي يجب استيرادها متى from package import * تمت مواجهته.

بالإضافة إلى الإجابات الموجودة، __all__ لا يجب أن تكون قائمة.حسب الوثائق الموجودة على import إفادة, ، إذا تم تعريفها، __all__ يجب أن يكون تسلسل السلاسل وهي أسماء محددة أو مستوردة بواسطة الوحدة.لذلك يمكنك أيضًا استخدام tuple to يحفظ بعض دورات الذاكرة ووحدة المعالجة المركزية.فقط لا تنسَ الفاصلة في حالة تعريف الوحدة لاسم عام واحد:

__all__ = ('some_name',)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top