برنامه نویسی ۲۲۳۶ بازدید

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

property در پایتون

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

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

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

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        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 جدید برای دستکاری آن است. این کار به صورت زیر قابل انجام است.

class Celsius:
    def __init__(self, temperature = 0):
        self.set_temperature(temperature)

    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    # new update
    def get_temperature(self):
        return self._temperature

    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        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

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

>>> c._temperature = -300
>>> c.get_temperature()
-300

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

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

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

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    def get_temperature(self):
        print("Getting value")
        return self._temperature

    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

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

>>> c.temperature = 37
Setting value

>>> c.to_fahrenheit()
Getting value
98.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)

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

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

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

class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self._temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

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

اگر این مطلب برای شما مفید بوده است، آموزش‌ها و مطالب زیر نیز به شما پیشنهاد می‌شوند:

بر اساس رای ۷ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

«الهام حصارکی»، فارغ‌التحصیل مقطع کارشناسی ارشد مهندسی فناوری اطلاعات، گرایش سیستم‌های اطلاعات مدیریت است. او در زمینه هوش مصنوعی و داده‌کاوی، به ویژه تحلیل شبکه‌های اجتماعی، فعالیت می‌کند.

یک نظر ثبت شده در “property در پایتون — به زبان ساده

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد.