تازههای پایتون 2.2¶
- نویسنده:
A.M. Kuchling
مقدمه¶
این مقاله قابلیتهای جدید پایتون 2.2.2 را که در ۱۴ اکتبر ۲۰۰۲ منتشر شده است، توضیح میدهد. پایتون 2.2.2 یک نسخهی رفع اشکال از پایتون 2.2 است که نخستینبار در ۲۱ دسامبر ۲۰۰۱ منتشر شد.
میتوان پایتون 2.2 را «نسخهی پاکسازی» دانست. برخی قابلیتها مانند تولیدگرها و پیمایشگرها کاملاً جدید هستند، اما بیشترِ تغییرات، هرچند مهم و گسترده باشند، با هدف پاکسازی ناهنجاریها و گوشههای تاریک طراحی زبان صورت گرفتهاند.
این مقاله تلاش نمیکند مشخصات کاملی از ویژگیهای جدید ارائه دهد، بلکه در عوض مروری مناسب ارائه میکند. برای جزئیات کامل، باید به مستندات پایتون 2.2 مانند مرجع کتابخانه پایتون و راهنمای مرجع پایتون مراجعه کنید. اگر میخواهید پیادهسازی کامل و منطق طراحی یک تغییر را درک کنید، به PEP (پیشنهاد بهبود پایتون) مربوط به ویژگی جدید مورد نظر مراجعه کنید.
PEPهای 252 و 253: تغییرات نوع و کلاس¶
بزرگترین و فراگیرترین تغییرات در پایتون 2.2 مربوط به مدل شیءها و کلاسهای پایتون است. این تغییرات باید سازگار به عقب باشند، بنابراین به احتمال زیاد کد شما بدون تغییر به کار خود ادامه خواهد داد، اما این تغییرات چند قابلیت جدید شگفتانگیز فراهم میکنند. پیش از شروع این بخش — طولانیترین و پیچیدهترین بخش این مقاله — مروری کلی بر تغییرات ارائه خواهم داد و چند نکته نیز مطرح خواهم کرد.
مدتها پیش صفحهای وب نوشتم که فهرستی از ایرادهای طراحی پایتون را در بر میگرفت. یکی از مهمترین این ایرادها این بود که زیرکلاسسازی نوعهای پایتون که در C پیادهسازی شدهاند ناممکن است. بهطور خاص، زیرکلاسسازی نوعهای توکار ممکن نیست، بنابراین نمیتوانید بهسادگی مثلاً از فهرستها زیرکلاس بگیرید تا تنها یک متد مفید به آنها بیفزایید. ماژول UserList کلاسی فراهم میکند که از تمام متدهای فهرستها پشتیبانی میکند و میتوان از آن زیرکلاسهای بیشتری ساخت، اما کدهای C زیادی وجود دارند که انتظار یک فهرست معمولی پایتون را دارند و نمونهی UserList را نمیپذیرند.
پایتون 2.2 این مشکل را برطرف میکند و در این فرایند، چند قابلیت جدید و هیجانانگیز اضافه میکند. خلاصهای کوتاه:
میتوانید از انواع توکار مانند فهرستها و حتی اعداد صحیح زیرکلاس بسازید، و زیرکلاسهای شما باید در هر جایی که به نوع اصلی نیاز است، کار کنند.
اکنون میتوان علاوه بر متدهای نمونه که در نسخههای قبلی پایتون در دسترس بودند، متدهای ایستا و متدهای کلاس را نیز تعریف کرد.
همچنین میتوان با استفاده از سازوکار جدیدی به نام پراپرتیها <properties>، متدها را هنگام دسترسی به یک ویژگی نمونه یا تنظیم آن بهطور خودکار فراخوانی کرد. بسیاری از کاربردهای
__getattr__()را میتوان بازنویسی کرد تا در عوض از پراپرتیها استفاده کنند، که این امر کد حاصل را سادهتر و سریعتر میکند. بهعنوان یک مزیت جانبی کوچک، ویژگیها اکنون میتوانند رشتههای مستند نیز داشته باشند.فهرست ویژگیهای مجاز برای یک نمونه را میتوان با استفاده از slots به مجموعهای خاص محدود کرد، که این امر هم امکان محافظت در برابر غلطهای تایپی را فراهم میکند و هم شاید بهینهسازیهای بیشتری را در نسخههای آیندهی پایتون ممکن میسازد.
برخی از کاربران دربارهی همهی این تغییرات ابراز نگرانی کردهاند. البته، به گفتهی آنها، قابلیتهای جدید جالب هستند و امکان انجام انواع ترفندهایی را فراهم میکنند که در نسخههای قبلی پایتون ممکن نبود، اما در عین حال زبان را پیچیدهتر نیز میکنند. برخی از افراد گفتهاند که همیشه پایتون را به دلیل سادگیاش توصیه کردهاند و احساس میکنند که این سادگی در حال از دست رفتن است.
شخصاً فکر میکنم نیازی به نگرانی نیست. بسیاری از قابلیتهای جدید کاملاً تخصصی و خاص هستند، و شما میتوانید مقدار زیادی کد پایتون بنویسید بدون اینکه هرگز نیازی به آگاهی از آنها داشته باشید. نوشتن یک کلاس ساده هیچ سختتر از گذشته نیست، بنابراین لازم نیست زحمت یادگیری یا آموزش آنها را بکشید، مگر اینکه واقعاً به آنها نیاز باشد. برخی از کارهای بسیار پیچیده که پیشتر تنها از طریق C ممکن بودند، اکنون در پایتون خالص امکانپذیر خواهند بود، و به باور من، همهی اینها چیز خوبی است.
قصد ندارم تلاش کنم تا تکتک حالتهای گوشهای (corner case) و تغییرات کوچکی را که برای کار کردن قابلیتهای جدید لازم بودند، پوشش دهم. در عوض، این بخش تنها سطرهای کلی را ترسیم خواهد کرد. برای منابع اطلاعاتی بیشتر دربارهی مدل شیء جدید پایتون 2.2، بخش پیوندهای مرتبط با عنوان «پیوندهای مرتبط» را ببینید.
کلاسهای قدیمی و جدید¶
نخست باید بدانید که پایتون 2.2 در واقع دو نوع کلاس دارد: کلاسهای کلاسیک یا سبک قدیمی، و کلاسهای سبک جدید. مدل کلاسهای سبک قدیمی دقیقاً همان مدل کلاس در نسخههای پیشین پایتون است. تمامی ویژگیهای جدیدی که در این بخش شرح داده شدهاند، تنها بر کلاسهای سبک جدید اعمال میشوند. این واگرایی قرار نیست برای همیشه ادامه یابد؛ در نهایت کلاسهای سبک قدیمی حذف خواهند شد، احتمالاً در پایتون 3.0.
پس چگونه یک کلاس سبک جدید تعریف میکنید؟ این کار را با زیرکلاس گرفتن از یک کلاس سبک جدید موجود انجام میدهید. بیشتر نوعهای توکار پایتون، مانند اعداد صحیح، فهرستها، دیکشنریها و حتی پروندهها، اکنون کلاسهای سبک جدید هستند. یک کلاس سبک جدید با نام object، که کلاس پایهی همهی نوعهای توکار است، نیز اضافه شده است تا در صورتی که هیچ نوع توکاری مناسب نباشد، بتوانید بهسادگی از object زیرکلاس بگیرید:
class C(object):
def __init__ (self):
...
...
این بدان معناست که دستورهای class که هیچ کلاس پایهای ندارند، در پایتون 2.2 همیشه کلاسهای کلاسیک هستند. (در واقع میتوانید با تنظیم متغیری در سطح ماژول به نام __metaclass__ این موضوع را نیز تغییر دهید --- برای جزئیات به PEP 253 مراجعه کنید --- اما آسانتر این است که صرفاً زیرکلاسی از object بسازید.)
اشیاء نوع برای نوعهای توکار بهصورت توکار در دسترس هستند و با استفاده از ترفندی هوشمندانه نامگذاری شدهاند. پایتون همیشه توابع توکار با نامهای int()، float() و str() داشته است. در نسخهی 2.2، آنها دیگر توابع نیستند، بلکه اشیاء نوع هستند که هنگام فراخوانی مانند کارخانه رفتار میکنند.
>>> int
<type 'int'>
>>> int('123')
123
برای کامل شدن مجموعهی انواع، اشیاء نوع جدیدی مانند dict() و file() اضافه شدهاند. در اینجا مثال جالبتری را میبینید که متد lock() را به اشیاء پرونده اضافه میکند:
class LockableFile(file):
def lock (self, operation, length=0, start=0, whence=0):
import fcntl
return fcntl.lockf(self.fileno(), operation,
length, start, whence)
ماژول posixfile که اکنون منسوخ شده است، کلاسی داشت که تمام متدهای یک شیء پرونده را شبیهسازی میکرد و همچنین یک متد lock() اضافه میکرد، اما این کلاس را نمیشد به توابع داخلیای که انتظار یک پرونده توکار را داشتند، پاس داد؛ امری که با LockableFile جدید ما امکانپذیر است.
توصیفگرها¶
در نسخههای پیشین پایتون، هیچ راه منسجمی برای کشف اینکه یک شیء از چه ویژگیها و متدهایی پشتیبانی میکرد وجود نداشت. برخی قراردادهای غیررسمی وجود داشت، مانند تعریف ویژگیهای __members__ و __methods__ که فهرستهایی از نامها بودند، اما اغلب نویسندهی یک نوع توسعهای یا یک کلاس زحمت تعریف آنها را به خود نمیداد. میتوانستید به بررسی __dict__ یک شیء روی بیاورید، اما وقتی وراثت کلاسی یا یک قلاب دلخواه __getattr__() به کار میرفت، این روش همچنان میتوانست نادرست باشد.
ایدهی بزرگ بنیادین مدل جدید کلاس این است که APIای برای توصیف ویژگیهای یک شیء با استفاده از توصیفگرها <descriptors> صورتبندی شده است. توصیفگرها مقدار یک ویژگی را مشخص میکنند و بیان میکنند که آیا این ویژگی یک متد است یا یک فیلد. با API توصیفگر، متدهای ایستا و متدهای کلاس، و همچنین ساختارهای غیرمعمولتری ممکن میشوند.
توصیفگرهای ویژگی، اشیاءی هستند که درون اشیاء کلاس قرار میگیرند و چند ویژگی مخصوص به خود دارند:
__name__نام ویژگی است.__doc__رشته مستند این ویژگی است.__get__(object)متدی است که مقدار ویژگی را از شیء بازیابی میکند.__set__(object, value)ویژگی روی object را به value تنظیم میکند.__delete__(object, value)ویژگی value از object را حذف میکند.
برای مثال، هنگامی که obj.x را مینویسید، گامهایی که پایتون در واقع انجام میدهد عبارتاند از:
descriptor = obj.__class__.x
descriptor.__get__(obj)
برای متدها، descriptor.__get__ یک شیء موقت فراخوانیپذیر برمیگرداند که نمونه و متدی را که باید روی آن فراخوانی شود دربر میگیرد. این همچنین توضیح میدهد که چرا متدهای ایستا و متدهای کلاس اکنون ممکن هستند؛ آنها توصیفگرهایی دارند که فقط متد، یا متد و کلاس را دربر میگیرند. بهعنوان توضیحی مختصر دربارهی این نوعهای جدید متد، به متدهای ایستا نمونه ارسال نمیشود و بنابراین آنها شبیه توابع معمولی هستند. به متدهای کلاس، کلاس شیء ارسال میشود، اما نه خودِ شیء. متدهای ایستا و متدهای کلاس به این صورت تعریف میشوند:
class C(object):
def f(arg1, arg2):
...
f = staticmethod(f)
def g(cls, arg1, arg2):
...
g = classmethod(g)
تابع staticmethod() تابع f() را میگیرد و آن را درون یک توصیفگر دربرگرفته برمیگرداند تا بتوان آن را در شیء کلاس ذخیره کرد. شاید انتظار داشته باشید که سینتکس ویژهای برای ایجاد چنین متدهایی وجود داشته باشد (def static f، defstatic f() یا چیزی شبیه به آن)، اما چنین نحوی هنوز تعریف نشده است؛ این موضوع به نسخههای آیندهی پایتون موکول شده است.
قابلیتهای جدید دیگری مانند جایگاهها و پراپرتیها نیز بهصورت گونههای جدیدی از توصیفگر پیادهسازی شدهاند، و نوشتن کلاس توصیفگری که کاری نو انجام دهد دشوار نیست. برای مثال، میتوان کلاس توصیفگری نوشت که امکان نوشتن پیششرطها و پسشرطها به سبک Eiffel برای یک متد را فراهم کند. کلاسی که از این قابلیت استفاده میکند، ممکن است به این صورت تعریف شود:
from eiffel import eiffelmethod
class C(object):
def f(self, arg1, arg2):
# The actual function
...
def pre_f(self):
# Check preconditions
...
def post_f(self):
# Check postconditions
...
f = eiffelmethod(f, pre_f, post_f)
توجه داشته باشید که کسی که از eiffelmethod() جدید استفاده میکند، لازم نیست هیچ چیز دربارهی توصیفگرها بداند. به همین دلیل است که به نظرم قابلیتهای جدید، پیچیدگی پایهای زبان را افزایش نمیدهند. تعداد کمی متخصص خبره خواهند بود که برای نوشتن eiffelmethod() یا ZODB یا هر چیز دیگری از این دست باید دربارهی آن بدانند، اما بیشتر کاربران صرفاً کد خود را بر پایهی کتابخانههای حاصل مینویسند و جزئیات پیادهسازی را نادیده میگیرند.
وراثت چندگانه: قانون لوزی¶
وراثت چندگانه نیز با تغییر قواعدی که نامها بر اساس آنها حل میشوند، کاربردیتر شده است. این مجموعه از کلاسها را در نظر بگیرید (نمودار برگرفته از PEP 253 نوشتهی Guido van Rossum):
class A:
^ ^ def save(self): ...
/ \
/ \
/ \
/ \
class B class C:
^ ^ def save(self): ...
\ /
\ /
\ /
\ /
class D
قاعدهی جستجو برای کلاسهای کلاسیک ساده اما نه چندان هوشمند است؛ کلاسهای پایه بهصورت اولعمق و از چپ به راست جستجو میشوند. ارجاع به D.save() کلاسهای D، B و سپس A را جستجو میکند که در آن save() یافته و بازگردانده میشود. C.save() بههیچوجه یافت نخواهد شد. این امر بد است، زیرا اگر متد save() کلاس C در حال ذخیرهی وضعیت داخلی مختص C باشد، عدم فراخوانی آن باعث میشود که آن وضعیت هرگز ذخیره نشود.
کلاسهای سبک جدید الگوریتم متفاوتی را دنبال میکنند که توضیح آن کمی پیچیدهتر است، اما در این وضعیت کار درست را انجام میدهد. (توجه داشته باشید که Python 2.3 این الگوریتم را به الگوریتمی تغییر میدهد که در بیشتر موارد نتایج یکسانی تولید میکند، اما برای گرافهای وراثت واقعاً پیچیده، نتایج مفیدتری تولید میکند.)
تمام کلاسهای پایه را با پیروی از قاعدهی جستجوی کلاسیک فهرست کنید و در صورتی که کلاسی بهطور مکرر بازدید شود، آن را چند بار در فهرست قرار دهید. در مثال بالا، فهرست کلاسهای بازدیدشده [
D,B,A,C,A] است.فهرست را برای کلاسهای تکراری بررسی کنید. اگر کلاس تکراریای یافت شد، همهی موارد بهجز یکی را حذف کنید تا آخرین مورد در فهرست باقی بماند. در مثال بالا، فهرست پس از حذف موارد تکراری به [
D,B,C,A] تبدیل میشود.
بر اساس این قاعده، ارجاع به D.save()، C.save() را برمیگرداند که همان رفتاری است که به دنبال آن هستیم. این قاعدهی جستجو همان قاعدهای است که Common Lisp از آن پیروی میکند. یک تابع توکار جدید، super()، راهی برای دسترسی به کلاسهای والد یک کلاس فراهم میکند، بدون آنکه لازم باشد الگوریتم پایتون را دوباره پیادهسازی کنید. رایجترین شکل استفاده، super(class, obj) خواهد بود که یک شیء مقید از کلاس والد را برمیگرداند (نه شیء کلاس واقعی). از این شکل در متدها برای فراخوانی متدی در کلاس والد استفاده خواهد شد؛ برای مثال، متد save() کلاس D به این شکل خواهد بود:
class D (B,C):
def save (self):
# Call superclass .save()
super(D, self).save()
# Save D's private information here
...
super() هنگامی که بهصورت super(class) یا super(class1, class2) فراخوانی شود، میتواند شیءهای غیرمقید کلاس والد (superclass) را نیز بازگرداند، اما این احتمالاً اغلب مفید نخواهد بود.
دسترسی به ویژگی¶
تعداد قابلتوجهی از کلاسهای پیشرفته پایتون با استفاده از __getattr__() قلابهایی برای دسترسی به ویژگی تعریف میکنند؛ در بیشتر موارد این کار برای سهولت انجام میشود تا کد با نگاشت خودکار دسترسی به ویژگیای مانند obj.parent به فراخوانی متدی مانند obj.get_parent خواناتر شود. پایتون 2.2 چند روش جدید برای کنترل دسترسی به ویژگی اضافه میکند.
نخست، __getattr__(attr_name) همچنان توسط کلاسهای سبک جدید (new-style) پشتیبانی میشود و هیچچیز دربارهی آن تغییر نکرده است. مانند قبل، هنگامی فراخوانی میشود که تلاشی برای دسترسی به obj.foo صورت گیرد و ویژگیای با نام foo در دیکشنری نمونه یافت نشود.
کلاسهای سبک جدید همچنین از یک متد جدید، __getattribute__(attr_name) پشتیبانی میکنند. تفاوت بین این دو متد این است که __getattribute__() همیشه هر زمان که به هر ویژگیای دسترسی شود فراخوانی میشود، در حالی که __getattr__() قدیمی تنها زمانی فراخوانی میشود که foo در دیکشنری نمونه پیدا نشود.
با این حال، پشتیبانی پایتون 2.2 از پراپرتیها <properties> اغلب راهی سادهتر برای رهگیری ارجاعهای ویژگی خواهد بود. نوشتن متد __getattr__() پیچیده است، زیرا برای پرهیز از بازگشت نمیتوانید درون آنها از دسترسیهای عادی به ویژگی استفاده کنید و در عوض باید محتوای __dict__ را دستکاری کنید. متدهای __getattr__() همچنین هنگامی که پایتون متدهای دیگر مانند __repr__() یا __coerce__() را بررسی میکند، در نهایت توسط پایتون فراخوانی میشوند و بنابراین باید با در نظر گرفتن این موضوع نوشته شوند. در آخر، فراخوانی یک تابع به ازای هر دسترسی به ویژگی، به افت کارایی قابلتوجهی منجر میشود.
property نوع توکار جدیدی است که سه تابع — که یک ویژگی را دریافت، تنظیم یا حذف میکنند — و یک رشته مستند را بستهبندی میکند. برای مثال، اگر بخواهید ویژگی size را تعریف کنید که محاسبه میشود، اما قابل تنظیم نیز هست، میتوانید بنویسید:
class C(object):
def get_size (self):
result = ... computation ...
return result
def set_size (self, size):
... compute something based on the size
and set internal state appropriately ...
# Define a property. The 'delete this attribute'
# method is defined as None, so the attribute
# can't be deleted.
size = property(get_size, set_size,
None,
"Storage size of this instance")
این کار قطعاً از یک جفت متد __getattr__()/__setattr__() — که ویژگی size را بررسی میکنند و آن را بهطور خاص مدیریت میکنند، در حالی که تمام ویژگیهای دیگر را از __dict__ نمونه بازیابی میکنند — روشنتر است و نوشتن آن نیز آسانتر است. دسترسیهای به size همچنین تنها مواردی هستند که باید کار فراخوانی یک تابع را انجام دهند؛ بنابراین ارجاعهای به ویژگیهای دیگر با سرعت معمول خود اجرا میشوند.
در نهایت، میتوان با استفاده از ویژگی کلاس جدید __slots__ فهرست ویژگیهایی را که میتوان روی یک شیء به آنها ارجاع داد، محدود کرد. اشیاء پایتون معمولاً بسیار پویا هستند؛ در هر لحظه میتوان صرفاً با انجام obj.new_attr=1 یک ویژگی جدید روی یک نمونه تعریف کرد. یک کلاس به سبک جدید میتواند ویژگی کلاسی به نام __slots__ تعریف کند تا ویژگیهای مجاز را به مجموعهای خاص از نامها محدود کند. یک مثال این موضوع را روشن میکند:
>>> class C(object):
... __slots__ = ('template', 'name')
...
>>> obj = C()
>>> print obj.template
None
>>> obj.template = 'Test'
>>> print obj.template
Test
>>> obj.newattr = None
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'newattr'
توجه کنید که چگونه هنگام تلاش برای انتساب به ویژگیای که در __slots__ فهرست نشده است، با خطای AttributeError مواجه میشوید.
PEP 234: پیمایشگرها¶
افزوده مهم دیگر در 2.2، یک رابط پیمایش در هر دو سطح C و پایتون است. اشیاء میتوانند تعریف کنند که چگونه میتوانند توسط فراخوانندهها پیمایش شوند.
در نسخههای پایتون تا 2.1، روش معمول برای اینکه for item in obj کار کند، تعریف متد __getitem__() است که چیزی شبیه به این است:
def __getitem__(self, index):
return <next item>
__getitem__() بهطور صحیحتری برای تعریف عملیات اندیسگذاری روی یک شیء به کار میرود تا بتوانید برای بازیابی ششمین عنصر، obj[5] بنویسید. وقتی تنها از آن برای پشتیبانی از حلقههای for استفاده میکنید، کمی گمراهکننده است. یک شیء شبهپرونده را در نظر بگیرید که میخواهد پیمایش شود؛ پارامتر اندیس اساساً بیمعناست، زیرا کلاس احتمالاً فرض میکند که مجموعهای از فراخوانیهای __getitem__() انجام خواهد شد که در آن هر بار اندیس یک واحد افزایش مییابد. به عبارت دیگر، وجود متد __getitem__() به این معنا نیست که استفاده از file[5] برای دسترسی تصادفی به ششمین عنصر کار خواهد کرد، هرچند واقعاً باید چنین باشد.
در پایتون 2.2، پیمایش میتواند بهصورت جداگانه پیادهسازی شود و متدهای __getitem__() میتوانند به کلاسهایی محدود شوند که واقعاً از دسترسی تصادفی پشتیبانی میکنند. ایدهی بنیادی پیمایشگرها ساده است. تابع توکار جدیدی، iter(obj) یا iter(C, sentinel)، برای به دست آوردن یک پیمایشگر به کار میرود. iter(obj) پیمایشگری برای شیء obj برمیگرداند، در حالی که iter(C, sentinel) پیمایشگری برمیگرداند که شیء فراخوانیپذیر C را فراخوانی میکند تا زمانی که sentinel را برگرداند تا نشان دهد که کار پیمایشگر به پایان رسیده است.
کلاسهای پایتون میتوانند متدی به نام __iter__() تعریف کنند که باید یک پیمایشگر جدید برای آن شیء ایجاد کرده و آن را بازگرداند؛ اگر شیء پیمایشگر خودش باشد، این متد میتواند صرفاً self را برگرداند. بهطور خاص، پیمایشگرها معمولاً پیمایشگرهای خودشان هستند. نوعهای توسعهای پیادهسازیشده در C میتوانند برای بازگرداندن یک پیمایشگر، تابع tp_iter را پیادهسازی کنند، و نوعهای توسعهای که میخواهند مانند پیمایشگرها رفتار کنند میتوانند تابع tp_iternext را تعریف کنند.
پس، بعد از همهی این موارد، پیمایشگرها در واقع چه کاری انجام میدهند؟ آنها یک متد الزامی به نام next() دارند که هیچ آرگومانی نمیگیرد و مقدار بعدی را برمیگرداند. وقتی دیگر مقداری برای بازگرداندن وجود ندارد، فراخوانی next() باید استثنای StopIteration را ایجاد کند.
>>> L = [1,2,3]
>>> i = iter(L)
>>> print i
<iterator object at 0x8116870>
>>> i.next()
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
>>>
در نسخه 2.2، دستور for پایتون دیگر انتظار یک دنباله را ندارد؛ بلکه انتظار چیزی را دارد که iter() برای آن یک پیمایشگر برگرداند. برای سازگاری با نسخههای قبلی و برای راحتی، یک پیمایشگر بهطور خودکار برای دنبالههایی ساخته میشود که __iter__() یا جایگاه tp_iter را پیادهسازی نمیکنند، بنابراین for i in [1,2,3] همچنان کار خواهد کرد. در هر جایی که مفسر پایتون بر روی یک دنباله حلقه میزند، بهگونهای تغییر کرده است که از پروتکل پیمایشگر استفاده کند. این بدان معناست که میتوانید کارهایی مانند این انجام دهید:
>>> L = [1,2,3]
>>> i = iter(L)
>>> a,b,c = i
>>> a,b,c
(1, 2, 3)
پشتیبانی از پیمایشگر به برخی از نوعهای پایهی پایتون اضافهشده است. فراخوانی iter() روی یک دیکشنری، پیمایشگری را برمیگرداند که روی کلیدهای آن حلقه میزند:
>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
... 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
>>> for key in m: print key, m[key]
...
Mar 3
Feb 2
Aug 8
Sep 9
May 5
Jun 6
Jul 7
Jan 1
Apr 4
Nov 11
Dec 12
Oct 10
این صرفاً رفتار پیشفرض است. اگر میخواهید بر روی کلیدها، مقادیر یا جفتهای کلید/مقدار پیمایش کنید، میتوانید متدهای iterkeys()، itervalues() یا iteritems() را بهطور صریح فراخوانی کنید تا یک پیمایشگر مناسب به دست آورید. در یک تغییر جزئی مرتبط، عملگر in اکنون بر روی دیکشنریها کار میکند، بنابراین key in dict اکنون معادل dict.has_key(key) است.
پروندهها همچنین یک پیمایشگر فراهم میکنند که متد readline() را تا زمانی که دیگر سطری در پرونده نباشد فراخوانی میکند. این بدان معناست که اکنون میتوانید هر سطر از یک پرونده را با کدی مانند این بخوانید:
for line in file:
# do something for each line
...
توجه داشته باشید که در یک پیمایشگر تنها میتوانید به جلو حرکت کنید؛ هیچ راهی برای دریافت عنصر قبلی، بازنشانی پیمایشگر یا ساخت رونوشتی از آن وجود ندارد. شیء پیمایشگر میتواند چنین قابلیتهای اضافیای را فراهم کند، اما پروتکل پیمایشگر تنها به یک متد next() نیاز دارد.
همچنین ملاحظه نمائید
- PEP 234 - پیمایشگرها
نوشتهی Ka-Ping Yee و GvR؛ پیادهسازیشده توسط گروه Python Labs، عمدتاً توسط GvR و Tim Peters.
PEP 255: تولیدگرهای ساده¶
تولیدگرها ویژگی جدید دیگری هستند که با معرفی پیمایشگرها برهمکنش دارد.
بیشک با نحوهی کار فراخوانی توابع در پایتون یا C آشنایی دارید. وقتی تابعی را فراخوانی میکنید، تابع یک فضای نام خصوصی میگیرد که متغیرهای محلیاش در آن ایجاد میشوند. وقتی تابع به دستور return میرسد، متغیرهای محلی نابود میشوند و مقدار حاصل به فراخوانیکننده بازگردانده میشود. فراخوانی بعدی همان تابع، مجموعهای تازه و جدید از متغیرهای محلی به دست میآورد. اما، اگر متغیرهای محلی هنگام خروج از تابع دور ریخته نمیشدند چه؟ اگر میتوانستید بعداً تابع را از همانجایی که متوقف شده بود از سر بگیرید چه؟ این همان چیزی است که تولیدگرها فراهم میکنند؛ میتوان آنها را توابعی قابل ازسرگیری در نظر گرفت.
این سادهترین مثال یک تابع تولیدگر است:
def generate_ints(N):
for i in range(N):
yield i
کلیدواژهی جدیدی با نام yield برای تولیدگرها معرفی شد. هر تابعی که شامل دستور yield باشد، یک تابع تولیدگر است؛ کامپایلر بایتکد پایتون این موضوع را تشخیص میدهد و در نتیجه، تابع را بهطور خاص کامپایل میکند. از آنجا که کلیدواژهی جدیدی معرفی شده است، تولیدگرها باید بهطور صریح در یک ماژول با گنجاندن دستور from __future__ import generators نزدیک به ابتدای کد منبع ماژول فعال شوند. در پایتون 2.3 این دستور دیگر لازم نخواهد بود.
هنگامی که یک تابع تولیدگر را فراخوانی میکنید، این تابع یک مقدار واحد بازنمیگرداند؛ در عوض، شیء تولیدگری را برمیگرداند که از پروتکل پیمایشگر پشتیبانی میکند. هنگام اجرای دستور yield، تولیدگر مقدار i را خروجی میدهد، مشابه دستور return. تفاوت بزرگ بین yield و دستور return این است که هنگام رسیدن به yield، وضعیت اجرای تولیدگر معلق میشود و متغیرهای محلی حفظ میشوند. در فراخوانی بعدی متد next() تولیدگر، تابع بلافاصله پس از دستور yield اجرا را از سر میگیرد. (به دلایل پیچیده، دستور yield در بلوک try یک دستور try...finally مجاز نیست؛ برای توضیح کامل تعامل بین yield و استثناها، PEP 255 را بخوانید.)
در ادامه نمونهای از استفاده از تولیدگر generate_ints() آمده است:
>>> gen = generate_ints(3)
>>> gen
<generator object at 0x8117f90>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in generate_ints
StopIteration
میتوانید به همان خوبی for i in generate_ints(5) بنویسید، یا a,b,c = generate_ints(3).
درون یک تابع تولیدگر، دستور return تنها میتواند بدون مقدار استفاده شود و پایان جریان مقادیر را اعلام میکند؛ پس از آن تولیدگر نمیتواند مقادیر بیشتری را بازگرداند. return همراه با مقدار، مانند return 5، درون تابع تولیدگر یک خطای نحوی است. پایان نتایج تولیدگر را همچنین میتوان با پرتاب دستی StopIteration یا صرفاً با اجازه دادن به جریان اجرا برای خروج از انتهای تابع مشخص کرد.
میتوانید با نوشتن کلاس خودتان و ذخیرهسازی تمام متغیرهای محلی تولیدگر بهعنوان متغیرهای نمونه، اثر تولیدگرها را بهصورت دستی به دست آورید. برای مثال، برگرداندن فهرستی از اعداد صحیح را میتوان با قرار دادن self.count برابر با ۰ و افزایش دادن self.count و برگرداندن آن در متد next() انجام داد. با این حال، برای یک تولیدگر نسبتاً پیچیده، نوشتن کلاس متناظر بسیار بههمریختهتر خواهد بود. Lib/test/test_generators.py شامل تعدادی مثال جالبتر است. سادهترین آنها پیمایش میانترتیب یک درخت را با استفاده از تولیدگرها بهصورت بازگشتی پیادهسازی میکند.
# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
دو نمونهی دیگر در Lib/test/test_generators.py راهحلهایی برای مسئلهی N وزیر (قرار دادن $N$ وزیر روی یک صفحهی شطرنج $NxN$ بهطوریکه هیچ وزیری دیگری را تهدید نکند) و مسیر اسب (مسیری که اسب را به همهی خانههای یک صفحهی شطرنج $NxN$ میبرد، بدون اینکه از هیچ خانهای دو بار عبور کند) تولید میکنند.
ایدهی تولیدگرها از زبانهای برنامهنویسی دیگر برخاسته است، بهویژه از Icon (https://www2.cs.arizona.edu/icon/)، که در آن ایدهی تولیدگرها نقشی محوری دارد. در Icon، هر عبارت و هر فراخوانی تابع مانند یک تولیدگر رفتار میکند. یک نمونه از «An Overview of the Icon Programming Language» در https://www2.cs.arizona.edu/icon/docs/ipd266.htm تصویری از چگونگی این موضوع به دست میدهد:
sentence := "Store it in the neighboring harbor"
if (i := find("or", sentence)) > 5 then write(i)
در Icon، تابع find() اندیسهایی را که زیررشتهی "or" در آنها یافت میشود برمیگرداند: ۳، ۲۳، ۳۳. در دستور if، ابتدا مقدار ۳ به i انتساب داده میشود، اما ۳ کمتر از ۵ است، بنابراین مقایسه شکست میخورد و Icon آن را با مقدار دوم یعنی ۲۳ دوباره امتحان میکند. ۲۳ بزرگتر از ۵ است، بنابراین این بار مقایسه موفق میشود و کد مقدار ۲۳ را روی صفحه چاپ میکند.
پایتون به هیچوجه به اندازهی Icon در پذیرش تولیدگرها بهعنوان یک مفهوم مرکزی پیش نمیرود. تولیدگرها بخش جدیدی از هستهی زبان پایتون محسوب میشوند، اما یادگیری یا استفاده از آنها اجباری نیست؛ اگر هیچیک از مشکلات شما را حل نمیکنند، میتوانید با خیال راحت آنها را نادیده بگیرید. یکی از ویژگیهای نوآورانهی رابط پایتون در مقایسه با رابط Icon این است که وضعیت یک تولیدگر بهصورت یک شیء مشخص (پیمایشگر) نمایش داده میشود که میتوان آن را به توابع دیگر پاس داد یا در یک ساختار داده ذخیره کرد.
همچنین ملاحظه نمائید
- PEP 255 - تولیدگرهای ساده
نوشتهی Neil Schemenauer، Tim Peters و Magnus Lie Hetland. پیادهسازی آن عمدتاً توسط Neil Schemenauer و Tim Peters انجام شده است، همراه با اصلاحات دیگر از سوی گروه Python Labs.
PEP 237: یکسانسازی اعداد صحیح بلند و اعداد صحیح¶
در نسخههای اخیر، تمایز بین اعداد صحیح معمولی، که در بیشتر رایانهها مقادیر ۳۲ بیتی هستند، و اعداد صحیح بلند، که میتوانند اندازه دلخواه داشته باشند، در حال تبدیل شدن به یک مزاحمت بود. برای مثال، در پلتفرمهایی که از پروندههای بزرگتر از 2**32 بایت پشتیبانی میکنند، متد tell() اشیای پرونده باید یک عدد صحیح بلند را بازگرداند. با این حال، بخشهای مختلفی از پایتون وجود داشتند که انتظار اعداد صحیح ساده را داشتند و اگر به جای آن یک عدد صحیح بلند ارائه میشد، خطا ایجاد میکردند. برای مثال، در پایتون 1.5، فقط اعداد صحیح معمولی میتوانستند به عنوان اندیس اسلایس استفاده شوند و 'abc'[1L:] استثنای TypeError را با پیام 'slice index must be int' ایجاد میکرد.
پایتون 2.2 در صورت نیاز مقادیر را از اعداد صحیح کوتاه به اعداد صحیح بلند تبدیل میکند. پسوند 'L' دیگر برای نشان دادن یک مقدار لفظی عدد صحیح بلند لازم نیست، زیرا اکنون کامپایلر نوع مناسب را انتخاب میکند. (استفاده از پسوند 'L' در نسخههای 2.x آینده پایتون توصیه نخواهد شد، در پایتون 2.4 هشداری صادر میکند و احتمالاً در پایتون 3.0 حذف خواهد شد.) بسیاری از عملیاتی که پیشتر استثنای OverflowError ایجاد میکردند، اکنون یک عدد صحیح بلند را بهعنوان نتیجه برمیگردانند. برای مثال:
>>> 1234567890123
1234567890123L
>>> 2 ** 64
18446744073709551616L
در بیشتر موارد، اعداد صحیح و اعداد صحیح بلند از این پس بهطور یکسان در نظر گرفته میشوند. شما همچنان میتوانید آنها را با تابع توکار type() از هم تشخیص دهید، اما این کار بهندرت لازم است.
همچنین ملاحظه نمائید
- PEP 237 - یکسانسازی اعداد صحیح بلند و اعداد صحیح
نوشتهشده توسط Moshe Zadka و Guido van Rossum. پیادهسازیشده عمدتاً توسط Guido van Rossum.
PEP 238: تغییر عملگر تقسیم¶
جنجالیترین تغییر در پایتون 2.2، آغازگر تلاشی برای رفع نقص طراحی قدیمیای است که از ابتدا در پایتون وجود داشته است. در حال حاضر، عملگر تقسیم پایتون، /، هنگامی که با دو آرگومان عدد صحیح روبهرو شود، مانند عملگر تقسیم C رفتار میکند: نتیجهای از نوع عدد صحیح برمیگرداند که در صورت وجود بخش کسری، به سمت پایین گرد میشود. برای مثال، 3/2 برابر با ۱ است، نه ۱٫۵، و (-1)/2 برابر با -۱ است، نه -۰٫۵. این بدان معناست که نتایج تقسیم میتوانند بسته به نوع دو عملوند، بهطور غیرمنتظرهای متفاوت باشند و از آنجا که پایتون به صورت پویا نوعدهی میشود، تعیین انواع ممکن عملوندها میتواند دشوار باشد.
(مناقشه بر سر این است که آیا این واقعاً یک نقص طراحی است و آیا رفع این موضوع ارزش شکستن کدهای موجود را دارد. این موضوع باعث بحثهای بیپایانی در python-dev شده است و در ژوئیه ۲۰۰۱ به طوفانی از پیامهای طعنهآمیز و تلخ در comp.lang.python تبدیل شد. من اینجا از هیچیک از طرفین جانبداری نمیکنم و به توصیف آنچه در 2.2 پیادهسازی شده است پایبند میمانم. برای خلاصهای از استدلالها و استدلالهای مقابل، PEP 238 را بخوانید.)
از آنجا که این تغییر ممکن است کد را بشکند، بهطور بسیار تدریجی معرفی میشود. پایتون 2.2 این گذار را آغاز میکند، اما این تعویض تا پایتون 3.0 کامل نخواهد شد.
ابتدا، برخی اصطلاحات را از PEP 238 وام میگیرم. «تقسیم حقیقی» (true division) تقسیمی است که بیشتر غیربرنامهنویسان با آن آشنا هستند: ۳/۲ برابر ۱٫۵ است، ۱/۴ برابر ۰٫۲۵ است و به همین ترتیب. «تقسیم کف» همان کاری است که عملگر / پایتون در حال حاضر هنگام دریافت عملوندهای عدد صحیح انجام میدهد؛ نتیجه، کف مقداری است که تقسیم حقیقی برمیگرداند. «تقسیم کلاسیک» (classic division) رفتار مختلط فعلی / است؛ هنگامی که عملوندها عدد صحیح باشند، نتیجه تقسیم کف را برمیگرداند و هنگامی که یکی از عملوندها عدد ممیز شناور باشد، نتیجه تقسیم حقیقی را برمیگرداند.
تغییراتی که 2.2 معرفی میکند به شرح زیر است:
یک عملگر جدید،
//، عملگر تقسیم کف است. (بله، میدانیم که شبیه نماد کامنت ++C است.)//همیشه تقسیم کف را انجام میدهد، مهم نیست که عملوندهای آن چه نوعی باشند؛ بنابراین1 // 2برابر 0 است و1.0 // 2.0نیز برابر 0.0 است.//در پایتون 2.2 همیشه در دسترس است؛ لازم نیست آن را با استفاده از دستور__future__فعال کنید.با گنجاندن
from __future__ import divisionدر یک ماژول، عملگر/طوری تغییر میکند که نتیجهی تقسیم حقیقی را برگرداند؛ بنابراین1/2برابر ۰.۵ است. بدون دستور__future__،/همچنان به معنای تقسیم کلاسیک است. معنای پیشفرض/تا پایتون 3.0 تغییر نخواهد کرد.کلاسها میتوانند متدهایی به نام
__truediv__()و__floordiv__()تعریف کنند تا دو عملگر تقسیم را سربارگذاری کنند. در سطح C، جایگاههایی نیز در ساختارPyNumberMethodsوجود دارد تا نوعهای توسعهای بتوانند این دو عملگر را تعریف کنند.پایتون 2.2 از چند آرگومان خط فرمان برای آزمایش اینکه آیا کد با معناشناسی تغییریافتهی تقسیم کار خواهد کرد، پشتیبانی میکند. اجرای python با
-Q warnباعث میشود هرگاه تقسیم روی دو عدد صحیح اعمال شود، هشداری صادر شود. میتوانید از این برای یافتن کدهای متأثر از این تغییر و اصلاح آنها استفاده کنید. بهطور پیشفرض، پایتون 2.2 صرفاً تقسیم کلاسیک را بدون هیچ هشداری انجام میدهد؛ این هشدار در پایتون 2.3 بهطور پیشفرض فعال خواهد شد.
همچنین ملاحظه نمائید
- PEP 238 - تغییر عملگر تقسیم
نوشتهشده توسط موشه زادکا و گیدو ون روسوم. پیادهسازیشده توسط گیدو ون روسوم.
تغییرات یونیکد¶
پشتیبانی پایتون از یونیکد در نسخهی 2.2 کمی بهبود یافته است. رشتههای یونیکد معمولاً بهصورت UCS-2، یعنی اعداد صحیح بدون علامت ۱۶ بیتی، ذخیره میشوند. پایتون 2.2 همچنین میتواند با ارائهی --enable-unicode=ucs4 به اسکریپت configure، بهگونهای کامپایل شود که از UCS-4، یعنی اعداد صحیح بدون علامت ۳۲ بیتی، بهعنوان کدگذاری داخلی خود استفاده کند. (همچنین میتوان --disable-unicode را مشخص کرد تا پشتیبانی از یونیکد بهطور کامل غیرفعال شود.)
هنگامی که مفسر برای استفاده از UCS-4 ساخته شود (یک «پایتون پهن»)، میتواند بهطور بومی نویسههای یونیکد از U+000000 تا U+110000 را مدیریت کند، بنابراین بازهی مقادیر مجاز برای تابع unichr() متناسب با آن گسترش مییابد. با استفاده از مفسری که برای استفاده از UCS-2 کامپایل شده است (یک «پایتون باریک»)، مقادیر بزرگتر از ۶۵۵۳۵ همچنان باعث میشوند که unichr() استثنای ValueError ایجاد کند. همهی این موارد در PEP 261 با عنوان «پشتیبانی از نویسههای یونیکد ‹پهن›» توصیف شدهاند؛ برای جزئیات بیشتر به آن مراجعه کنید.
توضیح تغییر دیگری سادهتر است. از زمان معرفیشان، رشتههای یونیکد از یک متد encode() برای تبدیل رشته به یک کدگذاری انتخابشده مانند UTF-8 یا Latin-1 پشتیبانی کردهاند. در نسخه 2.2، یک متد متقارن decode([*encoding*]) به رشتههای ۸-بیتی اضافه شده است (هرچند نه به رشتههای یونیکد). decode() فرض میکند که رشته در کدگذاری مشخصشده است و آن را کدگشایی میکند و هر آنچه را که کدک برمیگرداند، برمیگرداند.
با استفاده از این قابلیت جدید، کدکهایی برای کارهایی که ارتباط مستقیمی با یونیکد ندارند افزوده شدهاند. برای مثال، کدکهایی برای کدگذاری uu، کدگذاری base64 در MIME و فشردهسازی با ماژول zlib افزوده شدهاند:
>>> s = """Here is a lengthy piece of redundant, overly verbose,
... and repetitive text.
... """
>>> data = s.encode('zlib')
>>> data
'x\x9c\r\xc9\xc1\r\x80 \x10\x04\xc0?Ul...'
>>> data.decode('zlib')
'Here is a lengthy piece of redundant, overly verbose,\nand repetitive text.\n'
>>> print s.encode('uu')
begin 666 <data>
M2&5R92!I<R!A(&QE;F=T:'D@<&EE8V4@;V8@<F5D=6YD86YT+"!O=F5R;'D@
>=F5R8F]S92P*86YD(')E<&5T:71I=F4@=&5X="X*
end
>>> "sheesh".encode('rot-13')
'furrfu'
برای تبدیل نمونهی یک کلاس به یونیکد، میتوان متد __unicode__() را در کلاس تعریف کرد، مشابه __str__().
encode()، decode() و __unicode__() توسط Marc-André Lemburg پیادهسازی شدند. تغییرات مربوط به پشتیبانی از استفاده از UCS-4 بهصورت داخلی توسط Fredrik Lundh و Martin von Löwis پیادهسازی شدند.
همچنین ملاحظه نمائید
- PEP 261 - پشتیبانی از نویسههای یونیکد «پهن»
نوشتهی پل پرسکاد.
PEP 227: محدودههای تودرتو¶
در پایتون 2.1، محدودههای تودرتوی ایستا بهعنوان یک قابلیت اختیاری افزوده شد که با دایرکتیو from __future__ import nested_scopes فعال میشد. در 2.2، محدودههای تودرتو دیگر نیازی به فعالسازی خاص ندارند و اکنون همیشه وجود دارند. باقی این بخش رونوشتی از شرح محدودههای تودرتو از سند «What's New in Python 2.1» من است؛ اگر آن را هنگام انتشار 2.1 خواندهاید، میتوانید از خواندن باقی این بخش صرفنظر کنید.
بزرگترین تغییری که در Python 2.1 معرفی شد و در 2.2 تکمیل شد، در قواعد تعیین محدوده پایتون است. در Python 2.0، در هر لحظه حداکثر سه فضای نام برای جستجوی نامهای متغیر وجود دارد: محلی، سطح ماژول، و فضای نام توکار. این موضوع اغلب افراد را غافلگیر میکرد، زیرا با انتظارات شهودی آنها مطابقت نداشت. برای مثال، تعریف یک تابع بازگشتی تودرتو کار نمیکند:
def f():
...
def g(value):
...
return g(value-1) + 1
...
تابع g() همیشه استثنای NameError ایجاد میکند، زیرا مقیدسازی نام g نه در فضای نام محلی آن و نه در فضای نام در سطح ماژول وجود ندارد. این موضوع در عمل مشکل چندانی نیست (چند وقت یکبار توابع داخلی را به این صورت بازگشتی تعریف میکنید؟)، اما همین امر استفاده از عبارت lambda را نیز ناشیانهتر میکرد و این در عمل مشکلساز بود. در کدی که از lambda استفاده میکند، اغلب میتوانید متغیرهای محلی را بیابید که با پاس دادن آنها بهعنوان مقادیر پیشفرض آرگومانها کپی شدهاند.
def find(self, name):
"Return list of any entries equal to 'name'"
L = filter(lambda x, name=name: x == name,
self.list_attribute)
return L
در نتیجه، خوانایی کد پایتونی که به سبکی کاملاً تابعی نوشته شده است، بهشدت آسیب میبیند.
مهمترین تغییر در پایتون 2.2 این است که محدودهبندی ایستا (static scoping) برای رفع این مشکل به زبان افزوده شده است. به عنوان نخستین اثر، آرگومان پیشفرض name=name در مثال بالا دیگر لازم نیست. به بیان ساده، وقتی درون یک تابع مقداری به نام متغیر معینی انتساب داده نشده باشد (از طریق یک انتساب، یا دستورهای def، class یا import)، ارجاعها به آن متغیر در فضای نام محلیِ محدودهی دربرگیرنده جستجو خواهند شد. توضیح دقیقتری از قواعد، به همراه کالبدشکافی پیادهسازی، را میتوان در PEP (پیشنهاد بهبود پایتون) یافت.
این تغییر ممکن است برای کدهایی که در آنها نام یک متغیر هم در سطح ماژول و هم بهعنوان متغیر محلی درون تابعی که تعریفهای تابع دیگری را در بر میگیرد استفاده میشود، برخی مشکلات سازگاری ایجاد کند. با این حال، این موضوع نسبتاً بعید به نظر میرسد، چون خواندن چنین کدی از همان ابتدا بسیار گیجکننده بود.
یکی از اثرهای جانبی این تغییر این است که دستورهای from module import * و exec تحت شرایط خاصی درون محدودهی یک تابع غیرمجاز شدهاند. راهنمای مرجع پایتون همواره گفته است که from module import * فقط در سطح بالای یک ماژول مجاز است، اما مفسر سیپایتون پیش از این هرگز این را اعمال نکرده بود. بهعنوان بخشی از پیادهسازی محدودههای تودرتو، کامپایلری که کد منبع پایتون را به بایتکد تبدیل میکند، باید کد متفاوتی برای دسترسی به متغیرها در یک محدودهی دربرگیرنده تولید کند. دستورهای from module import * و exec باعث میشوند کامپایلر نتواند این موضوع را تشخیص دهد، زیرا این دستورها نامهایی به فضای نام محلی اضافه میکنند که در زمان کامپایل قابل شناسایی نیستند. بنابراین، اگر تابعی شامل تعریفهای تابع یا عبارتهای lambda با متغیرهای آزاد باشد، کامپایلر این موضوع را با ایجاد یک استثنای SyntaxError علامتگذاری میکند.
برای روشنتر شدن توضیح پیشین، در اینجا مثالی میآوریم:
x = 1
def f():
# The next line is a syntax error
exec 'x=2'
def g():
return x
سطر ۴ که حاوی دستور exec است، یک خطای نحوی است، زیرا exec یک متغیر محلی جدید با نام x تعریف میکند که g() باید به مقدار آن دسترسی داشته باشد.
این نباید محدودیت چندانی باشد، زیرا exec بهندرت در بیشتر کدهای پایتون استفاده میشود (و هنگامی که استفاده میشود، بههرحال اغلب نشانهی یک طراحی ضعیف است).
همچنین ملاحظه نمائید
- PEP 227 - محدودههای تودرتوی ایستا
نوشته و پیادهسازیشده توسط جرمی هایلتون.
ماژولهای جدید و بهبودیافته¶
ماژول
xmlrpclibتوسط Fredrik Lundh به کتابخانه استاندارد افزوده شد و پشتیبانی از نوشتن کلاینتهای XML-RPC را فراهم میکند. XML-RPC یک پروتکل سادهی فراخوانی رویه از راه دور است که بر پایهی HTTP و XML ساخته شده است. برای مثال، قطعه کد زیر فهرستی از کانالهای RSS را از O'Reilly Network بازیابی میکند و سپس عناوین اخیر یک کانال را فهرست میکند:import xmlrpclib s = xmlrpclib.Server( 'http://www.oreillynet.com/meerkat/xml-rpc/server.php') channels = s.meerkat.getChannels() # channels is a list of dictionaries, like this: # [{'id': 4, 'title': 'Freshmeat Daily News'} # {'id': 190, 'title': '32Bits Online'}, # {'id': 4549, 'title': '3DGamers'}, ... ] # Get the items for one channel items = s.meerkat.getItems( {'channel': 4} ) # 'items' is another list of dictionaries, like this: # [{'link': 'http://freshmeat.net/releases/52719/', # 'description': 'A utility which converts HTML to XSL FO.', # 'title': 'html2fo 0.3 (Default)'}, ... ]
ماژول
SimpleXMLRPCServerایجاد سرورهای ساده XML-RPC را آسان میکند. برای اطلاعات بیشتر درباره XML-RPC به http://xmlrpc.scripting.com/ مراجعه کنید.ماژول جدید
hmacالگوریتم HMAC توصیفشده در RFC 2104 را پیادهسازی میکند. (با مشارکت Gerhard Häring.)چندین تابع که در ابتدا تاپلهای طولانی برمیگرداندند، اکنون شبهدنبالههایی (pseudo-sequence) برمیگردانند که همچنان مانند تاپلها رفتار میکنند، اما ویژگیهای یادیاری مانند
memberst_mtimeیاtm_yearنیز دارند. توابع ارتقایافته شاملstat()،fstat()،statvfs()وfstatvfs()در ماژولosوlocaltime()،gmtime()وstrptime()در ماژولtimeهستند.برای مثال، برای بهدست آوردن اندازهی یک پرونده با استفاده از تاپلهای قدیمی، در نهایت مجبور بودید چیزی شبیه
file_size = os.stat(filename)[stat.ST_SIZE]بنویسید، اما اکنون میتوان آن را واضحتر بهصورتfile_size = os.stat(filename).st_sizeنوشت.وصل اصلی این قابلیت توسط نیک متیوسون ارائه شد.
پروفایلگیر پایتون بهطور گسترده بازنویسی شده و خطاهای گوناگونی در خروجی آن اصلاح شدهاند. (با مشارکت Fred L. Drake, Jr. و Tim Peters.)
ماژول
socketرا میتوان طوری کامپایل کرد که از IPv6 پشتیبانی کند؛ گزینهی--enable-ipv6را به اسکریپت configure پایتون بدهید. (مشارکتشده توسط Jun-ichiro "itojun" Hagino.)دو نویسهی قالب جدید برای اعداد صحیح ۶۴بیتی در پلتفرمهایی که نوع long long زبان C را پشتیبانی میکنند، به ماژول
structاضافه شد.qبرای عدد صحیح ۶۴بیتی علامتدار است وQبرای عدد صحیح بدون علامت است. مقدار در نوع عدد صحیح بلند پایتون برگردانده میشود. (مشارکت از Tim Peters.)در حالت تعاملی مفسر، تابع توکار جدیدی به نام
help()وجود دارد که از ماژولpydocمعرفیشده در پایتون 2.1 برای ارائهی راهنمای تعاملی استفاده میکند.help(object)هر متن راهنمای موجود دربارهی object را نمایش میدهد.help()بدون آرگومان شما را به یک ابزار راهنمای برخط میبرد که در آن میتوانید نام توابع، کلاسها یا ماژولها را وارد کنید تا متن راهنمای آنها را بخوانید. (ارائهشده توسط گیدو ون روسوم، با استفاده از ماژولpydocنوشتهی کا-پینگ یی.)رفع اشکالها و بهبودهای کارایی گوناگونی در موتور SRE که زیربنای ماژول
reاست، انجام شده است. برای مثال، توابعre.sub()وre.split()در C بازنویسی شدهاند. وصلی دیگر که مشارکت شده است، سرعت برخی از بازههای نویسهی یونیکد را دو برابر میکند، و متد جدیدfinditer()پیمایشگری بر روی تمام تطبیقهای غیرهمپوشان در یک رشتهی دادهشده برمیگرداند. (SRE توسط فردریک لوند نگهداری میشود. وصل BIGCHARSET توسط مارتین فون لویس ارائه شده است.)ماژول
smtplibاکنون از RFC 2487 با عنوان «Secure SMTP over TLS» پشتیبانی میکند، بنابراین اکنون میتوان ترافیک SMTP بین یک برنامهی پایتون و عامل انتقال پست (mail transport agent) که پیامی به آن تحویل داده میشود را رمزگذاری کرد.smtplibهمچنین از احراز هویت SMTP پشتیبانی میکند. (با مشارکت گرهارد هرینگ.)ماژول
imaplibکه توسط Piers Lauder نگهداری میشود، از چندین افزونهی جدید پشتیبانی میکند: افزونهی NAMESPACE تعریفشده در RFC 2342، SORT، GETACL و SETACL. (مشارکتشده توسط Anthony Baxter و Michel Pelletier.)تجزیهی نشانیهای ایمیل در ماژول
rfc822اکنون با RFC 2822 که بهروزرسانی RFC 822 است، سازگار است. (نام این ماژول بهrfc2822تغییر نخواهد کرد.) یک بستهی جدید به نامemailنیز برای تجزیه و تولید پیامهای ایمیل اضافه شده است. (مشارکتشده توسط Barry Warsaw، و برآمده از کار او روی Mailman.)ماژول
difflibاکنون شامل کلاس جدیدی به نامDifferبرای تولید فهرستهای خوانا برای انسان از تغییرات (یک «دلتا») میان دو دنباله از سطرهای متن است. همچنین دو تابع تولیدگر،ndiff()وrestore()، وجود دارند که به ترتیب یک دلتا از روی دو دنباله، یا یکی از دنبالههای اصلی را از روی یک دلتا برمیگردانند. (کار پرزحمت توسط David Goodger ارائه شده است، برگرفته از کد ndiff.py نوشتهی Tim Peters که سپس کار تولیدگرسازی (generatorization) را انجام داد.)ثابتهای جدید
ascii_letters،ascii_lowercaseوascii_uppercaseبه ماژولstringاضافه شدند. چندین ماژول در کتابخانه استاندارد وجود داشتند که ازstring.lettersبه معنای بازههای A-Za-z استفاده میکردند، اما این فرض هنگامی که از localeها استفاده میشود نادرست است، زیراstring.lettersبسته به مجموعه نویسههای مجاز تعریفشده توسط locale جاری تغییر میکند. ماژولهای دارای اشکال همگی اصلاح شدهاند تا بهجای آن ازascii_lettersاستفاده کنند. (گزارش شده توسط شخصی ناشناس؛ اصلاح شده توسط Fred L. Drake, Jr.)ماژول
mimetypesاکنون با افزودن کلاسMimeTypes، که فهرستی از نام پروندهها را برای تجزیه دریافت میکند، استفاده از پایگاه دادههای جایگزین نوع MIME را آسانتر میسازد. (مشارکت Fred L. Drake, Jr.)کلاس
Timerبه ماژولthreadingاضافه شد که امکان زمانبندی یک فعالیت برای رخ دادن در زمانی در آینده را فراهم میکند. (مشارکتشده توسط ایتامار شتول-تراورینگ.)
تغییرات و اصلاحات مفسر¶
برخی از تغییرات فقط بر کسانی اثر میگذارند که در سطح C با مفسر پایتون سروکار دارند، زیرا آنها ماژولهای توسعهای پایتون مینویسند، مفسر را تعبیه میکنند، یا صرفاً روی خودِ مفسر هک میکنند. اگر شما فقط کد پایتون مینویسید، هیچیک از تغییراتی که در اینجا شرح داده شدهاند، تأثیر زیادی بر شما نخواهند گذاشت.
توابع پروفایلگیری و ردگیری اکنون میتوانند به زبان C پیادهسازی شوند؛ توابعی که میتوانند با سرعت بسیار بالاتری نسبت به توابع مبتنی بر پایتون عمل کنند و باید سربار پروفایلگیری و ردگیری را کاهش دهند. این موضوع مورد توجه سازندگان محیطهای توسعه برای پایتون قرار خواهد گرفت. دو تابع C جدید به API پایتون اضافه شدند:
PyEval_SetProfile()وPyEval_SetTrace(). توابع موجودsys.setprofile()وsys.settrace()همچنان وجود دارند و صرفاً تغییر کردهاند تا از رابط جدید در سطح C استفاده کنند. (مشارکت از سوی Fred L. Drake, Jr.)یک API سطح پایین دیگر اضافه شد که عمدتاً مورد توجه پیادهسازان اشکالزداها و ابزارهای توسعهی پایتون است.
PyInterpreterState_Head()وPyInterpreterState_Next()به فراخواننده اجازه میدهند همهی اشیای مفسر موجود را پیمایش کند؛PyInterpreterState_ThreadHead()وPyThreadState_Next()امکان حلقه زدن روی همهی وضعیتهای نخ برای یک مفسر معین را فراهم میکنند. (مشارکتشده توسط David Beazley.)رابط زبالهروب در سطح C تغییر کرده است تا نوشتن نوعهای توسعهای که از زبالهروبی پشتیبانی میکنند و اشکالزدایی استفادههای نادرست از توابع آسانتر شود. توابع مختلفی معناشناسی کمی متفاوتی دارند، بنابراین تعدادی از توابع باید تغییر نام میدادند. ماژولهای توسعهای که از API قدیمی استفاده میکنند همچنان کامپایل خواهند شد، اما در زبالهروبی شرکت نخواهند کرد؛ بنابراین بهروزرسانی آنها برای 2.2 باید اولویتی نسبتاً بالا در نظر گرفته شود.
برای ارتقای یک ماژول توسعهای به API جدید، گامهای زیر را انجام دهید:
نام
Py_TPFLAGS_GCبهPy_TPFLAGS_HAVE_GCتغییر یافت.- برای تخصیص دادن، از
PyObject_GC_New()یاPyObject_GC_NewVar()استفاده کنید اشیاء، و
PyObject_GC_Del()برای آزادسازی آنها.
- برای تخصیص دادن، از
تغییر نام
PyObject_GC_Init()بهPyObject_GC_Track()وPyObject_GC_Fini()بهPyObject_GC_UnTrack().PyGC_HEAD_SIZEرا از محاسبات اندازه شیء حذف کنید.فراخوانیهای
PyObject_AS_GC()وPyObject_FROM_GC()را حذف کنید.دنبالهی قالب جدید
etبه تابعPyArg_ParseTuple()اضافه شد؛etهم یک پارامتر و هم یک نام کدگذاری میگیرد و اگر پارامتر یک رشتهی یونیکد باشد، آن را به کدگذاری دادهشده تبدیل میکند، یا اگر یک رشتهی ۸ بیتی باشد، آن را دستنخورده باقی میگذارد، با این فرض که از قبل در کدگذاری موردنظر قرار دارد. این با نویسهی قالبesمتفاوت است که فرض میکند رشتههای ۸ بیتی در کدگذاری پیشفرض اسکی پایتون هستند و آنها را به کدگذاری جدید مشخصشده تبدیل میکند. (مشارکتکرده M.-A. Lemburg، و برای پشتیبانی MBCS در ویندوز که در بخش بعدی توضیح داده شده است، استفاده شد.)تابع متفاوتی برای تجزیه آرگومانها،
PyArg_UnpackTuple()، اضافهشده است که سادهتر و احتمالاً سریعتر است. بهجای مشخص کردن یک رشته قالببندی، فراخوانکننده صرفاً کمینه و بیشینه تعداد آرگومانهای مورد انتظار و مجموعهای از اشارهگرها به متغیرهای PyObject* را میدهد که با مقادیر آرگومانها پر خواهند شد.دو پرچم جدید
METH_NOARGSوMETH_Oدر جدولهای تعریف متد در دسترس هستند تا پیادهسازی متدهای بدون آرگومان یا دارای یک آرگومان بدون نوع را سادهتر کنند. فراخوانی چنین متدهایی کارآمدتر از فراخوانی متد متناظری است که ازMETH_VARARGSاستفاده میکند. همچنین، سبک قدیمیMETH_OLDARGSبرای نوشتن متدهای C اکنون بهطور رسمی منسوخ شده است.دو تابع پوششی جدید،
PyOS_snprintf()وPyOS_vsnprintf()اضافه شدند تا پیادهسازیهای چندسکویی برای APIهای نسبتاً جدیدsnprintf()وvsnprintf()در کتابخانه C را فراهم کنند. برخلاف توابع استانداردsprintf()وvsprintf()، نسخههای پایتونی کرانهای بافر استفادهشده را برای محافظت در برابر سرریز بافر بررسی میکنند. (مشارکتشده توسط M.-A. Lemburg.)تابع
_PyTuple_Resize()یک پارامتر استفادهنشده را از دست داده است؛ بنابراین اکنون بهجای ۳ پارامتر، ۲ پارامتر میگیرد. آرگومان سوم هرگز استفاده نمیشد و هنگام انتقال کد از نسخههای قبلی به Python 2.2 میتوان بهسادگی آن را کنار گذاشت.
تغییرات و اصلاحات دیگر¶
همانطور که همیشه، تعدادی بهبود و رفع اشکال دیگر در سراسر درخت سورس پراکنده بودند. جستوجو در گزارشهای تغییرات CVS نشان میدهد که بین Python 2.1 و 2.2، تعداد ۵۲۷ وصل اعمال شد و ۶۸۳ اشکال رفع شد؛ در 2.2.1 تعداد ۱۳۹ وصل اعمال و ۱۴۳ اشکال رفع شد؛ در 2.2.2 نیز ۱۰۶ وصل اعمال و ۸۲ اشکال رفع شد. این ارقام احتمالاً دستکم گرفته شدهاند.
برخی از تغییرات قابل توجهتر عبارتاند از:
کد پورت MacOS برای پایتون، که توسط جک جانسن نگهداری میشود، اکنون در درخت CVS اصلی پایتون قرار دارد و تغییرات بسیاری برای پشتیبانی از MacOS X در آن انجام شده است.
مهمترین تغییر، امکان ساخت پایتون بهصورت یک چارچوب است که با ارائهی گزینهی
--enable-frameworkبه اسکریپت configure هنگام کامپایل پایتون فعال میشود. به گفتهی Jack Jansen، «این کار یک نصبِ خودکفای پایتون بهعلاوهی "چسب" چارچوب OS X را در/Library/Frameworks/Python.framework(یا مکان دلخواه دیگری) نصب میکند. فعلاً این کار فایدهی فوریِ چندانی نمیافزاید (در واقع، این عیب را دارد که باید PATH خود را تغییر دهید تا بتوانید پایتون را پیدا کنید)، اما مبنای ایجاد یک برنامهی کامل و تمامعیار پایتون، انتقال MacPython IDE، استفادهی احتمالی از پایتون بهعنوان یک زبان اسکریپتنویسی استاندارد OSA و بسیاری از موارد دیگر است.»اکثر ماژولهای جعبهابزار MacPython، که رابطهایی به APIهای MacOS مانند پنجرهسازی (windowing)، QuickTime، اسکریپتنویسی و غیره هستند، به OS X منتقل شدهاند، اما در
setup.pyبهصورت کامنت رها شدهاند. افرادی که میخواهند با این ماژولها آزمایش کنند، میتوانند آنها را بهصورت دستی از کامنت خارج کنند.ارسال آرگومانهای کلیدواژهای به توابع توکاری که آنها را نمیپذیرند، اکنون باعث میشود استثنای
TypeErrorبا پیام «function takes no keyword arguments» مطرح شود.ارجاعهای ضعیف که در پایتون 2.1 بهعنوان یک ماژول توسعهای افزوده شدند، اکنون بخشی از هسته هستند، زیرا در پیادهسازی کلاسهای سبک جدید استفاده میشوند. بنابراین استثنای
ReferenceErrorاز ماژولweakrefبه یک استثنای توکار منتقل شده است.یک اسکریپت جدید،
Tools/scripts/cleanfuture.pyنوشتهی تیم پیترز، دستورهای__future__منسوخ را بهطور خودکار از کد منبع پایتون حذف میکند.آرگومان اضافی flags به تابع توکار
compile()افزوده شده است، بهطوریکه رفتار دستورهای__future__اکنون میتواند بهدرستی در پوستههای شبیهسازیشده، مانند پوستههایی که IDLE و سایر محیطهای توسعه ارائه میدهند، مشاهده شود. این موضوع در PEP 264 توضیح داده شده است. (با مشارکت مایکل هادسون.)مجوز جدیدی که همراه با پایتون 1.6 معرفی شد، با GPL سازگار نبود. این مشکل با چند تغییر متنی جزئی در مجوز نسخه 2.2 برطرف شد، بنابراین اکنون دوباره تعبیه کردن پایتون درون برنامهای تحت مجوز GPL از نظر قانونی مجاز است. توجه داشته باشید که خود پایتون تحت GPL نیست، بلکه همانطور که همیشه بوده است، تحت مجوزی است که اساساً معادل مجوز BSD است. تغییرات مجوز همچنین به نسخههای پایتون 2.0.1 و 2.1.1 اعمال شد.
هنگامی که در ویندوز با نام پروندهای یونیکدی مواجه شود، پایتون اکنون آن را به یک رشتهی کدگذاریشده با MBCS تبدیل میکند؛ همان چیزی که APIهای پرونده مایکروسافت از آن استفاده میکنند. از آنجا که APIهای پرونده بهطور صریح از MBCS استفاده میکنند، انتخاب اسکی بهعنوان کدگذاری پیشفرض از سوی پایتون، در نهایت یک مزاحمت به شمار میآید. در یونیکس، اگر
locale.nl_langinfo(CODESET)در دسترس باشد، از مجموعه نویسههای locale استفاده میشود. (پشتیبانی از ویندوز توسط Mark Hammond با کمک Marc-André Lemburg ارائه شد. پشتیبانی از یونیکس توسط Martin von Löwis افزوده شد.)پشتیبانی از پروندههای بزرگ اکنون در ویندوز فعال شده است. (مشارکت Tim Peters.)
اسکریپت
Tools/scripts/ftpmirror.pyاکنون، در صورتی که پروندهی.netrcداشته باشید، آن را تجزیه میکند. (با مشارکت Mike Romberg.)برخی از ویژگیهای شیء بازگرداندهشده توسط تابع
xrange()اکنون منسوخ شدهاند و هنگام دسترسی به آنها هشدار ایجاد میکنند؛ این ویژگیها در Python 2.3 حذف خواهند شد. اشیاءxrangeبا پشتیبانی از اسلایسکردن، ضرب دنباله و عملگرinمیکوشیدند وانمود کنند که نوعهای دنبالهی کامل هستند، اما این ویژگیها بهندرت استفاده میشدند و در نتیجه دارای اشکال بودند. متدtolist()و ویژگیهایstart،stopوstepنیز منسوخ میشوند. در سطح C، آرگومان چهارم تابعPyRange_New()، یعنیrepeat، نیز منسوخ شده است.تعدادی وصل روی پیادهسازی دیکشنری اعمال شد که بیشتر برای رفع برونریزیهای احتمالی هسته در حالتی بود که دیکشنریای شامل اشیایی باشد که بهطور پنهانکارانه مقدار هش خود را تغییر میدادند یا دیکشنریای را که در آن قرار داشتند تغییر میدادند. برای مدتی، python-dev به ریتم ملایمی افتاد: Michael Hudson موردی را پیدا میکرد که باعث برونریزی هسته میشد، Tim Peters اشکال را برطرف میکرد، Michael مورد دیگری را پیدا میکرد، و این چرخه مدام تکرار میشد.
در ویندوز، به لطف تعدادی وصله ارائهشده از سوی استیون هانسن، اکنون میتوان پایتون را با Borland C کامپایل کرد، هرچند نتیجه هنوز بهطور کامل کار نمیکند. (اما این خود پیشرفت است...)
بهبود دیگری برای ویندوز: Wise Solutions سخاوتمندانه سیستم InstallerMaster 8.1 خود را در اختیار PythonLabs قرار داد. نصبکنندههای ویندوزی پیشین PythonLabs از Wise 5.0a استفاده میکردند که شروع به نشان دادن کهولت خود کرده بود. (بستهبندی توسط Tim Peters.)
پروندههایی که به
.pywختم میشوند اکنون میتوانند در ویندوز ایمپورت شوند..pywچیزی مخصوص ویندوز است که برای نشان دادن این موضوع به کار میرود که یک اسکریپت باید بهجای PYTHON.EXE با PYTHONW.EXE اجرا شود تا از ظاهر شدن کنسول DOS برای نمایش خروجی جلوگیری شود. این وصل امکان میدهد چنین اسکریپتهایی ایمپورت شوند، در صورتی که بهعنوان ماژول نیز قابل استفاده باشند. (پیادهسازی توسط دیوید بولن.)در سکوهایی که پایتون برای بارگذاری ماژولهای توسعهای از تابع
dlopen()در زبان C استفاده میکند، اکنون میتوان پرچمهای استفادهشده توسطdlopen()را با استفاده از توابعsys.getdlopenflags()وsys.setdlopenflags()تنظیم کرد. (همکاری Bram Stolk.)تابع توکار
pow()دیگر هنگامی که اعداد ممیز شناور ارائه شوند، از ۳ آرگومان پشتیبانی نمیکند.pow(x, y, z)مقدار(x**y) % zرا برمیگرداند، اما این هرگز برای اعداد ممیز شناور مفید نیست و نتیجهی نهایی بسته به پلتفرم بهطور غیرقابلپیشبینی متفاوت است. فراخوانیای مانندpow(2.0, 8.0, 7.0)اکنون استثنایTypeErrorایجاد میکند.
قدردانیها¶
نویسنده مایل است از افراد زیر برای ارائه پیشنهادها، اصلاحات و کمک در پیشنویسهای مختلف این مقاله تشکر کند: Fred Bremmer, Keith Briggs, Andrew Dalke, Fred L. Drake, Jr., Carel Fellinger, David Goodger, Mark Hammond, Stephen Hansen, Michael Hudson, Jack Jansen, Marc-André Lemburg, Martin von Löwis, Fredrik Lundh, Michael McLay, Nick Mathewson, Paul Moore, Gustavo Niemeyer, Don O'Donnell, Joonas Paalasma, Tim Peters, Jens Quade, Tom Reinhardt, Neil Schemenauer, Guido van Rossum, Greg Ward, Edward Welbourne.