property Decorator في Python: حالات استخدامه ومزاياها وتركيبها

? ميت خصائص

أهلا بك! في هذه المقالة سوف تتعلم كيفية العمل مع @propertyمصمم الديكور في بايثون.

سوف تتعلم:

  • مزايا العمل مع الخصائص في بايثون.
  • أساسيات وظائف الديكور: ما هي وكيف ترتبط بالملكية @.
  • كيف يمكنك استخدامproperty لتعريف المحصلات والمحددات والمحذوفات.

1️⃣ مزايا الخصائص في بايثون

لنبدأ بقليل من السياق. لماذا قد تستخدم الخصائص في بايثون؟

يمكن اعتبار الخصائص طريقة "Pythonic" للتعامل مع السمات للأسباب التالية:

  • الصيغة المستخدمة لتحديد الخصائص موجزة للغاية ويمكن قراءتها.
  • يمكنك الوصول إلى سمات المثيل تمامًا كما لو كانت سمات عامة أثناء استخدام "السحر" للوسطاء (الحاصلون والمحددون) للتحقق من صحة القيم الجديدة وتجنب الوصول إلى البيانات أو تعديلها مباشرةً.
  • باستخدامproperty ، يمكنك "إعادة استخدام" اسم خاصية لتجنب إنشاء أسماء جديدة لأحرف ، والمحددات ، والمحذوفات.

هذه المزايا تجعل الخصائص أداة رائعة حقًا لمساعدتك على كتابة كود أكثر إيجازًا وقابلية للقراءة. ؟

2️⃣ مقدمة عن الديكور

و ظيفة الديكور هي في الأساس وظيفة إضافة وظائف جديدة إلى وظيفة التي يتم تمريرها كوسيطة. استخدام وظيفة التزيين مثل إضافة رشات الشوكولاتة إلى الآيس كريم؟ يتيح لنا إضافة وظائف جديدة إلى وظيفة موجودة دون تعديلها.

في المثال أدناه ، يمكنك أن ترى كيف تبدو وظيفة الديكور النموذجية في Python:

def decorator(f): def new_function(): print("Extra Functionality") f() return new_function @decorator def initial_function(): print("Initial Functionality") initial_function()

دعنا نحلل هذه العناصر بالتفصيل:

  • نجد أولاً وظيفة الديكور def decorator(f)(الرشات ✨) التي تأخذ وظيفة fكوسيطة.
def decorator(f): def new_function(): print("Extra Functionality") f() return new_function
  • وظيفة التزيين هذه لها وظيفة متداخلة ، new_function. لاحظ كيف fيتم استدعاؤها بالداخل new_functionلتحقيق نفس الوظيفة أثناء إضافة وظائف جديدة قبل استدعاء الوظيفة (يمكننا أيضًا إضافة وظيفة جديدة بعد استدعاء الوظيفة).
  • دالة التزيين نفسها ترجع الدالة المتداخلة new_function.
  • ثم (أدناه) نجد الوظيفة التي سيتم تزيينها (الآيس كريم؟) initial_function. لاحظ بناء الجملة الغريب جدًا ( @decorator) أعلى رأس الوظيفة.
@decorator def initial_function(): print("Initial Functionality") initial_function()

إذا قمنا بتشغيل الكود ، فإننا نرى هذا الناتج:

Extra Functionality Initial Functionality

لاحظ كيف تعمل وظيفة الزخرفة حتى لو كنا نتصل فقط initial_function(). هذا هو سحر إضافةdecorator؟.

? ملاحظة: بشكل عام ، سنكتب@ ، مع استبدال اسم وظيفة الديكور بعد الرمز @.

أعلم أنك قد تسأل: ما علاقة هذا بالملكية @؟ property هو مصمم مضمن لوظيفة property () في Python. يتم استخدامه لإعطاء وظائف "خاصة" لطرق معينة لجعلها تعمل كأحياء أو محددات أو محذوفات عندما نحدد خصائص في فئة.

الآن بعد أن أصبحت معتادًا على المصممين ، دعنا نرى سيناريو حقيقيًا لاستخدامproperty!

? سيناريو العالم الحقيقي:property

لنفترض أن هذا الفصل هو جزء من برنامجك. أنت تصمم منزلًا Houseبفئة (في الوقت الحالي ، لا يحتوي الفصل إلا على سمة مثيل سعر محددة):

class House: def __init__(self, price): self.price = price

سمة المثيل هذه عامة لأن اسمها لا يحتوي على شرطة سفلية بادئة. نظرًا لأن السمة عامة حاليًا ، فمن المحتمل جدًا أن تكون أنت والمطورين الآخرين في فريقك قد قمت بالوصول إلى السمة وتعديلها مباشرةً في أجزاء أخرى من البرنامج باستخدام تدوين النقاط ، مثل هذا:

# Access value obj.price # Modify value obj.price = 40000

? نصيحة: يمثل obj متغيرًا يشير إلى مثيل House.

حتى الآن كل شيء يعمل بشكل جيد ، أليس كذلك؟ ولكن لنفترض أنه طُلب منك جعل هذه السمة محمية (غير عامة) والتحقق من صحة القيمة الجديدة قبل تعيينها . على وجه التحديد ، تحتاج إلى التحقق مما إذا كانت القيمة عائمة موجبة. كيف يمكنك أن تفعل ذلك؟ دعونا نرى.

تغيير الرمز الخاص بك

في هذه المرحلة ، إذا قررت إضافة معلمات ومحددات ، فمن المحتمل أن تشعر أنت وفريقك بالذعر؟ وذلك لأن كل سطر من التعليمات البرمجية يصل إلى قيمة السمة أو يعدلها يجب تعديله لاستدعاء getter أو setter ، على التوالي. خلاف ذلك ، سيتم كسر الرمز ⚠️.

