Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الأعضاء العامة والمحمية والخاصة

جزء من قسم 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 يعتمد على العرف، وليس مفروضاً بشكل صارم.

challenge icon

التحدي

متوسط

في هذا التحدي، ستقوم بتنفيذ فئة BankAccount التي توضح الاستخدام الصحيح لمستويات الوصول العامة (public)، والمحمية (protected)، والخاصة (private) في Python.

  • bankaccount.py - يحتوي على تعريف الفئة مع تعليقات TODO لإرشادك خلال التنفيذ
  1. اتبع تعليقات TODO في bankaccount.py لتنفيذ الوظائف المطلوبة
  2. قم بتنفيذ مستويات الوصول المناسبة (public، protected، private) كما هو محدد
  3. تأكد من أن جميع الطرق (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}")
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming