python - لماذا تحتوي tuples على عناصر قابلة للتغيير؟




list immutability (8)

إذا كانت المجموعة غير قابلة للتغيير ، فلماذا يمكن أن تحتوي على عناصر قابلة للتغيير؟

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


Answers

هذا سؤال ممتاز.

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

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

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

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

  1. تتميز الصفات الدموية بدرجة أقل من ثباتها وأكثر من غرضها المقصود.
    الصفوف هي طريقة بيثون لجمع أجزاء غير متجانسة من المعلومات تحت سقف واحد. على سبيل المثال ، s = ('www.python.org', 80) يجمع سلسلة ورقم بحيث يمكن تمرير زوج المضيف / المنفذ حول كمقبس ، كائن مركب. ينظر في ضوء ذلك ، فمن المعقول تماما أن يكون مكونات قابلة للتغيير.

  2. تسير الامتناع جنبا إلى جنب مع خاصية أخرى ، hashability . لكن هشابيليتي ليست خاصية مطلقة. إذا كان أحد مكونات tuple غير قابل للغسل ، فإن المجموعة الكلية غير قابلة للغسل أيضًا. على سبيل المثال ، t = ('red', [10, 20, 30]) غير قابل للغسل.

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

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

كان هذا مفيدًا لك :-)


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


لا يمكنك تغيير id عناصرها. لذلك سوف تحتوي دائما على نفس العناصر.

$ python
>>> t = (1, [2, 3])
>>> id(t[1])
12371368
>>> t[1].append(4)
>>> id(t[1])
12371368

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

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

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

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

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


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


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

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


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

  • ثبات Realio-trulio
  • الكتابة مرة واحدة
  • ثبات المصاصة
  • الضحلة مقابل ثبات عميق
  • واجهات غير قابلة للتغيير
  • ثبات الملاحظة

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

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


إذا كانت لديك قوائم ليست بنفس الطول ، فقد لا ترغب في استخدام الرمز البريدي وفقًا لإجابة Patricks. هذا يعمل:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)])
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]

ولكن مع وجود قوائم طول مختلفة ، يقوم الرمز البريدي باقتطاع كل عنصر إلى طول قائمة أقصر:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )])
[('a', 'b', 'c', 'd', 'e')]

يمكنك استخدام الخريطة بدون وظيفة لملء النتائج الفارغة بلا:

>>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )])
[('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)]

الرمز البريدي () بشكل هامشي أسرع على الرغم من.







python list tuples immutability