# Changed from obj.price obj.get_price() # Changed from obj.price = 40000 obj.set_price(40000)

لكن ... الممتلكات تنقذ! باستخدام @property، لن تحتاج أنت وفريقك إلى تعديل أي من هذه السطور لأنك ستتمكن من إضافة محاضر ومحددات "خلف الكواليس" دون التأثير على البنية التي استخدمتها للوصول إلى السمة أو تعديلها عندما كانت عامة.

رائع ، أليس كذلك؟  

?property: النحو والمنطق

إذا قررت الاستخدام @property، فسيبدو فصلك كما في المثال أدناه:

class House: def __init__(self, price): self._price = price @property def price(self): return self._price @price.setter def price(self, new_price): if new_price > 0 and isinstance(new_price, float): self._price = new_price else: print("Please enter a valid price") @price.deleter def price(self): del self._price

على وجه التحديد ، يمكنك تحديد ثلاث طرق للخاصية:

  • و جالبة - للوصول إلى قيمة السمة.
  • و اضع - لضبط قيمة السمة.
  • A deleter - حذف السمة المثال.

السعر الآن "محمي"

Please note that the price attribute is now considered "protected" because we added a leading underscore to its name in self._price:

self._price = price

In Python, by convention, when you add a leading underscore to a name, you are telling other developers that it should not be accessed or modified directly outside of the class. It should only be accessed through intermediaries (getters and setters) if they are available.

? Getter

Here we have the getter method:

@property def price(self): return self._price

Notice the syntax:

  • @property - Used to indicate that we are going to define a property. Notice how this immediately improves readability because we can clearly see the purpose of this method.
  • def price(self) - The header. Notice how the getter is named exactly like the property that we are defining: price. This is the name that we will use to access and modify the attribute outside of the class. The method only takes one formal parameter, self, which is a reference to the instance.
  • return self._price - This line is exactly what you would expect in a regular getter. The value of the protected attribute is returned.

Here is an example of the use of the getter method:

>>> house = House(50000.0) # Create instance >>> house.price # Access value 50000.0

Notice how we access the price attribute as if it were a public attribute. We are not changing the syntax at all, but we are actually using the getter as an intermediary to avoid accessing the data directly.

? Setter

Now we have the setter method:

@price.setter def price(self, new_price): if new_price > 0 and isinstance(new_price, float): self._price = new_price else: print("Please enter a valid price")

Notice the syntax:

  • @price.setter - Used to indicate that this is the setter method for the price property. Notice that we are not using @property.setter, we are using @price.setter. The name of the property is included before .setter.
  • def price(self, new_price): - The header and the list of parameters. Notice how the name of the property is used as the name of the setter. We also have a second formal parameter (new_price), which is the new value that will be assigned to the price attribute (if it is valid).
  • Finally, we have the body of the setter where we validate the argument to check if it is a positive float and then, if the argument is valid, we update the value of the attribute. If the value is not valid, a descriptive message is printed. You can choose how to handle invalid values according the needs of your program.

This is an example of the use of the setter method with @property:

>>> house = House(50000.0) # Create instance >>> house.price = 45000.0 # Update value >>> house.price # Access value 45000.0

Notice how we are not changing the syntax, but now we are using an intermediary (the setter) to validate the argument before assigning it. The new value (45000.0) is passed as an argument to the setter :

house.price = 45000.0

If we try to assign an invalid value, we see the descriptive message. We can also check that the value was not updated:

>>> house = House(50000.0) >>> house.price = -50 Please enter a valid price >>> house.price 50000.0

? Tip: This proves that the setter method is working as an intermediary. It is being called "behind the scenes" when we try to update the value, so the descriptive message is displayed when the value is not valid.

? Deleter

Finally, we have the deleter method:

@price.deleter def price(self): del self._price

Notice the syntax:

  • @price.deleter - Used to indicate that this is the deleter method for the price property. Notice that this line is very similar to @price.setter, but now we are defining the deleter method, so we write @price.deleter.
  • def price(self): - The header. This method only has one formal parameter defined, self.
  • del self._price - The body, where we delete the instance attribute.

? Tip: Notice that the name of the property is "reused" for all three methods.

This is an example of the use of the deleter method with @property:

# Create instance >>> house = House(50000.0) # The instance attribute exists >>> house.price 50000.0 # Delete the instance attribute >>> del house.price # The instance attribute doesn't exist >>> house.price Traceback (most recent call last): File "", line 1, in  house.price File "", line 8, in price return self._price AttributeError: 'House' object has no attribute '_price'

The instance attribute was deleted successfully ?. When we try to access it again, an error is thrown because the attribute doesn't exist anymore.

? Some final Tips

You don't necessarily have to define all three methods for every property. You can define read-only properties by only including a getter method. You could also choose to define a getter and setter without a deleter.

If you think that an attribute should only be set when the instance is created or that it should only be modified internally within the class, you can omit the setter.

You can choose which methods to include depending on the context that you are working with.

? In Summary

  • You can define properties with the @property syntax, which is more compact and readable.
  • يمكن اعتبارproperty طريقة "Pythonic" لتعريف الحاصل ، والمحددات ، والمحذوفات.
  • من خلال تحديد الخصائص ، يمكنك تغيير التنفيذ الداخلي للفئة دون التأثير على البرنامج ، بحيث يمكنك إضافة محصلات ومحددات ومحذوفات تعمل كوسطاء "خلف الكواليس" لتجنب الوصول إلى البيانات أو تعديلها مباشرة.

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