property در پایتون – به زبان ساده


در این مطلب، مفهوم property در پایتون برای استفاده از getters و setters همراه با ارائه مثال و کدهای نمونه، آموزش داده میشود.
property در پایتون
«زبان برنامهنویسی پایتون» (Python Programming Language) دارای قابلیت بسیار خوبی است که به آن property گفته میشود و کار برنامهنویسی را برای «برنامهنویسی شیئگرا» (Object Oriented Programming) حقیقتا آسانتر میکند. پیش از تعریف و ارائه جزئیات پیرامون چیستی property در پایتون به دلایل نیاز به این قابلیت پرداخته شده است.
چرا به قابلیت property در پایتون نیاز است؟
فرض میشود که کاربر قصد دارد یک کلاس بسازد که درجه حرارت را به درجه سلسیوس ذخیره میکند. همچنین، متدی را پیادهسازی میکند که درجه حرارت را به به فارنهایت تبدیل میکند. یک راه برای انجام این کار در ادامه بیان شده است.
میتوان شیئی از این کلاس را ساخت و خصیصه temperature را همانطور که انتظار میرود دستکاری کرد. کد زیر را در این راستا میتوان در شل پایتون اجرا کرد.
>>> # create new object >>> man = Celsius() >>> # set temperature >>> man.temperature = 37 >>> # get temperature >>> man.temperature 37 >>> # get degrees Fahrenheit >>> man.to_fahrenheit() 98.60000000000001
جایگاههای اعشاری اضافی هنگام تبدیل به فارنهایت به دلیل خطای محاسبات ممیز شناور برای عدد قرار میگیرند. هر زمان که هرگونه خصیصهای مانند temperature، همانطور که در بالا نشان داده شده تخصیص داده و یا بازیابی میشود پایتون آن را در شی __dict__ دیکشنری جستجو میکند.
>>> man.__dict__ {'temperature': 37}
بنابراین، man.temperature به طور داخلی، ['man.__dict__['temperature میشود. اکنون، فرض میشود که این کلاس نوشته شده به زبان پایتون در میان برنامهنویسان محبوب شده و آنها از این کلاس در برنامههای خود به کررات استفاده کردهاند. بنابراین، آنها هر نوع تخصیصی را به شیئ انجام میدهند. حال آنکه درجه حرارت، کمتر از ۲۷۳- سلسیوس نمیشود (دانشجوهای ترمودینامیک ممکن است بگویند که این مقدار ۲۷۳.۱۵- است) که به آن صفر مطلق (صفر کلوین) نیز میگویند. برای اعمال این موضوع و اصل فیزیکی در کلاس موجود، باید آن را بهبود و ارتقا داد و در واقع، کد موجود را ویرایش کرد.
دریافت Getters و Setters
یک راهکار واضح برای اعمال محدودیت بالا، مخفیسازی خصیصه temperature (آن را خصوصی میکند) و تعریف یک رابط getter و setter جدید برای دستکاری آن است. این کار به صورت زیر قابل انجام است.
میتوان در کد بالا مشاهده کرد که متدهای ()get_temperature و ()set_temperature تعریف شدهاند و علاوه بر آن، temperature با temperature_ جایگزین شده است. یک زیرخط (_) در آغاز برای تعریف متغیرهای خصوصی (Private) در پایتون مورد استفاده قرار میگیرد.
>>> c = Celsius(-277) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible >>> c = Celsius(37) >>> c.get_temperature() 37 >>> c.set_temperature(10) >>> c.set_temperature(-300) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible
در این به روز رسانی، محدودیتهای جدید بیان شده پیرامون درجه حرارت به طور موفقیتآمیزی پیادهسازی شدهاند. پس از این، امکان تنظیم درجه حرارت زیر ۲۷۳- وجود ندارد. شایان توجه است که متغیرهای خصوصی واقعا در پایتون وجود ندارد و برای پیادهسازی آنها قواعد سادهای وجود دارد که باید از آنها پیروی کرد. خود زبان پایتون، هیچ محدودیتی را پیادهسازی نمیکند.
اما پیادهسازی این کد و اعمال این محدودیت به کد حقیقتا مسئله مهمی محسوب نمی شود. مساله اصلی در به روز رسانی بالا آن است که همه افرادی که پیش از این از کلاس درجه حرارت در برنامه خود استفاده کردهاند، باید کد خود را از obj.temperature به ()obj.get_temperature و همه تخصیصهای obj.temperature = val را به (obj.set_temperature(val تغییر دهند. این «بازسازی کد» (Refactoring) میتواند موجب مشکلات زیادی برای مشتریانی بشود که صدها و هزاران خط کد دارند. به طور کلی، به روز رسانی جدیدی که انجام میشود به طور عقبگرد سازگار نیست. در چنین شرایطی است که قابلیت property در پایتون به میان میآید.
کلاس property در پایتون
راهکار پایتون برای سر و کله زدن با مسئله بالا استفاده از property است. در ادامه، روش انجام این کار بیان شده است.
پس از اجرای کد بالا، دستور زیر را باید در شل اجرا کرد.
>>> c = Celsius()
یک تابع ()print درون ()get_temperature و ()set_temperature قرار داده شده است تا به وضوح مشاهده شود که این توابع اجرا شدهاند. آخرین خط از کد، شیئ property یعنی temperature را ایجاد میکند. به سادگی، قابلیت property در پایتون کدهایی را (get_temperature و set_temperature) به دسترسیهای خصیصه عضو (temperature) اضافه میکند. هر کدی که مقدار temperature را بازیابی میکند، به طور خودکار، ()get_temperature را به جای جستوجو در دیکشنری (__dict__)، فراخوانی میکند. این یک ویژگی فوقالعاده در پایتون است. میتوان در بالا مشاهده کرد که ()set_temperature حتی هنگامی که یک شیئ ساخته شده، فراخوانی میشود.
شرحی بر عملکرد property در پایتون
دلیل آنچه در بالا بیان شد آن است که وقتی یک شیئ ساخته میشود، متد ()__init__ فراخوانی میشود. این متد دارای خط self.temperature = temperature است. این تخصیص به طور خودکار ()set_temperature را فراخوانی کرده است.
>>> c.temperature Getting value 0
به طور مشابه، هر دسترسی مانند c.temperature، به طور خودکار ()get_temperature را فراخوانی میکند. این همان کاری است که property انجام میدهد. در ادامه، چندین مثال ارائه شده است.
با استفاده از property، میتوان مشاهده کرد که کلاس ویرایش شده و مقدار محدودیت را بدون نیاز به اعمال هر گونه تغییری در کل مشتری، اعمال کرده است. بدین ترتیب، پیادهسازی کنونی دارای «سازگاری عقبرو» (Backward Compatible) است و همه را راضی خواهد کرد. در نهایت، توجه به این نکته لازم است که مقدار کنونی درجه حرارت در متغیر خصوصی temperature_ ذخیره شده است. خصیصه temperature، یک شیئ property است که رابطی را برای این متغیر خصوصی فراهم میکند.
نگاهی عمیقتر به property
()property در پایتون یک تابع توکار است که یک شیئ property را میسازد و با استفاده از دستور Return در پایتون باز میگرداند. روش نوشتن این تابع به صورت زیر است.
property(fget=None, fset=None, fdel=None, doc=None)
در حالی که fget تابعی برای دریافت مقادیر خصیصهها است، fset تابعی برای تنظیم مقدار خصیصه، fdel تابعی برای حذف خصیصه و doc یک رشته (مانند توضیحات) است. همانطور که در پیادهسازی مشهود است، آرگومانهای این تابع دلخواه هستند. بنابراین، شیئ property در پایتون را میتوان به سادگی به صورت زیر ساخت.
>>> property() <property object at 0x0000000003239B38>
یک شیئ property دارای سه متد ()setter() ،getter و ()deleter برای تعریف fset ،fget و fdel در نهایت است. این یعنی خط کد زیر،
temperature = property(get_temperature,set_temperature)
را میتوان به صورت زیر شکست.
دو کد ارائه شده در بالا، معادل یکدیگر هستند. برنامهنویسانی که با دکوراتورها در پایتون آشنایی دارند، میتوانند تشخیص دهند که ساختار بالا را میتوان به عنوان دکوراتور نیز پیادهسازی کرد. میتوان گام دیگری هم به جلوتر رفت و اسامی get_temperature و set_temperature را با توجه به اینکه غیر الزامی هستند و فضای نام کلاس را شلوغ میکنند. به همین دلیل، از نام temperature در حال تعریف توابع getter و getter استفاده میشود. در ادامه، روش انجام این کار آمده است.
پیادهسازی بالا، ساده است و برای ساخت Propertyها توصیه میشود.
@ که در مثال استفاده شده برای چیست؟