GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing python.github.io — 9. کلاسها 9. کلاسها¶کلاسها وسیلهای را برای بستهبندی کردن داده و عملکرد با هم فراهم میکنند. ایجاد یک کلاس جدید، نوع جدیدی ایجاد میکند که اجازه میدهد انواع جدیدی از آن نوع ساخته شود. هر نمونه از کلاس میتواند صفاتی داشته باشد که به آن متصل شدهاند تا وضعیت آن را حفظ کنند. نمونههای کلاس میتوانند همچنین متدهایی (که توسط کلاس تعریف شدهاند) داشته باشند تا وضعیت آن را تغییر دهند. در مقایسه با دیگر زبانهای برنامهنویسی، مکانیزم کلاس در پایتون کلاسها را با حداقل قواعد و معناشناسی جدید اضافه میکند. این مکانیزم ترکیبی از مکانیزمهای کلاسی است که در C++ و Modula-3 یافت میشود. کلاسهای پایتون تمام ویژگیهای استاندارد برنامهنویسی شیءگرا را فراهم میکنند: مکانیزم ارث بری کلاسها اجازه میدهد کلاسهای پایهی متعددی داشته باشید، یک کلاس مشتق میتواند هر یک از متدهای کلاسهای پایهی خود را بازنویسی کند و یک متد میتواند متدی از کلاس پایه با همان نام را فراخوانی کند. اشیاء میتوانند حاوی مقادیر و انواع دادهای دلخواه باشند. همانطور که برای ماژولها صادق است، کلاسها نیز از طبیعت پویا در پایتون برخوردارند: آنها در رانتایم ایجاد میشوند و میتوانند پس از ایجاد بیشتر تغییر داده شوند. در اصطلاحات C++، معمولاً اعضای کلاس (از جمله اعضای داده) public هستند (به جز موردی که در پایین متغیرهای خصوصی ذکر شده است)، و تمامی توابع عضو مجازیهستند. همانطور که در Modula-3، هیچ میانبرهایی برای ارجاع به اعضای شیء از متدهای آن وجود ندارد: تابع متد با یک آرگومان اول صریح که نمایانگر شیء است، اعلام میشود که این آرگومان به صورت ضمنی توسط فراخوانی فراهم میشود. همانطور که در Smalltalk، خود کلاسها اشیاء هستند. این ویژگی معناشناسی برای وارد کردن و تغییر نام را فراهم میآورد. برخلاف C++ و Modula-3، نوعهای توکار میتوانند بهعنوان کلاسهای پایه برای توسعه توسط کاربر استفاده شوند. همچنین، مانند C++، بیشتر عملگرهای توکار با نحوی خاص (عملگرهای ریاضی، زیرنویسی و غیره) میتوانند برای نمونههای کلاس بازتعریف شوند. (با نبود اصطلاحات پذیرفته شده همگانی برای صحبت دربارهی کلاسها، گاهی از اصطلاحات Smalltalk و C++ استفاده خواهم کرد. من از اصطلاحات Modula-3 استفاده میکردم، چون معنای شیءگرای آن به پایتون نزدیکتر از C++ است، اما انتظار دارم که تعداد کمی از خوانندگان با آن آشنا باشند.) 9.1. سخنی دربارهی نامها و اشیاء¶اشیاء هویت فردی دارند و چندین نام (در چندین محدوده) میتوانند به یک شیء مشخص متصل شوند. این در زبانهای دیگر با نام «اسمهای مستعار» شناخته میشود. این ویژگی معمولاً در نگاه اول به پایتون چندان جلب توجه نمیکند و هنگام کار با انواع پایهی تغییرناپذیر (اعداد، رشتهها، تاپلها) میتوان آن را نادیده گرفت. با این حال، نامهای مستعار میتوانند تأثیری شگفتآور بر معناشناسی کد پایتونی داشته باشند، بهویژه هنگامی که با اشیاء تغییرپذیر مانند فهرستها، دیکشنریها و بیشتر انواع دیگر سروکار داریم. این ویژگی معمولاً به سود برنامه تمام میشود، چون نامهای مستعار از برخی جهات مانند اشارهگرها رفتار میکنند. برای مثال، ارسال یک شیء بهعنوان آرگومان ارزان است، چون پیادهسازی فقط یک اشارهگر را ارسال میکند؛ و اگر تابعی شیءای را که بهعنوان آرگومان دریافت کرده تغییر دهد، فراخواننده تغییر را مشاهده خواهد کرد --- این کار نیاز به دو مکانیزم متفاوت برای ارسال آرگومان (مانند آنچه در پاسکال وجود دارد) را از بین میبرد. 9.2. محدودهها و فضای نامها در پایتون¶قبل از معرفی کلاسها، ابتدا باید چیزی دربارهی قوانین محدوده در پایتون به شما بگویم. تعریف کلاسها چند ترفند جالب با فضاهای نام انجام میدهد و برای اینکه بهطور کامل متوجه شوید چه میگذرد، باید بدانید که محدودهها و فضاهای نام چگونه کار میکنند. ضمناً، آگاهی از این موضوع برای هر برنامهنویس پیشرفتهی پایتون مفید است. بیایید با چند تعریف شروع کنیم. «فضای نام» namespace نگاشتی است از نامها به اشیا. بیشتر فضای نامها در حال حاضر بهصورت دیکشنریهای پایتون پیادهسازی شدهاند، اما معمولاً این موضوع (به جز در بازدهی قابل مشاهده نیست و ممکن است در آینده تغییر کند. مثالهایی از فضای نام عبارتاند از: مجموعه نامهای توکار (که شامل توابعی مثل بههرحال، من واژهی ویژگی «attribute» را برای هر نامی که بعد از یک نقطه بیاید به کار میبرم — برای مثال، در عبارت ویژگیها ممکن است فقط خواندنی یا قابل نوشتن باشند. در حالت دوم، اختصاص دادن به ویژگیها ممکن است. ویژگیهای ماژول قابل نوشتن هستند: میتوانید فضاهای نام در لحظات مختلف ایجاد میشوند و دارای طول عمرهای متفاوتی هستند. فضای نام که شامل نامهای توکار است، زمانی که مفسر پایتون شروع به کار میکند ایجاد میشود و هرگز حذف نمیشود. فضای نام عمومی برای یک ماژول زمانی که تعریف ماژول خوانده میشود ایجاد میگردد؛ بهطور معمول، فضاهای نام ماژول نیز تا زمانی که مفسر پایان یابد باقی میمانند. دستوراتی که توسط اجرای سطح بالای مفسر، یا از فایل اسکریپت خوانده شده یا بهطور تعاملی اجرا میشوند، بهعنوان بخشی از ماژولی به نام فضای نام محلی برای یک تابع زمانی ایجاد میشود که تابع فراخوانی شود و زمانی که تابع بازمیگردد یا استثناای پرتاب میکند که در درون تابع مدیریت نمیشود، حذف میگردد. (در واقع، «فراموش شدن» توصیف بهتری برای چیزی است که واقعاً اتفاق میافتد.) البته، هر فراخوانی بازگشتی، فضای نام محلی خودش را دارد. یک محدوده یک ناحیهی متنی از یک برنامهی پایتون است که در آن یک فضای نام «namespace» بهطور مستقیم قابل دستیابی است. "بهطور مستقیم قابل دستیابی" در اینجا به این معنی است که یک ارجاع بدون صلاحیت به یک نام تلاش میکند تا نام «name» را در فضای نام «namespace» پیدا کند. اگرچه محدودهها بهصورت ایستا تعیین میشوند، اما بهصورت پویا مورد استفاده قرار میگیرند. در هر زمان حین اجرا، ۳ یا ۴ محدودهی تو در تو وجود دارند که فضاهای نام آنها مستقیماً قابل دسترسی هستند:
اگر یک نام بهعنوان سراسری اعلام شود، تمام ارجاعها و تخصیصها مستقیماً به محدودهی یکیماندهبهآخر که شامل نامهای سراسری ماژول است، میروند. برای اتصال مجدد متغیرهایی که در خارج از درونیترین محدوده یافت میشوند، میتوان از دستور معمولاً، محدودهی محلی به نامهای محلی تابع جاری (از نظر متنی) ارجاع میدهد. در خارج از توابع، محدودهی محلی به همان فضای نامی ارجاع میدهد که محدودهی سراسری به آن ارجاع میدهد: فضای نام ماژول. تعاریف کلاسها، فضای نام دیگری را در محدودهی محلی قرار میدهند. درک این نکته مهم است که محدودهها بهصورت متنی (بر اساس جایگاه در کد) تعیین میشوند: محدودهی سراسری تابعی که در یک ماژول تعریف شده است، همان فضای نام آن ماژول است، فرقی نمیکند که تابع از کجا یا با چه نام مستعاری فراخوانی شود. از سوی دیگر، جستوجوی واقعی برای نامها بهصورت پویا (داینامیک) و در زمان اجرا انجام میشود --- با این حال، تعریف زبان به سمت و سوی حلِ ایستا (استاتیک) نامها در زمان «کامپایل» پیش میرود، پس به حلِ پویای نامها اعتماد نکنید! (در واقع، متغیرهای محلی از قبل بهصورت ایستا تعیین میشوند.) یک ویژگی خاص پایتون این است که --- اگر دستور از دستور 9.2.1. مثال محدودهها و فضای نامها¶این مثالی است که نحوهی ارجاع به محدودهها و فضای نامهای مختلف، و تأثیر دستورات def scope_test(): def do_local(): spam = "local spam" def do_nonlocal(): nonlocal spam spam = "nonlocal spam" def do_global(): global spam spam = "global spam" spam = "test spam" do_local() print("After local assignment:", spam) do_nonlocal() print("After nonlocal assignment:", spam) do_global() print("After global assignment:", spam) scope_test() print("In global scope:", spam) خروجی کد مثال: After local assignment: test spam After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam توجه کنید که تخصیص محلی (که بهصورت پیشفرض است)، اتصال spam را در scope_test تغییر نداد. تخصیص همچنین میتوانید ببینید که قبل از تخصیص 9.3. در نگاه اول به کلاسها¶کلاسها کمی سینتکس جدید، سه نوع شیء جدید، و مفاهیم معنایی جدیدی را معرفی میکنند. 9.3.1. قواعد تعریف کلاس¶سادهترین شکل تعریف کلاس به این صورت است: class ClassName: <statement-1> . . . <statement-N> تعاریف کلاسها، مانند تعاریف توابع (دستورات در عمل، دستورات داخل یک تعریف کلاس معمولاً تعاریف توابع هستند، اما دستورات دیگری نیز مجاز هستند و گاهی مفیدند --- بعداً به این موضوع بازخواهیم گشت. تعاریف توابع داخل یک کلاس معمولاً دارای شکل خاصی از فهرست آرگومانها هستند که توسط قراردادهای فراخوانی برای متدها تعیین میشود --- باز هم، این موضوع بعداً توضیح داده میشود. هنگامی که یک تعریف کلاس وارد میشود، یک فضای نام جدید ایجاد شده و بهعنوان محدودهی محلی استفاده میشود --- بنابراین، تمام تخصیصها به متغیرهای محلی به این فضای نام جدید میروند. بهویژه، تعاریف توابع، نام تابع جدید را در اینجا مقید میکنند. هنگامی که یک تعریف کلاس بهطور عادی (با رسیدن به انتها) ترک میشود، یک شیء کلاس ایجاد میشود. این در اصل یک پوسته پیرامون محتویات فضای نامی است که توسط تعریف کلاس ایجاد شده است؛ در بخش بعدی بیشتر دربارهی اشیاء کلاس خواهیم آموخت. محدودهی محلی اصلی (همان محدودهای که درست قبل از ورود به تعریف کلاس فعال بود) بازیابی میشود، و شیء کلاس در اینجا به نام کلاسی که در سرآیند کلاس تعریف شده است، متصل میشود ( 9.3.2. اشیاء کلاس¶اشیاء کلاس از دو نوع عملیات پشتیبانی میکنند: ارجاع به ویژگیها و نمونهسازی. ارجاع به ویژگیها از همان دستور زبان استاندارد که برای همهی ارجاعها به ویژگیها در پایتون استفاده میشود، پیروی میکند: class MyClass: """A simple example class""" i = 12345 def f(self): return 'hello world' سپس نمونهسازی کلاس از نماد توابع استفاده میکند. فقط تصور کنید که شیء کلاس، یک تابع بدون پارامتر است که یک نمونهی جدید از کلاس را برمیگرداند. برای مثال (با فرض کلاس بالا): x = MyClass() یک نمونه جدید از کلاس ایجاد میکند و این شیء را به متغیر محلی عملیات نمونهسازی (که «فراخوانی» یک شیء کلاس است) یک شیء خالی ایجاد میکند. بسیاری از کلاسها ترجیح میدهند اشیایی با نمونههایی ایجاد کنند که به یک حالت اولیهی خاص سفارشیسازی شدهاند. بنابراین، یک کلاس ممکن است یک متد ویژه به نام def __init__(self): self.data = [] وقتی یک کلاس یک متد x = MyClass() البته، متد >>> class Complex: ... def __init__(self, realpart, imagpart): ... self.r = realpart ... self.i = imagpart ... >>> x = Complex(3.0, -4.5) >>> x.r, x.i (3.0, -4.5) 9.3.3. اشیاء نمونه¶حالا با اشیاء نمونه چه کاری میتوانیم انجام دهیم؟ تنها عملیاتی که اشیاء نمونه میفهمند، ارجاع به ویژگیهااست. دو نوع نام ویژگی معتبر وجود دارد: ویژگیهای داده (data attributes) و متدها (methods). ویژگیهای داده معادل «متغیرهای نمونه» در زبان اسمالتاک و «عضوهای داده» در ++C هستند. ویژگیهای داده نیازی به اعلام ندارند؛ مانند متغیرهای محلی، هنگامی که برای اولین بار به آنها مقدار داده میشود، بهوجود میآیند. برای مثال، اگر x.counter = 1 while x.counter < 10: x.counter = x.counter * 2 print(x.counter) del x.counter نوع دیگر ارجاع به ویژگی نمونه، متد است. متد، تابعی است که «به» یک شیء تعلق دارد. نامهای متد معتبر یک شیء نمونه به کلاس آن بستگی دارد. طبق تعریف، تمام ویژگیهای یک کلاس که شیء تابع هستند، متدهای متناظر با نمونههای آن را تعریف میکنند. بنابراین در مثال ما، 9.3.4. متد اشیاء¶معمولاً، یک متد بلافاصله پس از اتصال فراخوانی میشود: x.f() اگر xf = x.f while True: print(xf()) تا پایان زمان به چاپ دقیقاً وقتی یک متد فراخوانی میشود، چه اتفاقی میافتد؟ احتمالاً متوجه شدهاید که در واقع، شاید پاسخ را حدس زده باشید: نکتهی ویژه در مورد متدها این است که شیء نمونه بهعنوان اولین آرگومان تابع ارسال میشود. در مثال ما، فراخوانی به طور کلی، متدها به صورت زیر کار میکنند. هنگامی که به یک ویژگی غیردادهای از یک نمونه ارجاع داده میشود، کلاس آن نمونه جستوجو میشود. اگر آن نام نشاندهندهی یک ویژگی کلاس معتبر باشد که یک شیء تابع است، ارجاعات به هر دو شیء نمونه و شیء تابع در یک شیء متد بستهبندی میشوند. هنگامی که شیء متد با یک فهرست آرگومان فراخوانی میشود، یک فهرست آرگومان جدید از شیء نمونه و فهرست آرگومان ساخته میشود و شیء تابع با این فهرست آرگومان جدید فراخوانی میشود. 9.3.5. کلاس و متغیرهای نمونه¶بهطور کلی، متغیرهای نمونه برای دادههایی هستند که مختص به هر نمونه و متغیرهای کلاس برای ویژگیها و متدهایی هستند که بین همهی نمونهها از آن کلاس مشترک هستند: class Dog: kind = 'canine' # class variable shared by all instances def __init__(self, name): self.name = name # instance variable unique to each instance >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.kind # shared by all dogs 'canine' >>> e.kind # shared by all dogs 'canine' >>> d.name # unique to d 'Fido' >>> e.name # unique to e 'Buddy' همانطور که در سخنی دربارهی نامها و اشیاء بحث شد، دادههای مشترک میتوانند تأثیرات شگفتآوری بر روی اشیاء تغییرپذیر مانند فهرستها و دیکشنریها داشته باشند. برای مثال، فهرست tricks در کد زیر نباید بهعنوان یک متغیر کلاس استفاده شود، زیرا فقط یک فهرست واحد بین همهی نمونههای Dog مشترک خواهد بود: class Dog: tricks = [] # mistaken use of a class variable def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks # unexpectedly shared by all dogs ['roll over', 'play dead'] طراحی صحیح کلاس باید به جای آن از یک متغیر نمونه استفاده کند: class Dog: def __init__(self, name): self.name = name self.tricks = [] # creates a new empty list for each dog def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks ['roll over'] >>> e.tricks ['play dead'] 9.4. نکات پراکنده¶اگر نام ویژگی مشابهی هم در یک نمونه و هم در یک کلاس وجود داشته باشد، آنگاه جستوجوی ویژگی، نمونه را اولویتبندی میکند: >>> class Warehouse: ... purpose = 'storage' ... region = 'west' ... >>> w1 = Warehouse() >>> print(w1.purpose, w1.region) storage west >>> w2 = Warehouse() >>> w2.region = 'east' >>> print(w2.purpose, w2.region) storage east ویژگیهای داده ممکن است هم توسط متدها و هم توسط کاربران عادی ("clients") یک شیء ارجاع داده شوند. به عبارت دیگر، کلاسها برای پیادهسازی انواع دادهی انتزاعی خالص قابل استفاده نیستند. در واقع، هیچ چیزی در پایتون امکان اعمال پنهانسازی داده را فراهم نمیکند --- همهچیز بر اساس قرارداد است. (از سوی دیگر، پیادهسازی پایتون که به زبان C نوشته شده است، میتواند جزئیات پیادهسازی را کاملاً پنهان کند و در صورت نیاز دسترسی به یک شیء را کنترل کند؛ این قابلیت میتواند توسط افزونههای پایتون که به زبان C نوشته میشوند، استفاده شود.) کاربران (clients) باید با احتیاط از ویژگیهای داده استفاده کنند --- کاربران ممکن است ناورداهای حفظشده توسط متدها را با دستکاری در ویژگیهای دادهشان به هم بریزند. توجه داشته باشید که کاربران ممکن است ویژگیهای دادهی خود را به یک شیء نمونه اضافه کنند بدون اینکه بر صحت متدها تأثیر بگذارند، تا زمانی که تداخل نامها اجتناب شود --- باز هم، یک قرارداد نامگذاری میتواند از بسیاری از سردرگمیها در اینجا جلوگیری کند. هیچ گونه میانبری برای ارجاع به ویژگیهای داده (یا سایر متدها!) از درون متدها وجود ندارد. به نظر من، این موضوع در واقع خوانایی متدها را افزایش میدهد: هیچ شانسی برای اشتباه گرفتن متغیرهای محلی و متغیرهای نمونه هنگام مرور سریع یک متد وجود ندارد. معمولاً، اولین آرگومان یک متد هر شیء تابعی که یک ویژگی کلاس باشد، یک متد برای نمونههای آن کلاس تعریف میکند. لازم نیست که تعریف تابع لزوماً درون تعریف کلاس قرار داشته باشد: اختصاص یک شیء تابع به یک متغیر محلی در کلاس نیز مجاز است. برای مثال: # Function defined outside the class def f1(self, x, y): return min(x, x+y) class C: f = f1 def g(self): return 'hello world' h = g اکنون متدها میتوانند سایر متدها را با استفاده از ویژگیهای متد آرگومان class Bag: def __init__(self): self.data = [] def add(self, x): self.data.append(x) def addtwice(self, x): self.add(x) self.add(x) متدها ممکن است به نامهای سراسری به همان روشی که توابع معمولی اشاره میکنند، ارجاع دهند. محدودهی سراسری مرتبط با یک متد، ماژولی است که شامل تعریف آن است. (یک کلاس هرگز بهعنوان محدودهی سراسری استفاده نمیشود.) اگرچه به ندرت دلیل خوبی برای استفاده از دادههای سراسری در یک متد یافت میشود، اما کاربردهای مشروع زیادی برای محدودهی سراسری وجود دارد: برای یک چیز، توابع و ماژولهایی که به محدودهی سراسری ایمپورت شدهاند، میتوانند توسط متدها استفاده شوند، همچنین توابع و کلاسهای تعریفشده در آن. معمولاً، کلاسی که شامل متد است، خود در این محدودهی سراسری تعریف میشود، و در بخش بعدی دلایل خوبی خواهیم یافت که چرا یک متد ممکن است بخواهد به کلاس خودش ارجاع دهد. هر مقدار یک شیء است، و بنابراین دارای یک کلاس (که به آن تایپ نیز گفته میشود) میباشد. این مقدار بهصورت 9.5. ارثبری¶مسلماً، یک ویژگی زبانی بدون پشتیبانی از ارثبری، شایستهی نام «کلاس» نخواهد بود. سینتکس تعریف یک کلاس مشتقشده (derived class) به این شکل است: class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N> نام class DerivedClassName(modname.BaseClassName): اجرای تعریف یک کلاس مشتقشده به همان شیوهای است که برای یک کلاس پایه انجام میشود. هنگامی که شیء کلاس ساخته میشود، کلاس پایه به خاطر سپرده میشود. این (اطلاعات) برای حل کردن ارجاعات به ویژگیها استفاده میشود: اگر ویژگی درخواستی در کلاس یافت نشود، جستوجو در کلاس پایه ادامه مییابد. این قاعده بهصورت بازگشتی اعمال میشود اگر خود کلاس پایه از کلاس دیگری مشتق شده باشد. هیچ نکتهی خاصی دربارهی نمونهسازی کلاسهای مشتقشده وجود ندارد: کلاسهای مشتقشده ممکن است متدهای کلاسهای پایهی خود را بازنویسی کنند. از آنجایی که متدها هنگام فراخوانی سایر متدهای همان شیء، امتیاز ویژهای ندارند، یک متد از یک کلاس پایه که متد دیگری را که در همان کلاس پایه تعریف شده فراخوانی میکند، ممکن است در نهایت متدی از یک کلاس مشتقشده را فراخوانی کند که آن را بازنویسی کرده است. (برای برنامهنویسان C++: تمام متدها در پایتون بهطور مؤثر یک متد بازنویسیشده در یک کلاس مشتقشده ممکن است در واقع بخواهد به جای اینکه صرفاً متد کلاس پایه را با همین نام جایگزین کند، آن را گسترش دهد. یک راه ساده برای فراخوانی مستقیم متد کلاس پایه وجود دارد: فقط پایتون دو تابع توکار دارد که با ارثبری کار میکنند:
9.5.1. ارثبری چندگانه¶پایتون همچنین از نوعی ارثبری چندگانه پشتیبانی میکند. تعریف یک کلاس با چندین کلاس پایه به این شکل است: class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N> برای بیشتر کاربردها، در سادهترین موارد، میتوانید جستوجو برای ویژگیهای بهارثبردهشده از یک کلاس والد را بهصورت عمقاول، چپبهراست، بدون جستوجوی دوباره در همان کلاس در جایی که همپوشانی در سلسلهمراتب وجود دارد، در نظر بگیرید. بنابراین، اگر ویژگیای در در واقع، این موضوع کمی پیچیدهتر از این است؛ ترتیب حل متد بهصورت پویا تغییر میکند تا از فراخوانیهای مشارکتی به ترتیبدهی پویا ضروری است زیرا همهی موارد ارثبری چندگانه دارای یک یا چند رابطهی الماسی هستند (جایی که حداقل یکی از کلاسهای والد از طریق چندین مسیر از پایینترین کلاس قابل دسترسی است). برای مثال، همهی کلاسها از در برخی موارد وراثت چندگانه مجاز نیست؛ برای جزئیات به وراثت چندگانه مراجعه کنید. 9.6. متغیرهای خصوصی¶متغیرهای نمونهای «خصوصی» که جز از درون یک شیء قابل دسترسی نیستند، در پایتون وجود ندارند. با این حال، یک قرارداد وجود دارد که توسط بیشتر کدهای پایتون دنبال میشود: یک نام که با زیرخط آغاز میشود (مثلاً از آنجا که یک مورد استفادهی معتبر برای اعضای خصوصی کلاس وجود دارد (بهویژه برای جلوگیری از برخورد نامها با نامهای تعریفشده توسط زیرکلاسها)، پشتیبانی محدودی برای چنین مکانیزمی، به نام name mangling (دستکاری نام) وجود دارد. هر شناسهای به شکل همچنین ببینید برای جزئیات و موارد خاص، به مشخصات دستکاری نامهای خصوصی مراجعه کنید. دستکاری نام برای این مفید است که به زیرکلاسها اجازه دهد متدها را بدون شکستن فراخوانیهای متد درونکلاسی بازنویسی کنند. برای مثال: class Mapping: def __init__(self, iterable): self.items_list = [] self.__update(iterable) def update(self, iterable): for item in iterable: self.items_list.append(item) __update = update # private copy of original update() method class MappingSubclass(Mapping): def update(self, keys, values): # provides new signature for update() # but does not break __init__() for item in zip(keys, values): self.items_list.append(item) مثال بالا حتی اگر توجه داشته باشید که قوانین دستکاری نام بیشتر برای جلوگیری از اشتباهات طراحی شدهاند؛ همچنان دسترسی یا تغییر یک متغیر که خصوصی در نظر گرفته میشود، ممکن است. این حتی میتواند در شرایط خاص، مانند اشکالزدایی، مفید باشد. توجه داشته باشید که کد ارسالشده به 9.7. نکات متفرقه¶گاهی اوقات داشتن یک نوع داده مشابه «رکورد» در پاسکال یا «ساختار» در C مفید است، که چندین آیتم دادهی نامدار را کنار هم بستهبندی میکند. روش استاندارد برای این کار استفاده از from dataclasses import dataclass @dataclass class Employee: name: str dept: str salary: int >>> john = Employee('john', 'computer lab', 1000) >>> john.dept 'computer lab' >>> john.salary 1000 یک قطعه کد پایتون که انتظار یک نوع دادهی انتزاعی خاص را دارد، اغلب میتواند به جای آن، یک کلاس دریافت کند که متدهای آن نوع داده را شبیهسازی میکند. برای مثال، اگر تابعی دارید که دادههایی را از یک شیء فایل قالببندی میکند، میتوانید کلاسی تعریف کنید با متدهای اشیاء متد نمونه نیز ویژگیهایی دارند: 9.8. پیمایشگرها¶تا اینجا احتمالاً متوجه شدهاید که میتوان روی بیشتر اشیاء ظرف با استفاده از دستور for element in [1, 2, 3]: print(element) for element in (1, 2, 3): print(element) for key in {'one':1, 'two':2}: print(key) for char in "123": print(char) for line in open("myfile.txt"): print(line, end='') این سبک دسترسی واضح، مختصر و راحت است. استفاده از پیمایشگرها در همهجا نفوذ کرده و پایتون را یکپارچه میکنند. در پشت صحنه، دستور >>> s = 'abc' >>> it = iter(s) >>> it <str_iterator object at 0x10c90e650> >>> next(it) 'a' >>> next(it) 'b' >>> next(it) 'c' >>> next(it) Traceback (most recent call last): File "<stdin>", line 1, in <module> next(it) StopIteration حالا که با سازوکار پشت پروتکل پیمایشگر آشنا شدید، اضافه کردن رفتار پیمایشگر به کلاسهایتان آسان است. یک متد class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index] >>> rev = Reverse('spam') >>> iter(rev) <__main__.Reverse object at 0x00A1DB50> >>> for char in rev: ... print(char) ... m a p s 9.9. تولیدگر¶تولیدگر یک ابزار ساده و قدرتمند برای ایجاد پیمایشگرها هستند. آنها مانند توابع عادی نوشته میشوند اما هر زمان که میخواهند دادهای را بازگردانند از دستور def reverse(data): for index in range(len(data)-1, -1, -1): yield data[index] >>> for char in reverse('golf'): ... print(char) ... f l o g هر چیزی که با تولیدگرها قابل انجام است، میتواند با استفاده از پیمایشگرهای مبتنی بر کلاس نیز انجام شود، همانطور که در بخش قبلی توضیح داده شد. آنچه تولیدگرها را فشرده میکند، این است که روشهای یکی دیگر از ویژگیهای اصلی این است که متغیرهای محلی و وضعیت اجرا بهطور خودکار بین فراخوانیها ذخیره میشوند. این امر نوشتن تابع را آسانتر و خیلی واضحتر از رویکردی که از متغیرهای نمونهای مانند علاوه بر ایجاد خودکار متدها و ذخیره سازی وضعیت برنامه، زمانی که تولیدگرها خاتمه مییابند، بهطور خودکار 9.10. عبارات تولیدگر¶برخی از تولیدگرهای ساده را میتوان بهصورت مختصر بهعنوان عبارت کدنویسی کرد، با استفاده از یک سینتکس مشابه درکفهرستها اما با پرانتز به جای کروشهی باز. این عبارتها برای شرایطی طراحی شدهاند که تولیدگر بلافاصله توسط یک تابع محاطشونده استفاده میشود. عبارتهای تولیدگر فشردهتر هستند اما نسبت به تعاریف کامل تولیدگر، انعطافپذیری کمتری دارند و معمولاً نسبت به درکفهرستهای معادل، با حافظه سازگارتر هستند. مثالها: >>> sum(i*i for i in range(10)) # sum of squares 285 >>> xvec = [10, 20, 30] >>> yvec = [7, 5, 3] >>> sum(x*y for x,y in zip(xvec, yvec)) # dot product 260 >>> unique_words = set(word for line in page for word in line.split()) >>> valedictorian = max((student.gpa, student.name) for student in graduates) >>> data = 'golf' >>> list(data[i] for i in range(len(data)-1, -1, -1)) ['f', 'l', 'o', 'g'] >>> x = [[1,2,3], [], [4, 5]] >>> g = (*i for i in x) >>> list(g) [1, 2, 3, 4, 5] در بیشتر موارد، عبارتهای تولیدگر باید داخل پرانتز قرار گیرند. با این حال، به عنوان یک مورد خاص، هنگامی که به عنوان تنها آرگومان یک تابع ارائه میشوند (همانند مثالهای شامل >>> f(x for x in y) >>> f((x for x in y)) همچنین موارد زیر: >>> f(*x for x in y) >>> f((*x for x in y)) پانویسها
© حق نشر 2001 Python Software Foundation.
این صفحه تحت مجوز بنیاد نرمافزار پایتون نسخه ۲ منتشر شده است. مثالها، دستورالعملها، و سایر کدهای موجود در مستندات همچنین تحت مجوز بدون بند BSD منتشر شدهاند. برای اطلاعات بیشتر به تاریخچه و مجوز مراجعه کنید. بنیاد نرمافزار پایتون یک شرکت غیرانتفاعی است. لطفاً کمک مالی کنید. آخرین بهروزرسانی در نوامبر 12, 2026 (06:19 UTC). آیا اشکالی یافتید؟ ساخته شده با Sphinx 9.1.0. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |