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

۱۴۰۹ بازدید
آخرین به‌روزرسانی: ۳۰ خرداد ۱۴۰۱
زمان مطالعه: ۵ دقیقه
property در پایتون — به زبان ساده

در این مطلب، مفهوم property در پایتون برای استفاده از getters و setters همراه با ارائه مثال و کدهای نمونه، آموزش داده می‌شود.

property در پایتون

«زبان برنامه‌نویسی پایتون» (Python Programming Language) دارای قابلیت بسیار خوبی است که به آن property گفته می‌شود و کار برنامه‌نویسی را برای «برنامه‌نویسی شیئ‌گرا» (Object Oriented Programming) حقیقتا آسان‌تر می‌کند. پیش از تعریف و ارائه جزئیات پیرامون چیستی property در پایتون به دلایل نیاز به این قابلیت پرداخته شده است.

چرا به قابلیت property در پایتون نیاز است؟

فرض می‌شود که کاربر قصد دارد یک کلاس بسازد که درجه حرارت را به درجه سلسیوس ذخیره می‌کند. همچنین، متدی را پیاده‌سازی می‌کند که درجه حرارت را به به فارنهایت تبدیل می‌کند. یک راه برای انجام این کار در ادامه بیان شده است.

1class Celsius:
2    def __init__(self, temperature = 0):
3        self.temperature = temperature
4
5    def to_fahrenheit(self):
6        return (self.temperature * 1.8) + 32

می‌توان شیئی از این کلاس را ساخت و خصیصه 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 جدید برای دستکاری آن است. این کار به صورت زیر قابل انجام است.

1class Celsius:
2    def __init__(self, temperature = 0):
3        self.set_temperature(temperature)
4
5    def to_fahrenheit(self):
6        return (self.get_temperature() * 1.8) + 32
7
8    # new update
9    def get_temperature(self):
10        return self._temperature
11
12    def set_temperature(self, value):
13        if value < -273:
14            raise ValueError("Temperature below -273 is not possible")
15        self._temperature = value

می‌توان در کد بالا مشاهده کرد که متدهای ()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

در این به روز رسانی، محدودیت‌های جدید بیان شده پیرامون درجه حرارت به طور موفقیت‌آمیزی پیاده‌سازی شده‌اند. پس از این، امکان تنظیم درجه حرارت زیر ۲۷۳- وجود ندارد. شایان توجه است که متغیرهای خصوصی واقعا در پایتون وجود ندارد و برای پیاده‌سازی آن‌ها قواعد ساده‌ای وجود دارد که باید از آن‌ها پیروی کرد. خود زبان پایتون، هیچ محدودیتی را پیاده‌سازی نمی‌کند.

1>>> c._temperature = -300
2>>> c.get_temperature()
3-300

اما پیاده‌سازی این کد و اعمال این محدودیت به کد حقیقتا مسئله مهمی محسوب نمی شود. مساله اصلی در به روز رسانی بالا آن است که همه افرادی که پیش از این از کلاس درجه حرارت در برنامه خود استفاده کرده‌اند، باید کد خود را از obj.temperature به ()obj.get_temperature و همه تخصیص‌های obj.temperature = val را به (obj.set_temperature(val تغییر دهند. این «بازسازی کد» (Refactoring) می‌تواند موجب مشکلات زیادی برای مشتریانی بشود که صدها و هزاران خط کد دارند. به طور کلی، به روز رسانی جدیدی که انجام می‌شود به طور عقب‌گرد سازگار نیست. در چنین شرایطی است که قابلیت property در پایتون به میان می‌آید.

کلاس property در پایتون

راهکار پایتون برای سر و کله زدن با مسئله بالا استفاده از property است. در ادامه، روش انجام این کار بیان شده است.

1class Celsius:
2    def __init__(self, temperature = 0):
3        self.temperature = temperature
4
5    def to_fahrenheit(self):
6        return (self.temperature * 1.8) + 32
7
8    def get_temperature(self):
9        print("Getting value")
10        return self._temperature
11
12    def set_temperature(self, value):
13        if value < -273:
14            raise ValueError("Temperature below -273 is not possible")
15        print("Setting value")
16        self._temperature = value
17
18    temperature = property(get_temperature,set_temperature)

پس از اجرای کد بالا، دستور زیر را باید در شل اجرا کرد.

>>> 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 انجام می‌دهد. در ادامه، چندین مثال ارائه شده است.

1>>> c.temperature = 37
2Setting value
3
4>>> c.to_fahrenheit()
5Getting value
698.60000000000001

با استفاده از property، می‌توان مشاهده کرد که کلاس ویرایش شده و مقدار محدودیت را بدون نیاز به اعمال هر گونه تغییری در کل مشتری، اعمال کرده است. بدین ترتیب، پیاده‌سازی کنونی دارای «سازگاری عقب‌رو» (Backward Compatible) است و همه را راضی خواهد کرد. در نهایت، توجه به این نکته لازم است که مقدار کنونی درجه حرارت در متغیر خصوصی temperature_ ذخیره شده است. خصیصه temperature، یک شیئ property است که رابطی را برای این متغیر خصوصی فراهم می‌کند.

نگاهی عمیق‌تر به property

()property در پایتون یک تابع توکار است که یک شیئ property را می‌سازد و باز می‌گرداند. روش نوشتن این تابع به صورت زیر است.

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)

را می‌توان به صورت زیر شکست.

1# make empty property
2temperature = property()
3# assign fget
4temperature = temperature.getter(get_temperature)
5# assign fset
6temperature = temperature.setter(set_temperature)

دو کد ارائه شده در بالا، معادل یکدیگر هستند. برنامه‌نویسانی که با دکوراتورها در پایتون آشنایی دارند، می‌توانند تشخیص دهند که ساختار بالا را می‌توان به عنوان دکوراتور نیز پیاده‌سازی کرد. می‌توان گام دیگری هم به جلوتر رفت و اسامی get_temperature و set_temperature را با توجه به اینکه غیر الزامی هستند و فضای نام کلاس را شلوغ می‌کنند. به همین دلیل، از نام temperature در حال تعریف توابع getter و getter استفاده می‌شود. در ادامه، روش انجام این کار آمده است.

1class Celsius:
2    def __init__(self, temperature = 0):
3        self._temperature = temperature
4
5    def to_fahrenheit(self):
6        return (self.temperature * 1.8) + 32
7
8    @property
9    def temperature(self):
10        print("Getting value")
11        return self._temperature
12
13    @temperature.setter
14    def temperature(self, value):
15        if value < -273:
16            raise ValueError("Temperature below -273 is not possible")
17        print("Setting value")
18        self._temperature = value

پیاده‌سازی بالا، ساده است و برای ساخت Property‌ها توصیه می‌شود.

بر اساس رای ۷ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
Programiz
۱ دیدگاه برای «property در پایتون — به زبان ساده»

@ که در مثال استفاده شده برای چیست؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *