لماذا تتصرف مراجع السمة مثل هذا بميراث بيثون؟ [مكرر
-
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
في جميع الأوقات لأنه في مثالك ، لم تقم بتعيينه لهذا الاسم ولكن استخدمته للبحث عن قوله ثم تعيين عنصر (مفتاح ، قيمة) له. إنها مسكات هي نتيجة لكيفية عمل مترجم الثعبان ويمكن أن يكون مربكًا في البداية.