أساسيات بايثون 35: الفرق بين Class Attributes وInstance Attributes في Python للمبتدئين

الفرق بين class attributes وinstance attributes في Python للمبتدئين

في الدرس السابق تعلمت أن class هو قالب ننشئ منه كائنات objects، وأن كل كائن يستطيع امتلاك بياناته الخاصة باستخدام self. لكن عندما تبدأ بإنشاء أكثر من كائن، يظهر سؤال مهم: هل كل قيمة داخل class يجب أن تكون خاصة بكل كائن؟

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

هذا الدرس يكمل درس أساسيات بايثون 34: شرح class وobject في Python، وسيجعلك تكتب classes أكثر تنظيمًا وتتجنب أخطاء شائعة تظهر عند تغيير القيم المشتركة أو استخدام القوائم داخل class.

{alertInfo} الفكرة ببساطة: Instance Attribute هي قيمة تخص كائنًا واحدًا وتُنشأ غالبًا باستخدام self، مثل self.name. أما Class Attribute فهي قيمة مشتركة بين كل الكائنات وتُكتب مباشرة داخل class، مثل school_name.

{getToc} $title={محتوى المقال}

ما هي Attributes في Python؟

كلمة attribute تعني خاصية أو معلومة مرتبطة بكائن أو class. عندما تكتب student.name فإن name هي خاصية للكائن student. وقد تكون هذه الخاصية مخزنة داخل الكائن نفسه، أو موجودة على مستوى class ويستطيع الكائن الوصول إليها.

في هذا الدرس سنقسم الخصائص إلى نوعين:

  • Instance Attributes: بيانات فريدة تخص كل كائن.
  • Class Attributes: بيانات مشتركة بين كل الكائنات التابعة للكلاس نفسه.

مثال سريع: بيانات طالب

تخيل أننا نبني class تمثل طالبًا. الاسم والدرجة يختلفان من طالب إلى آخر، لكن اسم المدرسة واحد لجميع الطلاب.

class Student:
    school_name = "مدرسة بايثون العرب"

    def __init__(self, name, score):
        self.name = name
        self.score = score

student1 = Student("علي", 90)
student2 = Student("سارة", 95)

print(student1.name)
print(student2.name)
print(student1.school_name)
print(student2.school_name)

في هذا المثال:

  • name وscore خصائص خاصة بكل طالب.
  • school_name خاصية مشتركة بين كل الطلاب.
مقارنة بين class attributes وinstance attributes في Python باستخدام مثال الطالب

ما هي Instance Attributes؟

خصائص الكائن أو Instance Attributes هي البيانات التي تخص نسخة واحدة فقط من class. عادة ننشئها داخل __init__ باستخدام self.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

employee1 = Employee("أحمد", 800)
employee2 = Employee("مريم", 950)

employee1.salary = 1000

print(employee1.salary)
print(employee2.salary)

سيظهر:

1000
950

تغيير راتب employee1 لم يغير راتب employee2، لأن الراتب قيمة تخص كل كائن منفصل.

إنشاء instance attributes باستخدام self داخل init في Python

ما هي Class Attributes؟

خصائص class أو Class Attributes تكتب مباشرة داخل class وخارج الدوال. تكون متاحة لجميع الكائنات التي تنشئها من class نفسه.

مثال: كل منتجات متجر معين تستخدم نسبة ضريبة واحدة:

class Product:
    tax_rate = 0.15

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def price_after_tax(self):
        return self.price * (1 + self.tax_rate)

book = Product("كتاب Python", 20)
course = Product("دورة Python", 50)

print(book.price_after_tax())
print(course.price_after_tax())

لا نكتب self.tax_rate = 0.15 هنا لأننا لا نحتاج نسخة مستقلة من الضريبة لكل منتج. نحتاج قيمة واحدة مشتركة بين كل المنتجات.

كيف يصل الكائن إلى Class Attribute؟

يمكنك الوصول إلى class attribute بطريقتين:

print(Product.tax_rate)
print(book.tax_rate)

كلتا الطريقتين تعملان، لكن الأفضل عند القراءة والتعديل هو استخدام ClassName.attribute إذا كنت تريد توضيح أن القيمة تخص class كله. أما داخل methods فيمكنك الوصول إليها عبر self.attribute.

عندما لا تجد Python الخاصية داخل الكائن نفسه، تبحث عنها في class. لهذا يستطيع book.tax_rate الوصول إلى Product.tax_rate.

خاصية class مشتركة بين عدة objects في Python

ماذا يحدث عند تعديل Class Attribute؟

إذا عدلت الخاصية من خلال class نفسه، ستتغير القيمة التي يصل إليها كل كائن لا يملك نسخة خاصة من الاسم نفسه.

class Student:
    school_name = "مدرسة بايثون العرب"

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

student1 = Student("علي")
student2 = Student("سارة")

Student.school_name = "أكاديمية بايثون العرب"

print(student1.school_name)
print(student2.school_name)

الناتج:

أكاديمية بايثون العرب
أكاديمية بايثون العرب

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

فرق مهم: التعديل من class والتعديل من object

هذه النقطة تسبب ارتباكًا عند المبتدئين. انظر إلى المثال التالي:

class Student:
    school_name = "مدرسة بايثون العرب"

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

student1 = Student("علي")
student2 = Student("سارة")

student1.school_name = "مدرسة خاصة بعلي"

print(student1.school_name)
print(student2.school_name)
print(Student.school_name)

الناتج:

مدرسة خاصة بعلي
مدرسة بايثون العرب
مدرسة بايثون العرب

لم تتغير الخاصية المشتركة الأصلية. عند كتابة student1.school_name = ... أنشأت Python instance attribute جديدة داخل student1 بالاسم نفسه، فأصبحت تخفي class attribute عند استخدام هذا الكائن.

{alertWarning} معلومة مهمة: عند كتابة object.attribute = value باسم يطابق class attribute، فأنت غالبًا تنشئ قيمة خاصة لهذا الكائن. لتغيير القيمة للجميع، استخدم ClassName.attribute = value.

فحص خصائص الكائن وclass باستخدام __dict__

للتعلم فقط، تستطيع رؤية أين خزنت Python الخصائص باستخدام __dict__:

class Student:
    school_name = "مدرسة بايثون العرب"

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

student = Student("علي")

print(Student.__dict__)
print(student.__dict__)

ستجد أن Student.__dict__ يحتوي على school_name والدوال، بينما student.__dict__ يحتوي على name لأنه خاص بهذا الكائن.

خطأ شائع جدًا: وضع List قابلة للتعديل كـ Class Attribute

القيم القابلة للتعديل مثل list وdict تحتاج انتباهًا. عندما تضع قائمة في class attribute، تصبح القائمة نفسها مشتركة بين كل الكائنات.

class Student:
    courses = []

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

student1 = Student("علي")
student2 = Student("سارة")

student1.courses.append("Python")

print(student1.courses)
print(student2.courses)

قد تتفاجأ أن الناتج يظهر المادة عند الطالبين، لأنهما يشتركان في القائمة نفسها:

['Python']
['Python']

الحل الصحيح هو إنشاء قائمة مستقلة لكل طالب داخل __init__:

class Student:
    def __init__(self, name):
        self.name = name
        self.courses = []

student1 = Student("علي")
student2 = Student("سارة")

student1.courses.append("Python")

print(student1.courses)
print(student2.courses)
['Python']
[]
خطأ شائع عند استخدام list كـ class attribute في Python

مثال عملي كامل: نظام طلاب بسيط

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

class Student:
    academy_name = "أكاديمية بايثون العرب"
    passing_score = 60

    def __init__(self, name, score):
        self.name = name
        self.score = score
        self.lessons = []

    def add_lesson(self, lesson_name):
        self.lessons.append(lesson_name)

    def is_passed(self):
        return self.score >= self.passing_score

    def show_info(self):
        status = "ناجح" if self.is_passed() else "يحتاج مراجعة"
        print(f"الأكاديمية: {self.academy_name}")
        print(f"الطالب: {self.name}")
        print(f"الدرجة: {self.score}")
        print(f"الحالة: {status}")
        print(f"الدروس: {', '.join(self.lessons)}")

student1 = Student("علي", 85)
student2 = Student("سارة", 55)

student1.add_lesson("أساسيات بايثون")
student1.add_lesson("القوائم")
student2.add_lesson("الشروط")

student1.show_info()
print("-" * 30)
student2.show_info()

في هذا المشروع الصغير:

  • academy_name وpassing_score هما class attributes.
  • name وscore وlessons هي instance attributes.
  • كل طالب يملك قائمة دروس مستقلة لأنها أنشئت عبر self.lessons = [].

متى تستخدم Class Attribute ومتى تستخدم Instance Attribute؟

