في الدرس السابق تعلمت أن 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خاصية مشتركة بين كل الطلاب.
ما هي 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، لأن الراتب قيمة تخص كل كائن منفصل.
ما هي 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 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']
[]
مثال عملي كامل: نظام طلاب بسيط
المثال التالي يجمع المفهومين معًا. اسم الأكاديمية والحد الأدنى للنجاح مشتركان، بينما الاسم والدرجة والدروس المسجلة تخص كل طالب وحده.
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()
روابط داخلية مفيدة من بايثون العرب
- أساسيات بايثون 34: شرح class وobject في Python للمبتدئين
- شرح القوائم List في Python للمبتدئين
- كورس أساسيات بايثون للمبتدئين
الخلاصة
Instance Attributesتخص كائنًا واحدًا، وتُنشأ غالبًا باستخدامselfداخل__init__.Class Attributesتكتب داخل class وخارج methods، وتكون مشتركة بين كل الكائنات.- غيّر class attribute عبر
ClassName.attributeعندما تريد تطبيق التغيير على الجميع. - عند التعديل عبر
object.attributeقد تنشئ قيمة خاصة بالكائن وتخفي القيمة المشتركة. - لا تستخدم
listأوdictكـ class attribute عندما تريد بيانات مستقلة لكل object.
{alertSuccess} قاعدة ذهبية: اسأل قبل إنشاء أي attribute: هل هذه القيمة مشتركة بين جميع الكائنات؟ ضعها داخلclass. هل تختلف من كائن إلى آخر؟ أنشئها باستخدامselfداخل__init__.
مصادر خارجية رسمية للتوسع
- Classes — Python Tutorial
- Class definitions — Python Language Reference
- Class variable — Python Glossary
أسئلة شائعة
ما الفرق بين 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 مناسبة للقيم الثابتة فقط؟
هي مناسبة للقيم المشتركة عمومًا، سواء كانت ثابتة أو يمكن تغييرها للجميع مثل نسبة ضريبة أو عدد الكائنات. المهم ألا تكون بيانات خاصة بكائن واحد.



