python - ما معنى الشارة السفلية المزدوجة والفرعية المزدوجة قبل اسم الكائن؟




naming-conventions private underscores double-underscore (12)

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


Answers

إجابات ممتازة حتى الآن لكن بعض الحكايات مفقودة. لا يمثل التسطير التسطير الرئيسي الوحيد بالضبط تمامًا : إذا كنت تستخدم from foobar import * ، و foobar الوحدة لا تحدد قائمة __all__ ، فإن الأسماء المستوردة من الوحدة لا تتضمن تلك التي تحتوي على __all__ سفلية رائدة. دعنا نقول إنه في الغالب اتفاقية ، لأن هذه الحالة هي ركن غامض جدًا ؛-).

يتم استخدام الاتفاقية القيادية السفلية على نطاق واسع ليس فقط للأسماء الخاصة ، ولكن أيضًا لما يطلق عليه C ++ أسماء المحميات - على سبيل المثال ، أسماء الطرق التي يُقصد بها بشكل كامل أن يتم تجاوزها بواسطة الفئات الفرعية (حتى تلك التي يجب تجاوزها منذ الفئة الأساسية التي raise NotImplementedError ! -) غالبًا ما تكون أسماء فردية-تسطير سفلية واحدة للإشارة إلى التعليمة البرمجية باستخدام مثيلات هذه الفئة (أو الفئات الفرعية) التي لا يقصد بها أن تسمى الأساليب مباشرة.

على سبيل المثال ، لإنشاء قائمة انتظار مؤمدة لسلسلة محادثات مع نظام فرعي آخر في قائمة انتظار من FIFO ، قائمة انتظار استيراد واحدة ، فئة فرعية Queue.Queue ، وتجاوز طرق مثل _get و _put ؛ لا يطلق "رمز العميل" مطلقًا تلك الطرق ("هوك") ، بل على الأساليب العامة ("التنظيمية") مثل put get (يُعرف هذا باسم نمط تصميم نموذج القالب - انظر على سبيل المثال here لعرض مثير للاهتمام يستند إلى فيديو من حديث عني في هذا الموضوع ، مع إضافة ملخصات النص).


في بعض الأحيان يكون لديك ما يبدو أنه صف مع شرطة سفلية رئيسية كما في

def foo(bar):
    return _('my_' + bar)

في هذه الحالة ، ما يجري هو أن _ () هو اسم مستعار لوظيفة الترجمة التي تعمل على النص لوضعها في اللغة المناسبة ، إلخ. استناداً إلى الإعدادات المحلية. على سبيل المثال ، يفعل Sphinx هذا ، وستجد من بين الواردات

from sphinx.locale import l_, _

وفي sphinx.locale ، يتم تعيين _ () كاسم مستعار لبعض وظيفة الترجمة.


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

مرجع https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references


سؤالك جيد ، ليس فقط حول الطرق. عادةً ما تكون بادئات وكائنات في الوحدات النمطية مسبوقة بعلامة تسطير سفلية واحدة أيضًا ، ويمكن أن يسبقها حرفان.

ولكن __double_underscore لا يتم تسمية الأسماء في الوحدات النمطية ، على سبيل المثال. ما يحدث هو أن الأسماء التي تبدأ بشُرط سفلية واحدة (أو أكثر) لا يتم استيرادها إذا قمت باستيراد الكل من وحدة نمطية (من استيراد الوحدة النمطية *) ، ولا هي الأسماء المعروضة في التعليمات (الوحدة النمطية).


تسطير واحد في البداية:

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

class BaseForm(StrAndUnicode):

    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

    errors = property(_get_errors)

مقتطف شفرة مأخوذة من شفرة المصدر django (django / forms / forms.py). وهذا يعني أن الأخطاء هي خاصية ، وهي جزء من الوحدة ، ولكن الطريقة التي تستدعيها هذه الخاصية ، _get_errors ، هي "خاصة" ، لذلك لا يجب عليك الوصول إليها.

نقطتان سفليتان في البداية:

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

class A(object):
    def __test(self):
        print "I'm test method in class A"

    def test(self):
        self.__test()

a = A()
a.test()

انتاج:

$ python test.py
I'm test method in class A

الآن أنشئ فئة فرعية B وقم بعمل التخصيص لأسلوب __test

class B(A):
    def __test(self):
        print "I'm test method in class B"

b = B()
b.test()

سيكون الناتج ....

$ python test.py
I'm test method in class A

كما رأينا ، لم يستعمل A.test () أساليب B .__ test () ، كما قد نتوقع. ولكن في الحقيقة ، هذا هو السلوك الصحيح لـ __. لذلك عندما تنشئ طريقة تبدأ بـ __ ، فهذا يعني أنك لا تريد أن يتمكن أي شخص من تجاوزها ، فسيكون الوصول إليها من داخل الصف الخاص فقط.

وحدتا سفلية في البداية وفي النهاية:

عندما نرى طريقة مثل __this__ ، لا تسميها. لأنه يعني أنها طريقة تدعوها بيثون ، وليس من قبلك. لنلقي نظرة:

>>> name = "test string"
>>> name.__len__()
11
>>> len(name)
11

>>> number = 10
>>> number.__add__(40)
50
>>> number + 50
60

هناك دائما مشغل أو وظيفة الأم التي تدعو هذه الأساليب السحرية. في بعض الأحيان انها مجرد دعوة بيثون هوك في حالات محددة. على سبيل المثال يتم __init__() عند إنشاء الكائن بعد __new__() لإنشاء المثيل ...

لنأخذ مثالا ...

class FalseCalculator(object):

    def __init__(self, number):
        self.number = number

    def __add__(self, number):
        return self.number - number

    def __sub__(self, number):
        return self.number + number



number = FalseCalculator(20)
print number + 10      # 10
print number - 20      # 40

لمزيد من التفاصيل ، يساعد دليل PEP-8 أكثر.

يرجى الاطلاع على المزيد من الطرق السحرية في python هنا. https://github.com/RafeKettler/magicmethods/blob/master/magicmethods.pdf


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

يتم استخدام __bool__ المزدوجة والرائدة المزدوجة للأساليب المدمجة ، مثل __init__ ، __bool__ ، إلخ.

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


واحد سفلي

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

PEP-8 إلى PEP-8 :

_single_leading_underscore: ضعيف مؤشر "الاستخدام الداخلي". على سبيل المثال ، from M import * لا يقوم باستيراد الكائنات التي يبدأ اسمها بشرطة سفلية.

شرطة سفلية مزدوجة (اسم Mangling)

من مستندات Python :

يتم __spam أي معرف للنموذج __spam (على الأقل __spam على الأقل واحد أسفل السطر السفلي) مع _classname__spam ، حيث يكون _classname__spam هو اسم الفئة الحالي مع تسطير أسفل السطر ( _classname__spam ). تتم هذه العملية دون النظر إلى الموضع النحوي للمُعرِّف ، بحيث يمكن استخدامها لتعريف متغيرات الطبقة والصف والمتغيرات الطبقية ، والطرق ، والمتغيرات المخزنة في globals ، وحتى المتغيرات المخزنة في الحالات. خاصة لهذه الفئة في حالات الطبقات الأخرى.

وتحذير من نفس الصفحة:

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

مثال

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}

في ما يلي مثال توضيحي بسيط حول كيفية تأثير خصائص الشرطة السفلية المزدوجة على فئة موروثة. لذلك مع الإعداد التالي:

class parent(object):
    __default = "parent"
    def __init__(self, name=None):
        self.default = name or self.__default

    @property
    def default(self):
        return self.__default

    @default.setter
    def default(self, value):
        self.__default = value


class child(parent):
    __default = "child"

إذا قمت بإنشاء مثيل تابع في PIP ثعبان ، سترى أدناه

child_a = child()
child_a.default            # 'parent'
child_a._child__default    # 'child'
child_a._parent__default   # 'parent'

child_b = child("orphan")
## this will show 
child_b.default            # 'orphan'
child_a._child__default    # 'child'
child_a._parent__default   # 'orphan'

قد يكون هذا واضحًا بالنسبة للبعض ، لكنه أمسك بي على حين غرة في بيئة أكثر تعقيدًا


__foo__ : هذه مجرد اتفاقية ، وهي طريقة يستخدمها نظام بايثون لاستخدام أسماء لا تتعارض مع أسماء المستخدمين.

_foo : هذه مجرد اتفاقية ، طريقة للمبرمج للإشارة إلى أن المتغير خاص (أيًا كان معنى ذلك في Python).

__foo : هذا له معنى حقيقي: يستبدل المترجم هذا الاسم بـ _classname__foo كطريقة لضمان عدم تداخل الاسم مع اسم مشابه في فصل آخر.

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

لا يوجد فرق بين الفئة ، والمتغير ، والعالمي ، وما إلى ذلك في هذه الاتفاقيات.


._variable هو ._variable والمقصود فقط من أجل الاتفاقية

غالبًا ما يعتبر .__variable بشكل غير صحيح أمرًا استثنائيًا ، بينما يكون المعنى الفعلي للنمنع فقط لمنع الوصول غير المقصود [1]

يتم الاحتفاظ عادةً .____ variable__ لأساليب أو متغيرات builtin

لا يزال بإمكانك الوصول إلى .__mangled المتغيرات .__mangled إذا كنت ترغب بشدة في ذلك. يؤكد السطر المزدوج فقط على أسماء nememangles أو إعادة تسمية المتغير إلى شيء مثل example._className__mangled

مثال:

class Test(object):
    def __init__(self):
        self.__a = 'a'
        self._b = 'b'

>>> t = Test()
>>> t._b
'b'

t._b يمكن الوصول إليه لأنه مخفي فقط بموجب الاتفاقية

>>> t.__a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'

لم يتم العثور على t. لأنه لم يعد موجودًا بسبب namemangling

>>> t._Test__a
'a'

من خلال الوصول إلى instance._className__variable بدلاً من اسم الشرطة السفلية مزدوجة فقط ، يمكنك الوصول إلى القيمة المخفية


الحصول على الحقائق من _ و __ هو سهل جدا. الإجابات الأخرى تعبر عنهم بشكل جيد. من الصعب جدا تحديد الاستخدام.

هكذا أراها:

_

يجب أن تستخدم للإشارة إلى أن الوظيفة ليست للاستخدام العام على سبيل المثال API. يؤدي هذا الأمر وتقييد الاستيراد إلى جعله يتصرف تمامًا كما هو الحال في c #.

__

يجب أن تستخدم لتجنب تضارب الأسماء في التسلسل الهرمي لتوريث وتجنب الربط المتأخر. مثل الكثير من القطاع الخاص في ج #.

==>

إذا كنت تريد الإشارة إلى أن شيئًا ما ليس للاستخدام العام ، فيجب أن يتصرف مثل الاستخدام protected _ . إذا كنت تريد الإشارة إلى أن شيئًا ما ليس للاستخدام العام ، فيجب أن يتصرف مثل الاستخدام private __ .

هذا هو الاقتباس الذي يعجبني كثيرا:

تكمن المشكلة في أن مؤلف فئة ما قد يفكر بشكل شرعي "يجب أن يكون اسم السمة / الطريقة هذا خاصًا ، ولا يمكن الوصول إليه إلا من خلال تعريف الفئة هذا" واستخدم اصطلاح __Private. ولكن في وقت لاحق ، قد يقوم مستخدم تلك الفئة بعمل فئة فرعية تحتاج بشكل شرعي إلى الوصول إلى هذا الاسم. لذا ، يجب تعديل الطبقة الفائقة (التي قد تكون صعبة أو مستحيلة) ، أو يجب أن تستخدم شفرة الفئة الفرعية أسماءًا متشابكة يدوياً (وهي قبيحة وهشة في أحسن الأحوال).

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


في حين تم expanded الاستخدامات لمشغلي النجمة / الملحوظة في Python 3 ، فأنا أحب الجدول التالي لأنه يتعلق باستخدام هذه المشغلات مع الوظائف . يمكن استخدام مشغّل splat (مشغلات splat) داخل بناء الوظائف وفي استدعاء الدالة:

            In function *construction*      In function *call*
=======================================================================
          |  def f(*args):                 |  def f(a, b):
*args     |      for arg in args:          |      return a + b
          |          print(arg)            |  args = (1, 2)
          |  f(1, 2)                       |  f(*args)
----------|--------------------------------|---------------------------
          |  def f(a, b):                  |  def f(a, b):
**kwargs  |      return a + b              |      return a + b
          |  def g(**kwargs):              |  kwargs = dict(a=1, b=2)
          |      return f(**kwargs)        |  f(**kwargs)
          |  g(a=1, b=2)                   |
-----------------------------------------------------------------------

هذا حقا يخدم فقط لتلخيص answer لورين Hochstein ولكن أجد أنه من المفيد.





python naming-conventions private underscores double-underscore