استخدم Class Attribute عندما...استخدم Instance Attribute عندما...
القيمة يجب أن تكون مشتركة بين كل الكائنات.القيمة تختلف من كائن إلى آخر.
القيمة تمثل إعدادًا عامًا مثل نسبة ضريبة أو اسم مؤسسة.القيمة تمثل حالة أو بيانات خاصة مثل اسم أو سعر أو درجة.
تريد تغيير القيمة للجميع من مكان واحد.تريد تعديل القيمة لكائن واحد دون التأثير على الآخرين.
القيمة ثابتة أو غير قابلة للتعديل غالبًا.القيمة قائمة أو قاموس أو بيانات قابلة للتعديل لكل كائن.

أخطاء شائعة يجب تجنبها

1. وضع بيانات خاصة في Class Attribute

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

2. تعديل Class Attribute من object دون قصد

تذكر أن student.school_name = "..." ينشئ غالبًا نسخة خاصة بهذا الطالب. استخدم Student.school_name = "..." إذا كنت تريد التغيير للجميع.

3. استخدام قائمة أو قاموس مشترك لكل الكائنات

إذا كانت القائمة يجب أن تكون مستقلة لكل كائن، أنشئها داخل __init__ باستخدام self.

4. استخدام Class Attribute لكل شيء

Class attributes مفيدة، لكنها ليست بديلًا عن instance attributes. اسأل دائمًا: هل هذه القيمة تخص كل الكائنات أم هذا الكائن فقط؟

تمرين عملي

أنشئ class باسم Book تحقق الشروط التالية:

  • لديها class attribute باسم library_name وقيمتها "مكتبة بايثون العرب".
  • لديها instance attributes باسم title وauthor.
  • أنشئ كتابين مختلفين واطبع بياناتهما.
  • غيّر library_name من خلال class ثم اطبعها لكلا الكتابين.

حل مقترح

class Book:
    library_name = "مكتبة بايثون العرب"

    def __init__(self, title, author):
        self.title = title
        self.author = author

    def show_info(self):
        print(f"المكتبة: {self.library_name}")
        print(f"العنوان: {self.title}")
        print(f"المؤلف: {self.author}")

book1 = Book("تعلم Python", "إيليا علي")
book2 = Book("مشاريع Python", "فريق بايثون العرب")
Book.library_name = "مكتبة بايثون العرب الرقمية"

book1.show_info()
print("-" * 25)
book2.show_info()

روابط داخلية مفيدة من بايثون العرب

الخلاصة

  • Instance Attributes تخص كائنًا واحدًا، وتُنشأ غالبًا باستخدام self داخل __init__.
  • Class Attributes تكتب داخل class وخارج methods، وتكون مشتركة بين كل الكائنات.
  • غيّر class attribute عبر ClassName.attribute عندما تريد تطبيق التغيير على الجميع.
  • عند التعديل عبر object.attribute قد تنشئ قيمة خاصة بالكائن وتخفي القيمة المشتركة.
  • لا تستخدم list أو dict كـ class attribute عندما تريد بيانات مستقلة لكل object.
{alertSuccess} قاعدة ذهبية: اسأل قبل إنشاء أي attribute: هل هذه القيمة مشتركة بين جميع الكائنات؟ ضعها داخل class. هل تختلف من كائن إلى آخر؟ أنشئها باستخدام self داخل __init__.

مصادر خارجية رسمية للتوسع

أسئلة شائعة

ما الفرق بين class attribute وinstance attribute في Python؟

Class attribute قيمة مشتركة بين جميع الكائنات التابعة لنفس class. أما instance attribute فهي قيمة خاصة بكل كائن، مثل اسم الطالب أو درجته.

هل يمكن الوصول إلى class attribute من خلال object؟

نعم، مثل student.school_name. لكن من الأفضل استخدام Student.school_name عندما تريد توضيح أن الخاصية مشتركة وتابعة للكلاس كله.

لماذا لم تتغير القيمة عند تعديلها من object واحد؟

لأن كتابة student.school_name = "..." قد تنشئ instance attribute جديدة لهذا الكائن بدل تعديل class attribute الأصلية.

هل أضع list داخل class أم داخل init؟

إذا كانت كل نسخة تحتاج قائمة مستقلة، ضعها داخل __init__ كالتالي: self.items = []. وضعها مباشرة داخل class يجعل القائمة مشتركة بين جميع الكائنات.

هل class attributes مناسبة للقيم الثابتة فقط؟

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

إرسال تعليق

أحدث أقدم