لماذا تتصرف مراجع السمة مثل هذا بميراث بيثون؟ [مكرر

StackOverflow https://stackoverflow.com/questions/206734

  •  03-07-2019
  •  | 
  •  

سؤال

هذا السؤال لديه بالفعل إجابة هنا:

ما يلي يبدو غريبًا .. في الأساس ، يبدو أن سمة يوما ما مشتركة بين جميع الفئات الموروثة من the_base_class.

class the_base_class:
    somedata = {}
    somedata['was_false_in_base'] = False


class subclassthing(the_base_class):
    def __init__(self):
            print self.somedata


first = subclassthing()
{'was_false_in_base': False}
first.somedata['was_false_in_base'] = True
second = subclassthing()
{'was_false_in_base': True}
>>> del first
>>> del second
>>> third = subclassthing()
{'was_false_in_base': True}

تعريف self.somedata في ال __init__ من الواضح أن الوظيفة هي الطريقة الصحيحة للالتفاف على هذا (لذلك كل فصل لها somedata DICT) - ولكن متى يكون مثل هذا السلوك مرغوبًا فيه؟

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

المحلول

أنت محق، somedata تتم مشاركتها بين جميع حالات الفصل والفئات الفرعية ، لأنه تم إنشاؤه في الفصل تعريف زمن. الخطوط

somedata = {}
somedata['was_false_in_base'] = False

يتم تنفيذها عند تعريف الفصل ، أي عندما يواجه المترجم المترجم class بيان - ليس عند إنشاء المثيل (فكر في كتل التهيئة الثابتة في Java). إذا لم تكن السمة موجودة في مثيل الفئة ، فسيتم فحص كائن الفئة للسمة.

في وقت تعريف الفصل ، يمكنك تشغيل رمز Arbritrary ، مثل هذا:

 import sys
 class Test(object):
     if sys.platform == "linux2":
         def hello(self):
              print "Hello Linux"
     else:
         def hello(self):
              print "Hello ~Linux"

على نظام Linux ، Test().hello() سوف تطبع Hello Linux, ، على جميع الأنظمة الأخرى ، سيتم طباعة السلسلة الأخرى.

في حدة ، الكائنات في __init__ يتم إنشاؤها في مثيل الوقت والانتماء إلى المثيل فقط (عندما يتم تعيينهم self):

class Test(object):
    def __init__(self):
        self.inst_var = [1, 2, 3]

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

class SomeClass(object):
    __instances__ = {}

    def __new__(cls, v1, v2, v3):
        try:
            return cls.__insts__[(v1, v2, v3)]
        except KeyError:
            return cls.__insts__.setdefault(
               (v1, v2, v3), 
               object.__new__(cls, v1, v2, v3))

في الغالب ، أستخدم البيانات في الأجسام الصفية بالتزامن مع metaclasses أو طرق المصنع العامة.

نصائح أخرى

لاحظ أن جزءًا من السلوك الذي تراه بسبب somedata كونه dict, ، على عكس نوع البيانات البسيط مثل أ bool.

على سبيل المثال ، راجع هذا المثال المختلفة الذي يتصرف بشكل مختلف (على الرغم من أنه متشابه جدًا):

class the_base_class:
    somedata = False

class subclassthing(the_base_class):
    def __init__(self):
        print self.somedata


>>> first = subclassthing()
False
>>> first.somedata = True
>>> print first.somedata
True
>>> second = subclassthing()
False
>>> print first.somedata
True
>>> del first
>>> del second
>>> third = subclassthing()
False

السبب في أن هذا المثال يتصرف بشكل مختلف عن هذا المثال الوارد في السؤال هو أنه هنا first.somedata يتم إعطاؤه قيمة جديدة (الكائن True) ، بينما في المثال الأول كائن DICT المشار إليه بواسطة first.somedata (وكذلك بواسطة مثيلات الفئة الفرعية الأخرى) يتم تعديلها.

انظر تعليق Torsten Marek على هذه الإجابة لمزيد من التوضيح.

أعتقد أن أسهل طريقة لفهم هذا (حتى تتمكن من التنبؤ بالسلوك) هو إدراك أن somedata هي سمة من الفئة وليس مثيل تلك الفئة إذا حددتها بهذه الطريقة.

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

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