<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های DJZ</title>
        <link>https://virgool.io/feed/@DJZ</link>
        <description>اولین باری که کامپیوتری رو خراب کردم چون می خواستم ببینم یه قسمت خاصش چه شکلیه فقط یک ساله بودم.
ولی مطمئن نیستم آخرین بارم باشه.</description>
        <language>fa</language>
        <pubDate>2026-06-15 23:01:31</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/4857295/avatar/zwvBOd.jpg?height=120&amp;width=120</url>
            <title>DJZ</title>
            <link>https://virgool.io/@DJZ</link>
        </image>

                    <item>
                <title>Descriptor ها؟! توصیف‌گر ها؟! (2)</title>
                <link>https://virgool.io/@DJZ/descriptor-%D9%87%D8%A7-%D8%AA%D9%88%D8%B5%DB%8C%D9%81-%DA%AF%D8%B1-%D9%87%D8%A7-2-p8otuvok0ruj</link>
                <description>به نام خداتو قسمت قبلی به این اشاره کردیم که چطور descriptor ها برای کاهش تکرار کد به ما کمک می کنن و چه زمانی نیاز میشه ازشون استفاده کنیم. در پایان اون قسمت، تصمیم گرفتیم از descriptor مون استفاده کنیم و ازش چندتا شیء بسازیم.توی خط 19، 20 و 21 همین کار رو کردیم. اما هنوز همه چیز به شکلی حرفه ای درست نیست. همون طور که مشخصه، توی __init__ کلاس descriptor مون (که اینجا NonEmptyString هست) باید اسم ویژگی رو داشته باشیم. اما مشکلی که داریم اینه: داریم اسم ویژگی رو دستی بهش میدیم. از لحاظ کدنویسی خیلی هم چیز رومخ و زشتیه. فرض کن کلاست 20 تا ویژگی داره که مجبوری برای همه شون از یه کلاس descriptor استفاده کنی. اون وقت مجبوری اسم هر 20 تا ویژگی رو دستی به شیءت بدی.پس برای حل این مشکل، اول برمی‌گردیم به کلاس descriptor مون و متد __init__ ش رو جور دیگه ای می‌نویسیم:دیدی چطور __init__ جدید رو چطوری پیاده سازی کردیم؟ همچنان هر شيء descriptor ما یه ویژگی به اسم name داره. ولی دیگه مقدارش رو توی __init__ بهش نمی‌دیم و فعلا فقط مقدار پیشفرضش رو None می‌ذاریم.حالا مسئله اصلی اینه: اسم ویژگی رو از کجا بیاریم؟ (منظور اینه که اسم ویژگی ای که رفتار دلخواهمون رو بعدا بهش می چسبونیم. مثل همین first_name، last_name و nickname توی کلاس اصلی مون که Person باشه)از پایتون 3.6 به بعد یه متد جادویی جدید وارد شد که دقیقا راه حل مشکل ما بود: __set_name__. کجا تعریف می‌شه؟ توی کلاس descriptor. آیا چیزی برمی‌گردونه؟ نه. فقط set می کنه (اسمش برازنده‌شه). پس بریم اول امضاش رو ببینیم:owner اسم کلاسیه که توش ویژگی ما تعریف شده (همون صاحب descriptor که Person ما هست). برای همین میشه type hint این آرگومان رو type گذاشت. name هم اسم ویژگی ماست و همیشه از نوع str هست. برای مثال وقتی که توی کلاس Person می زنی:توی متد __set_name__ این نمونه از NonEmptyString، آرگومان owner تو میشه خود Person و nameت هم میشه first_name.اما کِی فراخوانی میشه؟ کی فراخوانیش می کنه؟ دقیقا تو همون عکس بالا، خط 23 __set_name__ فراخوانی میشه. ما این کارو نمی کنیم. خود مفسر پایتون انجامش میده. برای هر نمونه از descriptor هم فقط یه بار صدا زده میشه. پس بریم بر اساس این توضیحات بدنه تابع رو هم پیاده سازی کنیم:دیگه لازم نیست به __get__ و __set__ دست بزنیم و همه چیز درسته. پس یک بار کل کد رو ببینیم:بعد می تونیم براش تست هم بنویسیم.که خروجیش میشه:ممکنه بپرسی __main__ چیه توی خط 1 تا 3 خروجی. پاسخ اینه که وقتی کدی رو توی پایتون مستقیما اجرا می کنی، اون فایل به عنوان ماژول اصلی یا همون __main__ شناخته میشه.تو قسمت بعدی برای درک بهتر __set_name__ یه کار کوچیک می کنیم👍</description>
                <category>DJZ</category>
                <author>DJZ</author>
                <pubDate>Sat, 23 May 2026 11:20:42 +0330</pubDate>
            </item>
                    <item>
                <title>Descriptor ها؟! توصیف‌گر ها؟!</title>
                <link>https://virgool.io/@DJZ/descriptor-%D9%87%D8%A7-%D8%AA%D9%88%D8%B5%DB%8C%D9%81-%DA%AF%D8%B1-%D9%87%D8%A7-b1eyv6uroqqv</link>
                <description>به نام خداهمه چیز از اصل DRY (Don&#039;t repeat yourself) شروع میشه و بحث Encapsulation در پایتون. معمولا وقتی که تازه بحث property ها و مثلا setter/getter/deleter رو یاد می گیریم و سعی می کنیم باهاش تمرین کنیم، به یه سری مسئله هایی بر می خوریم که توی اونا یه کلاس چندتا ویژگی از یه نوع (مثلا str) داره. گاهی هم میشه که منطقی که توی setter شون پیاده می کنیم رو توی setter صدتا property دیگه پیاده کردیم. مثال رو ببین:class Person:
    def __init__(self, first_name: str, last_name: str, nickname: str):
        self.__first_name = first_name
        self.__last_name = last_name
        self.__nickname = nickname

    @property
    def first_name(self) -&gt; str:
        return self.__first_name
    
    @first_name.setter
    def first_name(self, value: str):
        if value is None or value == &quot;&quot;:
            raise ValueError(&#039;The first name shouldn\&#039;t be empty.&#039;)
        elif not isinstance(value, str):
            raise TypeError(&#039;The first name should be a string.&#039;)
        self.__first_name = value
    
    @property
    def last_name(self) -&gt; str:
        return self.__last_name
    
    @last_name.setter
    def last_name(self, value: str):
        if value is None or value == &quot;&quot;:
            raise ValueError(&#039;The last name shouldn\&#039;t be empty.&#039;)
        elif not isinstance(value, str):
            raise TypeError(&#039;The last name should be a string.&#039;)
        self.__last_name = value

    @property
    def nickname(self) -&gt; str:
        return self.__nickname
    
    @nickname.setter
    def nickname(self, value: str):
        if value is None or value == &quot;&quot;:
            raise ValueError(&#039;The nickname shouldn\&#039;t be empty.&#039;)
        elif not isinstance(value, str):
            raise TypeError(&#039;The nickname should be a string.&#039;)
        self.__nickname = valueاگه واقعا با دقت و حوصله کل کد رو مطالعه کردی، تبریک میگم جایزه ی فرد با حوصله #1 ویرگول به تو تعلق می گیره. -___-. خودم حتی حوصله نداشتم منطق رو کپی پیست کنم. می بینی؟ اشکال از کلاس نیست که 3 تا ویژگی با یه اعتبارسنجی داره. مشکل از کدنویسه (که من باشم) که کدشو تکرار می کنه.شاید بگی چه کاریه، تابع validation تعریف کن و توی هر setter صداش کن. این بدک نیست. جواب میده. اما یه راه حل بهتر هست. اصلا descriptor ها متولد شدن تا به attribute موردنظر ما رفتار دلخواه مون رو بچسبونن. مثل چی؟ مثل همین منطق validation که بررسی می کنیم مقداری که کاربر میده None یا رشته ی خالی نباشه. قبل این که شروع کنیم: همین الان هم برای این کلاس Person پایتون پشت صحنه خودش شیء descriptor ساخت (ما اگه لازممون بشه باید دستی بنویسیمش، در غیر این صورت پایتون خودش اون رو می سازه.) بریم یکی بسازیم 👇class NonEmptyString:
    def __init__(self, name: str): 
        self.name = nameاولین بخشی که توی کلاس descriptor مون تعریف می کنیم مقداردهی اولیه ش هست (چون ما در ادامه از این کلاس چندتا شیء میخوایم.) می پرسی name چی میگه این وسط؟ حق داری، خودم هم وقتی داشتم یاد می گرفتم همین سوال رو پرسیدم. اولا که اگه تازه داری این مبحث رو یاد می گیری، یه کم صبر کن. اگه بریم جلوتر به جواب خیلی از سوال هات می رسی. اما توضیح کوتاهش اینه اسم ویژگی ای هست که میخوایم رفتارهایی رو که در ادامه تعریف می کنیم، بهش بچسبونیم. (مثلا توی مثال کلاس Person، ویژگی های first_name، last_name و nickname)در ادامه:    def __get__(self, instance, owner): این امضای این متد هست. instance چیه؟ فرض کن یه جا زده باشی:p1 = Person(&#039;Ali&#039;, &#039;Alavai&#039;, &#039;Ali Agha&#039;)
print(p1.nickname)وقتی خط دوم اجرا میشه، دقیقا پشت صحنه توی متد __get__، آرگومان instance تو شده p1 و ownerت شده Person (یا همون type(p1)) 👈 ولی وقتی مستقیما بزنی Person.nickname، اون وقت instanceت None هست.پس بریم بر همین اساس، متد __get__ رو پیاده سازی کنیم:    def __get__(self, instance, owner): 
        if instance is None:
            print(f&#039;Accessing the {self.name} directly from {owner.__name__}&#039;)
            return self
        print(f&#039;Accessing the {self.name} from an instance of {owner.__name__}&#039;)
        return instance.__dict__[self.name](می تونی منطق دلخواهت رو پیاده کنی. مثلا لاگ گیری پیشرفته تر یا حتی جلوگیری از دسترسی به attribute از کلاس/نمونه) الان نپرس چرا توی خط 4 داریم self رو بر می گردونیم. جلوتر راجع بهش صحبت می کنیم.و اما __set__:    def __set__(self, instance, value: str):این هم از امضای این متد (طبیعتا نباید چیزی برگردونه.) اما instance چیه؟ مجددا همین نمونه ای از کلاس که داریم یه ویژگیش رو به مقدار دلخواه خودمون تغییر می دیم. چون این شیء descriptor رو برای property هایی که str هستن استفاده می کنیم، مطمئنا تایپ value باید str باشه.    def __set__(self, instance, value: str):
        if value is None or value == &#039;&#039;:
            raise ValueError(f&#039;The {self.name} shouldn\&#039;t be empty.&#039;)
        instance.__dict__[self.name] = valueو اما منطق لعنتی که توی نسخه ی noob نوشتن Person استفاده کردیم رو داریم اینجا به شکل pythonic تر می نویسیم. خداحافظ تکرار!خب حالا از این چیزی که نوشتیم چجوری استفاده کنیم؟؟؟ بیا برگردیم به کلاس Person جدیدمون:class Person:
    first_name = NonEmptyString(&#039;first_name&#039;)
    last_name = NonEmptyString(&#039;last_name&#039;)
    nickname = NonEmptyString(&#039;nickname&#039;)

    def __init__(self, first_name: str, last_name: str, nickname: str):
        self.first_name = first_name
        self.last_name = last_name
        self.nickname = nicknameبخش __init__ که کلا مشخصه. موقع ساخت شیء از کاربر first_name، last_name و nickname رو میگیره و برای self (همین instance)، به ویژگی تبدیل شون می کنه. اما خط 2 تا 4 چی می خوان؟ و چرا این جوری هستن؟ ما در اصل داریم برای هر کدوم از این سه تا ویژگی، شیء descriptor می سازیم. و اگه به __init__ در NonEmptyString مون نگاه کنی، به نام ویژگی مون نیاز داریم برای هر نمونه از descriptor مون. اما این که دستی باید اسم ویژگی رو بهش بدیم خودش باز هم یک مشکله. که البته توی پست بعدی میگم :)پس کد کامل تا اینجا:class NonEmptyString:
    def __init__(self, name: str): 
        self.name = name

    def __get__(self, instance, owner): 
        if instance is None:
            print(f&#039;Accessing the {self.name} directly from {owner.__name__}&#039;)
            return self
        print(f&#039;Accessing the {self.name} from an instance of {owner.__name__}&#039;)
        return instance.__dict__[self.name]
    
    def __set__(self, instance, value: str):
        if value is None or value == &#039;&#039;:
            raise ValueError(f&#039;The {self.name} shouldn\&#039;t be empty.&#039;)
        instance.__dict__[self.name] = value


class Person:
    first_name = NonEmptyString(&#039;first_name&#039;)
    last_name = NonEmptyString(&#039;last_name&#039;)
    nickname = NonEmptyString(&#039;nickname&#039;)

    def __init__(self, first_name: str, last_name: str, nickname: str):
        self.first_name = first_name
        self.last_name = last_name
        self.nickname = nickname</description>
                <category>DJZ</category>
                <author>DJZ</author>
                <pubDate>Tue, 19 May 2026 12:00:48 +0330</pubDate>
            </item>
            </channel>
</rss>