python - اسم بيثون تتلوى




naming-conventions (6)

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

هل ينطبق الأمر نفسه على بايثون؟ هل يجب علي استخدام شرطيين سفليين رئيسيين في كل شيء في البداية ، وجعلهما أقل خفية (فقط شرطة سفلية واحدة) حسب حاجتي؟

إذا كانت الاتفاقية تستخدم تسطيرًا واحدًا فقط ، فإني أود أيضًا معرفة الأساس المنطقي.

هنا تعليق تركته على إجابة JBernardo . تشرح لماذا سألت هذا السؤال ، ولماذا أريد أن أعرف لماذا تختلف بايثون عن اللغات الأخرى:

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


أولا - ما هو اسم تشابك؟

يتم استدعاء __any_name mangling عندما تكون في تعريف فئة واستخدام __any_name أو __any_name_ ، أي ، __any_name_ (أو أكثر) بادئة وأهم تسطير __any_name_ واحد.

class Demo:
    __any_name = "__any_name"
    __any_other_name_ = "__any_other_name_"

و الأن:

>>> [n for n in dir(Demo) if 'any' in n]
['_Demo__any_name', '_Demo__any_other_name_']
>>> Demo._Demo__any_name
'__any_name'
>>> Demo._Demo__any_other_name_
'__any_other_name_'

عندما تكون في شك ، افعل ماذا؟

الاستخدام الظاهري هو منع الفئات الفرعية من استخدام سمة يستخدمها الفصل.

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

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

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

يجب أن تكون على دراية به حتى تعرفه عندما تراه.

PEP 8

PEP 8 ، دليل نمط مكتبة بايثون القياسي ، يقول حاليًا (مختصر):

هناك بعض الجدل حول استخدام __names .

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

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

  2. يمكن أن تقوم __getattr__() الاسم باستخدام بعض الاستخدامات ، مثل التصحيح و __getattr__() ، أقل ملاءمة. ومع ذلك ، فإن خوارزمية تشذيب الاسم موثقة بشكل جيد ويسهل تنفيذها يدويًا.

  3. ليس الجميع يحب اسم تشابك. حاول موازنة الحاجة إلى تجنب الصدامات غير المقصودة مع الاستخدام المحتمل بواسطة المتصلين المتقدمين.

كيف يعمل؟

إذا قمت بإرفاق شرطيين سفليين (بدون إنهاء الشرطات المزدوجة المزدوجة) في تعريف الفئة ، فسيتم تشويه الاسم ، وسيتم إرفاق تسطير أسفل يتبعه اسم الفئة على الكائن:

>>> class Foo(object):
...     __foobar = None
...     _foobaz = None
...     __fooquux__ = None
... 
>>> [name for name in dir(Foo) if 'foo' in name]
['_Foo__foobar', '__fooquux__', '_foobaz']

لاحظ أن الأسماء ستحصل فقط على mangled عند تحليل تعريف الفئة:

>>> Foo.__test = None
>>> Foo.__test
>>> Foo._Foo__test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute '_Foo__test'

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

واحد Underscore؟

إذا كانت الاتفاقية تستخدم تسطيرًا واحدًا فقط ، فإني أود أيضًا معرفة الأساس المنطقي.

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

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


أولاً: لماذا تريد إخفاء بياناتك؟ لماذا ذلك مهما جدا؟

معظم الوقت لا تريد فعل ذلك ولكنك تفعله لأن الآخرين يفعلون ذلك.

إذا كنت لا تريد حقًا أن يستخدم الأشخاص شيئًا ، فأضف تسطيرًا واحدًا أمامه. هذا هو ... Pythonistas نعرف أن الأمور مع واحد الشرطة السفلية ليست مضمونة للعمل في كل مرة ، ويمكن أن تتغير دون أن تعرف.

هذه هي الطريقة التي نعيش بها ونحن على ما يرام في ذلك.

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


سيوضح بعد مقتطف الشفرة جميع الحالات المختلفة:

  • شريحتين رئيسيتين (__a)
  • تسطير رئيسي واحد (_a)
  • لا تسطير (أ)

    class Test:
    
    def __init__(self):
        self.__a = 'test1'
        self._a = 'test2'
        self.a = 'test3'
    
    def change_value(self,value):
        self.__a = value
        return self.__a
    

طباعة جميع الصفات الصحيحة لكائن الاختبار

testObj1 = Test()
valid_attributes = dir(testObj1)
print valid_attributes

['_Test__a', '__doc__', '__init__', '__module__', '_a', 'a', 
'change_value']

هنا ، يمكنك أن ترى أن اسم __a قد تم تغييره إلى _Test__a لمنع هذا المتغير ليتم تجاوزه بواسطة أي فئة فرعية. يُعرف هذا المفهوم باسم "Name Mangling" في python. يمكنك الوصول إلى هذا مثل هذا:

testObj2 = Test()
print testObj2._Test__a

test1

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

testObj3 = Test()
print testObj3._a

test2

يمكن للمتغير أن يصل إلى أي مكان ، مثل المتغير العام.

testObj4 = Test()
print testObj4.a

test3

آمل أن الجواب ساعدك :)


عندما تكون في شك ، اتركها "عامة" - أعني ، لا تضيف أي شيء لإخفاء اسم صفتك. إذا كان لديك فصل ذي قيمة داخلية ، فلا تهتم به. بدلا من الكتابة:

class Stack(object):

    def __init__(self):
        self.__storage = [] # Too uptight

    def push(self, value):
        self.__storage.append(value)

اكتب هذا بشكل افتراضي:

class Stack(object):

    def __init__(self):
        self.storage = [] # No mangling

    def push(self, value):
        self.storage.append(value)

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

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

class Stack(object):

    def __init__(self):
        self._storage = [] # This is ok but pythonistas use to be relaxed about it

    def push(self, value):
        self._storage.append(value)

يمكن أن يكون هذا مفيدًا أيضًا ، لتجنب التعارض بين أسماء الخصائص وأسماء السمات:

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

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

إذا كنت ترغب في استخدامه لأغراض أخرى ، يمكنك ذلك ، ولكنها ليست معتادة ولا موصى بها.

تحرير : لماذا هذا صحيح؟ حسنا ، لا يركز أسلوب بايثون المعتاد على جعل الأشياء خاصة - على العكس! هناك الكثير من الأسباب لذلك - معظمها مثير للجدل ... دعنا نرى بعضًا منها.

بايثون لديها خصائص

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

ومع ذلك ، هذا ليس بهذه البساطة. على سبيل المثال ، تحتوي فئات Java على الكثير من السمات و getters التي تحصل فقط على القيم والمستقرات التي تقوم بتعيين القيم فقط. تحتاج ، دعنا نقول ، سبعة أسطر من الكود لإعلان خاصية واحدة - والتي يقولها مبرمج بايثون معقدة لا داعي لها. أيضا ، من الناحية العملية ، عليك فقط كتابة هذا الكود للحصول على حقل عام واحد ، حيث يمكنك تغيير قيمته باستخدام الـ getters and setters.

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

person.age = age;

في التعليمات البرمجية الخاصة بك ، دعنا نقول ،

person.setAge(age);

يجري setAge() :

public void setAge(int age) {
    if (age >= 0) {
        this.age = age;
    } else {
        this.age = 0;
    }
}

لذا في Java (واللغات الأخرى) ، يكون الإعداد الافتراضي هو استخدام getters و setters على أي حال ، لأنهم يمكن أن يكونوا مزعجين للكتابة ولكن يمكن أن يوفروا لك الكثير من الوقت إذا وجدت نفسك في الوضع الذي وصفته.

ومع ذلك ، لا تحتاج إلى القيام بذلك في Python ، لأن Python لها خصائص. إذا كان لديك هذا الفصل:

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self.age = age

ومن ثم تقرر التحقق من الأعمار ، لا تحتاج إلى تغيير الشخص. person.age = age من شفرتك. فقط قم بإضافة خاصية (كما هو موضح أدناه)

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

إذا كنت تستطيع فعل ذلك وما زلت تستخدم person.age = age ، فلماذا تضيف حقولًا خاصة وحاصلين؟

(أيضا ، انظر بايثون ليست جافا وهذه المقالة عن أضرار استخدام getters والمستقرون .).

كل شيء مرئي على أي حال - ومحاولة الاختباء يعقد فقط عملك

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

نظرًا لأن Python لغة ديناميكية للغاية ، فإنه من غير المجدي إضافة هذا العبء إلى صفوفك.

المشكلة ليست ممكنة أن نرى - هو مطلوب أن نرى

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

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

غيدو قال ذلك

حسنا ، هذا ليس مثيرا للجدل: قال ذلك ، في الواقع . (ابحث عن "كيمونو مفتوح".)

هذه ثقافة

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

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


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

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

استراتيجيتي في بيثون هي:

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

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

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


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

إذا نظرت إلى Java / C # ، فلكل منهما خاص / محمي / عام. كل هذه هي التركيبات وقت التجميع . يتم فرضها فقط في وقت التجميع. إذا كنت تستخدم الانعكاس في Java / C # ، فيمكنك الوصول إلى الطريقة الخاصة بسهولة.

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

lst = []
lst.append(1)
getattr(lst, 'append')(1)

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

لذلك ، لا يمكن إصدار نسخة جافا / C # خاصة ، نظرًا لأن بايثون لا تجمع الشفرة. لا يمكن لـ Java و C # التحقق مما إذا كانت إحدى الدوال خاصة أو عامة في وقت التشغيل ، نظرًا لأن هذه المعلومات قد اختفت (وليس لديها معرفة بمكان استدعاء الدالة من).

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

إخلاء المسؤولية: لم يسبق لي أن سمعت أحد من مؤسسة بايثون يقول أي شيء من هذا القبيل. السبب الحقيقي لعدم وجود "خاص" هو ثقافي ، ولكن ستلاحظ أيضًا أن معظم لغات البرمجة / الترجمة ليست خاصة. لا يمكن استخدام خاص قابل للتنفيذ بشكل صارم في أي شيء باستثناء وقت التحويل البرمجي.





naming-conventions