الأعضاء العامة والمحمية والخاصة
جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 28 من 64.
تمتلك بايثون ثلاثة مستويات للتحكم في الوصول لأعضاء الفئة: العام (public)، والمحمي (protected)، والخاص (private). تتحكم هذه المستويات في كيفية الوصول إلى السمات (attributes) والطرق (methods).
إليك مثال على فئة (class) تحتوي على جميع مستويات الوصول الثلاثة:
class BankAccount:
def __init__(self, owner, balance, account_id):
self.owner = owner # عام - يمكن الوصول إليه من أي مكان
self._balance = balance # محمي - للاستخدام الداخلي
self.__account_id = account_id # خاص - للفئة فقط
def deposit(self, amount): # دالة عامة
self._balance += amount
def _calculate_interest(self): # دالة محمية
return self._balance * 0.02
def __validate_transaction(self, amount): # دالة خاصة
return amount > 0 and amount <= self._balanceالوصول إلى الأعضاء العامين من أي مكان:
account = BankAccount("Alice", 1000, "12345")
print(account.owner) # Alice
account.deposit(500) # يعمل بشكل جيدالوصول إلى الأعضاء المحميين (شرطة سفلية واحدة - مجرد عرف فقط):
print(account._balance) # 1500 - يعمل ولكن لا يُنصح به
result = account._calculate_interest() # يعمل ولكن لا يُنصح به
print(result) # 30.0حاول الوصول إلى الأعضاء الخاصين (شرطة سفلية مزدوجة - تم تشويه الاسم):
# هذا لن يعمل:
# print(account.__account_id) # AttributeError
# لكن هذا يعمل (تشويه الاسم):
print(account._BankAccount__account_id) # 12345أنشئ فئة فرعية (subclass) لتوضيح الوصول المحمي (protected) مقابل الوصول الخاص (private):
class SavingsAccount(BankAccount):
def show_balance(self):
return self._balance # محمي - يمكن الوصول إليه في الفئة الفرعية
def show_id(self):
# return self.__account_id # لن يعمل هذا - خاص
return "Cannot access private member"savings = SavingsAccount("Bob", 2000, "67890")
print(savings.show_balance()) # 2000
print(savings.show_id()) # لا يمكن الوصول إلى العضو الخاصالمخرجات:
Alice
30.0
12345
2000
Cannot access private memberنقطة رئيسية: الأعضاء العامون (Public members) ليس لديهم بادئة ويمكن الوصول إليهم من أي مكان. الأعضاء المحميون (Protected members) يستخدمون شرطة سفلية واحدة (_) ويجب استخدامهم فقط داخل التسلسل الهرمي للفئات. الأعضاء الخاصون (Private members) يستخدمون شرطة سفلية مزدوجة (__) ويتم تعديل أسمائهم (name-mangled) لخصوصية أقوى. التحكم في الوصول في Python يعتمد على العرف، وليس مفروضاً بشكل صارم.
التحدي
متوسطفي هذا التحدي، ستقوم بتنفيذ فئة BankAccount التي توضح الاستخدام الصحيح لمستويات الوصول العامة (public)، والمحمية (protected)، والخاصة (private) في Python.
bankaccount.py- يحتوي على تعريف الفئة مع تعليقات TODO لإرشادك خلال التنفيذ
- اتبع تعليقات TODO في
bankaccount.pyلتنفيذ الوظائف المطلوبة - قم بتنفيذ مستويات الوصول المناسبة (public، protected، private) كما هو محدد
- تأكد من أن جميع الطرق (methods) تتعامل مع الحالات الاستثنائية بشكل مناسب (الإيداعات السالبة، وما إلى ذلك)
ورقة مرجعية
تمتلك لغة بايثون ثلاثة مستويات للتحكم في الوصول لأعضاء الفئة (class members):
- Public (عام): بدون بادئة، يمكن الوصول إليه من أي مكان
- Protected (محمي): شرطة سفلية واحدة (
_)، للاستخدام الداخلي ضمن التسلسل الهرمي للفئات - Private (خاص): شرطة سفلية مزدوجة (
__)، يتم تغيير الاسم (name-mangled) للوصول من داخل الفئة فقط
class BankAccount:
def __init__(self, owner, balance, account_id):
self.owner = owner # عام
self._balance = balance # محمي
self.__account_id = account_id # خاص
def deposit(self, amount): # طريقة عامة
self._balance += amount
def _calculate_interest(self): # طريقة محمية
return self._balance * 0.02
def __validate_transaction(self, amount): # طريقة خاصة
return amount > 0 and amount <= self._balanceالوصول إلى الأعضاء:
account = BankAccount("Alice", 1000, "12345")
# وصول عام
print(account.owner) # يعمل بشكل جيد
# وصول محمي (يعمل ولكن لا يُنصح به)
print(account._balance) # 1500
# وصول خاص (تم تغيير الاسم)
print(account._BankAccount__account_id) # 12345في الفئات الفرعية (subclasses):
class SavingsAccount(BankAccount):
def show_balance(self):
return self._balance # محمي - يمكن الوصول إليه
def show_id(self):
# return self.__account_id # خاص - لا يمكن الوصول إليه
return "Cannot access private member"جرّب بنفسك
from bank_account import BankAccount
# معالج حالات الاختبار الشاملة
test_case = input()
if test_case == "basic_test":
# اختبار الوظائف الأساسية
account = BankAccount("John Doe", "12345")
print(account.account_holder) # سمة عامة (Public attribute)
print(account.deposit(100)) # طريقة عامة (Public method)
print(account.get_balance()) # طريقة عامة (Public method)
elif test_case == "transaction_count":
# اختبار سمة محمية (Protected attribute)
account = BankAccount("Jane Smith", "67890")
account.deposit(50)
account.deposit(100)
account.deposit(200)
print(account._transaction_count) # سمة محمية (Protected attribute)
print(account.get_balance())
elif test_case == "invalid_deposit":
# اختبار التحقق من الصحة في طريقة الإيداع
account = BankAccount("Bob Johnson", "54321")
print(account.deposit(-50)) # يجب أن يعيد None
print(account.deposit(0)) # يجب أن يعيد None
print(account.deposit(75)) # يجب أن يعيد 75
print(account.get_balance())
elif test_case == "private_access":
# اختبار الوصول إلى سمة خاصة (Private attribute)
account = BankAccount("Alice Brown", "98765")
try:
print(account.__balance) # سيؤدي هذا إلى رفع AttributeError
except AttributeError:
pass
try:
print(account.__account_number) # سيؤدي هذا إلى رفع AttributeError
except AttributeError:
pass
print("Private attributes are not directly accessible")
account.deposit(500)
print(account.get_balance()) # الوصول من خلال طريقة عامة
elif test_case == "name_mangling":
# توضيح تشويه الأسماء (name mangling) للسمات الخاصة
account = BankAccount("Charlie Green", "13579")
print(account._BankAccount__balance) # الوصول إلى سمة خاصة من خلال تشويه الأسماء
print(account._BankAccount__account_number) # الوصول إلى سمة خاصة من خلال تشويه الأسماء
account.deposit(300)
print(account._BankAccount__balance) # يجب أن يظهر الرصيد المحدث
elif test_case == "multiple_accounts":
# اختبار حسابات متعددة
account1 = BankAccount("David White", "24680")
account2 = BankAccount("Eva Black", "86420")
account3 = BankAccount("Frank Red", "97531")
account1.deposit(150)
account2.deposit(250)
account2.deposit(50)
account3.deposit(500)
account3.deposit(300)
account3.deposit(200)
print(f"{account1.account_holder}: {account1.get_balance()}")
print(f"{account2.account_holder}: {account2.get_balance()}")
print(f"{account3.account_holder}: {account3.get_balance()}")
print(f"Account 1 transactions: {account1._transaction_count}")
print(f"Account 2 transactions: {account2._transaction_count}")
print(f"Account 3 transactions: {account3._transaction_count}")
elif test_case == "stress_test":
# اختبار الأداء مع العديد من المعاملات
account = BankAccount("Stress Tester", "11111")
for _ in range(1000):
account.deposit(1)
print(f"Final balance: {account.get_balance()}")
print(f"Transaction count: {account._transaction_count}")
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال3خصائص الـ Class
متغيرات الـ Instance مقابل متغيرات الـ ClassProperty Decoratorsالسمات الخاصةمراجعة - مدير الحساب البنكي6التغليف (Encapsulation)
الأعضاء العامة والمحمية والخاصةمحددات الوصولإخفاء المعلوماتProperty Decorators متقدمةمراجعة - نظام سجلات الطلاب