بهترین مفسر پایتون برای برنامه نویسی — راهنمای کاربردی

۱۲۳۷ بازدید
آخرین به‌روزرسانی: ۲۰ تیر ۱۴۰۲
زمان مطالعه: ۲۶ دقیقه
بهترین مفسر پایتون برای برنامه نویسی — راهنمای کاربردی

«زبان برنامه‌نویسی پایتون» (Python Programming Language)، یک زبان برنامه‌نویسی «مفسری» (Interpreted) محسوب می‌شود. مفسر زبان پایتون دستورالعمل‌ها را خط به خط و بر اساس ترتیب آن‌ها (در مجموعه کدهای نوشته شده توسط برنامه‌‌نویس)، می‌خواند، ارزیابی می‌کند و خروجی‌های حاصل از اجرای دستورات را در خروجی نمایش می‌دهد. تمام این کارها توسط «مفسر» (Interpreter) انجام می‌شود. یکی از سؤالاتی که برنامه‌نویسان در طول توسعه برنامه‌های کاربردی با آن دست و پنجه نرم می‌کنند این است که کدام نسخه از مفسر پایتون برای کد نویسی برنامه مد نظر آن‌ها مناسب است.

چنین رویه‌ای در اجرای کدهای برنامه‌ها، برخلاف نحوه اجرای برنامه‌ها توسط زبان‌های «کامپایلری» (Compiler) است؛ در زبان‌های کامپایلری، تمامی کدها وارد کامپایلر می‌شوند و یک «واحد ترجمه» (Translation Unit)، کدهای نوشته شده به یک زبان برنامه‌نویسی کامپایلری را به دستورالعمل‌های خاصی ترجمه می‌کند و دستورالعمل‌های خاص قابل اجرا توسط ماشین را خروجی می‌دهد.

مفسر یک برنامه کامپیوتری است که برای اجرای مستقیم دستورات برنامه‌ها مورد استفاده قرار می‌گیرد. بیشتر زبان‌هایی که از مفسر برای اجرای دستورات خود استفاده می‌کنند، «زبان‌های برنامه‌نویسی سطح بالا» (High-Level Programming Languages) هستند. مفسر، برنامه‌های نوشته شده به زبان‌های سطح بالا را به یک زبان «میانجی» (Intermediate) ترجمه و سپس اجرا می‌کند. همچنین، برخی از مفسرها، «کدهای منبع» (Source Code) سطح بالا را «تجزیه و تحلیل» (Parse) و سپس، دستورات را به طور مستقیم اجرا می‌کنند؛ اجرای دستورات، به صورت خط به خط یا دستور به دستور انجام می‌شود.

مفسر پایتون

تفاوت کامپایلر و مفسر

زبان‌های برنامه‌نویسی معمولا به دو شیوه پیاده‌سازی می‌شوند؛ زبان‌های برنامه‌نویسی کامپایلری و زبان‌های برنامه‌نویسی مفسری.

کامپایلر، یک برنامه کامپیوتری ویژه است که دستورات (کد‌ها یا دستورالعمل‌ها) نوشته شده به یک زبان برنامه‌نویسی خاص را پردازش و آن‌ها را به «زبان ماشین» (Machine Language) یا کدهایی که توسط «پردازنده» (Processor) کامپیوتر قابل اجرا باشد تبدیل می‌کند. معمولا، پس از اینکه برنامه‌نویس کدهای مرتبط با برنامه خود را می‌نویسد (کدهای منبع)، در زمان اجرای این کدها، کامپایلر ابتدا تمامی دستورات نوشته شده را خط به خط، از لحاظ نحوی (Syntactically) تجزیه و تحلیل می‌کند. سپس، در یک یا چند مرحله متوالی (که به آن‌ها Pass گفته می‌شود) کد خروجی نهایی را تولید می‌کند.

در این چند مرحله متوالی، کامپایلر اطمینان حاصل می‌کند که پیوستگی کدها در خروجی نهایی حفظ شده است. معمولا به خروجی مرحله کامپایل کردن کدها، «کد شیء» (Object Code) یا «ماژول شیء» (Object Module) گفته می‌شود (شیء نامبرده شده در این اصطلاحات، با مفهوم شیء در «برنامه‌نویسی شیءگرا» (Object Oriented Programming) متفاوت است).  کد شیء (Object Code) یا ماژول شیء (Object Module)، کد ماشینی است که پردازنده‌‌های سیستم‌های کامپیوتری قادر به اجرای خط به خط آن‌ها هستند.

همانطور که از نام آن‌ها مشخص است، مفسر در زبان‌های برنامه‌نویسی، کد برنامه‌نویسی سطح بالا را به کدی که توسط ماشین قابل فهم باشد (کد ماشین) تفسیر یا تبدیل می‌کند. همچنین، این امکان وجود دارد که مفسر، کد برنامه‌نویسی سطح بالا را به کدهای یک زبان میانجی، که به راحتی توسط ماشین قابل اجرا است، تفسیر یا تبدیل کند. مفسر، هر یک از دستورات موجود در کدهای برنامه را خوانده و به طور مستقیم تبدیل یا اجرا می‌کند.

بنابراین تفاوت مهم میان کامپایلر و مفسر این است که کامپایلر، یک کد منبع سطح بالا را به کد محلی کامپایل شده تبدیل می‌کند؛ کدی که به طور مستقیم توسط «سیستم عامل» (Operating System) قابل اجرا است. این در حالی است که مفسر، کد برنامه‌نویسی سطح بالا را به کدی که توسط ماشین قابل فهم باشد یا کدهای یک زبان میانجی، تفسیر یا تبدیل می‌کند. از همه مهم‌تر، کدهای نوشته شده در زبان‌های برنامه‌نویسی کامپایلری، به طور کامل پردازش و در نهایت اجرا می‌شود، ولی کدهای نوشته شده توسط زبان‌های مفسری، خط به خط یا دستور به دستور اجرا می‌شوند.

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

مفسر پایتون

انتخاب مفسر مناسب برای پایتون

زمانی که نوبت به انتخاب مفسر مناسب برای اجرای یک برنامه نوشته شده به زبان پایتون می‌رسد، سؤالی که ناخودآگاه، ذهن برنامه‌نویسان و توسعه‌دهندگان را به خود معطوف می‌کند این است که «آیا باید مفسر پایتون برای نسخه 2.7 انتخاب شود یا نسخه 3.7؟». پیش از اینکه معیارهای لازم برای انتخاب مفسر نسخه 2.7 یا نسخه 3.7 برای اجرای برنامه‌های نوشته شده به زبان برنامه‌نویسی پایتون مورد بررسی قرار بگیرد، ذکر نکات زیر خالی از لطف نیست:

  • هنوز بخش قابل توجهی از برنامه‌های کاربردی و کتابخانه‌های برنامه‌نویسی که توسط زبان پایتون نوشته شده‌اند و توسط جامعه برنامه‌نویسان و توسعه‌دهندگان مورد استفاده قرار می‌گیرند، از نسخه 2.7 پایتون استفاده می‌کنند. پس از عرضه نسخه 3 پایتون، مهاجرت این دسته از برنامه‌های کاربردی و کتابخانه‌های برنامه‌نویسی به سمت استفاده از نسخه 3 پایتون آغاز شده است. با این حال، از آنجایی که لیست برنامه‌های کاربردی و کتابخانه‌های برنامه‌نویسی نوشته شده توسط نسخه 2.7 پایتون بسیار گسترده است، این فرایند کمی زمان‌بر خواهد بود.
  • بنیاد نرم‌افزاری پایتون (Python Software Foundation) تنها تا سال 2020 از نسخه 2.7 پایتون پشتیبانی و بسته‌های امنیتی برای آن منتشر می‌کند.
  • برند تجاری پایتون، هر دو نسخه 2.7 و 3.7 زبان برنامه‌نویسی پایتون را شامل می‌شود.

پیش از اینکه قادر به توسعه برنامه‌های کاربردی در زبان پایتون باشید، لازم است تا ابتدا زبان پایتون را روی سیستم عامل مقصد خود نصب کنید. کاربران و مخاطبان این مطلب می‌توانند آموزش جامع نصب پایتون در سیستم‌های عامل مختلف را در اینجا مطالعه کنند. همچنین، مجموعه‌ای از آموزش‌های مرتبط با یادگیری پایتون و نصب نسخه 2 و 3 پایتون در سیستم‌های عامل ویندوز، لینوکس و مک نیز در اینجا گردآوری شده است.

مفسر پایتون

مفسر پیشنهادی برای اجرای برنامه‌های پایتون

به طور کلی، استفاده از مفسر نسخه 3.7 پایتون برای اجرای برنامه‌ها، ارجحیت به مراتب بیشتری نسبت به استفاده از مفسر نسخه 2.7 پایتون دارد. بنابراین به کاربران، برنامه‌نویسان و توسعه‌دهندگان برنامه‌های کاربردی توصیه می‌شود، در صورتی که هنوز از مفسر نسخه 2.7 پایتون استفاده می‌کنند، سریعا نسبت به به‌روزرسانی مفسر پایتون به نسخه 3.7 اقدام کنند.

در ادامه، برخی از توصیه‌های مهم در مورد استفاده از نسخه‌های مختلف (نسخه 2.7 یا نسخه 3.7) مفسر پایتون ارائه شده است:

  • اکیدا توصیه می‌شود که از نسخه 3.7 مفسر پایتون برای کد نویسی برنامه‌های کاربردی جدید استفاده شود.
  • به کاربران یا برنامه‌نویسان مبتدی که به دنبال یادگیری زبان برنامه‌نویسی پایتون هستند، توصیه می‌شود که برنامه‌نویسی به وسیله قواعد دستوری و قابلیت‌های نسخه 3.7 پایتون را یاد بگیرند و حتما از نسخه 3.7 مفسر پایتون برای اجرای برنامه‌ها استفاده کنند؛ با این حال، آشنا شدن با قواعد دستوری و قابلیت‌های نسخه 2.7 پایتون می‌تواند مفید باشد و دانش برنامه‌نویس از قواعد کد نویسی پایتون را بالا ببرد.
  • اگر از نرم‌افزاری استفاده می‌کنید که پیش از عرضه نسخه 3 مفسر پایتون عرضه شده است، این نرم‌افزار بدون شک از نسخه 2.7 مفسر پایتون برای اجرای دستورات و کدهای برنامه استفاده می‌کند؛ بنابراین، در صورتی که به روز رسانی جدیدی از نرم‌افزار ارائه نشده باشد (نسخه‌ای از نرم‌افزار که با نسخه 3.7 مفسر پایتون برنامه‌نویسی شده باشد)، بهتر است که از نسخه 2.7 مفسر پایتون استفاده کنید.
  • یک سیاست منطقی و صحیحی که برنامه‌نویسان و توسعه‌دهندگان حرفه‌ای هنگام توسعه کتابخانه‌های برنامه‌نویسی «منبع باز» (Open Source) اتخاذ می‌کنند این است که همزمان برای نسخه 2.7 و نسخه 3.7 مفسر پایتون کد نویسی انجام می‌دهند. در صورتی که «کتابخانه‌های برنامه‌نویسی» (Libraries) در حال توسعه، تنها برای یکی از نسخه‌های مفسر پایتون کد نویسی شوند، حجم زیادی از کاربران زبان پایتون که از نسخه 2.7 پایتون استفاده می‌کنند، قادر به استفاده از این کتابخانه نخواهند بود. با این حال، طی سال‌های آینده و با همه گیر شدن استفاده از نسخه 3 زبان پایتون به عنوان مفسر پیش‌فرض، برنامه‌نویسان و توسعه‌دهندگان قادر خواهند بود تا کتابخانه‌های خود را تنها برای یکی از نسخه‌های مفسر پایتون توسعه دهند.

مفسر پایتون

نسخه 3 مفسر پایتون

در صورتی که کاربر، برنامه‌نویس یا توسعه‌دهنده، جهت توسعه برنامه‌ کاربردی، نیاز به استفاده از زبان برنامه‌نویس پایتون دارد و یا تمایل دارد که از این زبان جهت کد نویسی برنامه کاربردی مد نظر خود استفاده کند، توصیه می‌شود که از نسخه 3 (هم اکنون نسخه 3.7 مفسر پایتون، نسخه پایدار پایتون محسوب می‌شود) مفسر پایتون برای این کار استفاده کنند. دلیل دیگر استفاده از نسخه 3 مفسر پایتون، به‌روز رسانی ویژگی‌های امنیتی، از بین بردن باگ‌ها و اشکالات منطقی و ارتقاء عملکرد کتابخانه‌های استاندارد زبان پایتون در به‌روزرسانی‌های متوالی است. همچنین، به‌روزرسانی و پشتیبانی امنیتی از نسخه 2 مفسر پایتون، تنها تا سال 2020 ادامه پیدا می‌کند.

بنابراین، تنها در صورتی به سراغ استفاده از نسخه 2.7 مفسر پایتون (جهت کد نویسی برنامه‌های کاربردی) بروید که یکی از شرایط زیر برقرار باشد:

  • برای کد نویسی برنامه‌های کاربردی، از منابع، کدها و کتابخانه‌هایی استفاده شده است که از نسخه 2.7 مفسر پایتون به عنوان «پاید کد» (Code-Base) استفاده می‌کنند.
  • برخی از کتابخانه‌های معروف زبان پایتون، تنها از نسخه 2 مفسر پایتون پشتیبانی می‌کنند. در صورتی که برای کد نویسی برنامه کاربردی، نیاز به استفاده از چنین کتابخانه‌هایی وجود داشته باشد، استفاده از نسخه 2 مفسر پایتون ضروری می‌شود.
  • در صورتی که برنامه‌نویس، تسلط کافی روی کد نویسی به زبان پایتون در نسخه‌ 2 داشته باشد، ممکن است ترجیح دهد تا از مفسر این نسخه از پایتون برای توسعه برنامه‌های کاربردی استفاده کند. با این حال، توصیه می‌شود که آشنایی کافی با نسخه 3 مفسر پایتون ایجاد شود تا فرایند مهاجرت از نسخه 2 به نسخه 3 پایتون به راحتی انجام بپذیرد.

سؤالی که در اینجا ممکن است مطرح شود این است که آیا می‌توان کدی در زبان پایتون نوشت که توسط نسخه‌های مختلف مفسر پایتون قابل اجرا باشد. پاسخ این سؤال، بستگی به نوع برنامه کاربردی دارد. همچنین، بسته به نوع برنامه کاربردی که قرار است در پایتون تولید شود، نوشتن کدی که در نسخه‌های مختلف مفسر پایتون قابل استفاده باشد، کار بسیار سخت یا حتی آسانی خواهد بود.

با این حال، به برنامه‌نویسان مبتدی توصیه نمی‌شود که به دنبال نوشتن کدهایی باشند که در نسخه‌های مختلف پایتون قابل اجرا باشند. به طور کلی، در صورتی که برنامه‌نویس، بر اساس مفاد قرارداد استخدامی، جهت کد نویسی در نسخه 2 پایتون استخدام نشده باشد، توصیه می‌شود که برای توسعه برنامه‌های کاربردی، از نسخه 3 زبان پایتون و مفسر آن استفاده شود.

مفسر پایتون

پیاد‌ه‌سازی‌های مختلف از زبان برنامه‌نویسی پایتون

وقتی که مردم از زبان پایتون صحبت می‌کنند، علاوه بر این که منظورشان زبان برنامه‌نویسی پایتون است، پیاده‌سازی خاصی از زبان پایتون به نام CPython را نیز مد نظر دارند. پایتون در واقع مشخصات یک زبان برنامه‌نویسی را تعریف می‌کند که می‌تواند در پلتفرم‌ها و محیط‌های مختلف و به شیوه‌های گوناگونی پیاده‌سازی شود.

در ادامه، برخی از مهم‌ترین پیاده‌سازی انجام شده از زبان برنامه‌نویسی پایتون (بر اساس مشخصات تعریف شده برای پایتون) معرفی خواهند شد:

مفسر پایتون

پیاده‌سازی CPython

پیاده‌سازی CPython، پیاده‌سازی مرجع و رسمی از پایتون محسوب می‌شود که به وسیله زبان C نوشته شده است. ویژگی مهم پیاده‌سازی CPython این است که ابتدا کدهای پایتون نوشته شده را به «بایت‌‎کد میانجی» (Intermediate ByteCode) تبدیل می‌کند. در مرحله بعد، بایت‌کدهای میانجی تولید شده توسط یک «ماشین مجازی» (Virtual Machine) تفسیر می‌‌شود. پیاده‌سازی CPython، بالاترین سطح مطابقت ممکن را با بسته‌های برنامه‌نویسی پایتون و ماژول‌های پایتون نوشته شده به زبان C دارد.

در صورتی که برنامه‌نویس یا توسعه‌دهنده قصد داشته باشد تا از کدهای منبع باز پایتون برای کد نویسی برنامه‌های کاربردی استفاده کند، به گونه‌ای که کاربران زیادی بتوانند از کدهای نوشته شده استفاده کنند، اکیدا توصیه می‌شود که از پیاده‌سازی CPython برای چنین کاری استفاده شود. همچنین، بسیاری از بسته‌های نرم‌افزاری ارائه شده برای زبان پایتون، وابسته به پیاده‌سازی پیاده‌سازی CPython هستند. به آن دسته از کاربران، برنامه‌نویسان یا توسعه‌دهندگانی که جهت توسعه برنامه‌های کاربردی به این مجموعه ار بسته‌های نرم‌افزار نیارد دارند، توصیه می‌شود که حتما از پیاده‌سازی CPython استفاده کنند.

تمامی نسخه‌های زبان برنامه‌نویسی و مفسر پایتون در زبان C پیاده‌سازی شده‌اند، زیرا CPython، پیاده‌سازی مرجع زبان برنامه‌نویسی پایتون محسوب می‌شود. در ادامه، نمونه‌ای از پیاده‌سازی‌های انجام شده برای برخی ار توابع ریاضی، در پیاده‌سازی CPython نمایش داده شده است. برای پیاده‌سازی این توابع ریاضی از زبان C‎ استفاده شده است.

1#include "Python.h"
2#include 
3#include "_math.h"
4
5
6#if !defined(HAVE_ACOSH) || !defined(HAVE_ASINH)
7static const double ln2 = 6.93147180559945286227E-01;
8static const double two_pow_p28 = 268435456.0; /* 2**28 */
9#endif
10#if !defined(HAVE_ASINH) || !defined(HAVE_ATANH)
11static const double two_pow_m28 = 3.7252902984619141E-09; /* 2**-28 */
12#endif
13#if !defined(HAVE_ATANH) && !defined(Py_NAN)
14static const double zero = 0.0;
15#endif
16
17double
18_Py_acosh(double x)
19{
20    if (Py_IS_NAN(x)) {
21        return x+x;
22    }
23    if (x < 1.) {                       /* x < 1; return a signaling NaN */ errno = EDOM; #ifdef Py_NAN return Py_NAN; #else return (x-x)/(x-x); #endif } else if (x >= two_pow_p28) {        /* x > 2**28 */
24        if (Py_IS_INFINITY(x)) {
25            return x+x;
26        }
27        else {
28            return log(x) + ln2;          /* acosh(huge)=log(2x) */
29        }
30    }
31    else if (x == 1.) {
32        return 0.0;                     /* acosh(1) = 0 */
33    }
34    else if (x > 2.) {                  /* 2 < x < 2**28 */
35        double t = x * x;
36        return log(2.0 * x - 1.0 / (x + sqrt(t - 1.0)));
37    }
38    else {                              /* 1 < x <= 2 */ double t = x - 1.0; return m_log1p(t + sqrt(2.0 * t + t * t)); } } #endif /* HAVE_ACOSH */ #ifndef HAVE_ASINH /* asinh(x) * Method : * Based on * asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ] * we have * asinh(x) := x if 1+x*x=1, * := sign(x)*(log(x)+ln2)) for large |x|, else * := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else
39 *               := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2)))
40 */
41
42double
43_Py_asinh(double x)
44{
45    double w;
46    double absx = fabs(x);
47
48    if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) {
49        return x+x;
50    }
51    if (absx < two_pow_m28) {           /* |x| < 2**-28 */ return x; /* return x inexact except 0 */ } if (absx > two_pow_p28) {           /* |x| > 2**28 */
52        w = log(absx) + ln2;
53    }
54    else if (absx > 2.0) {              /* 2 < |x| < 2**28 */
55        w = log(2.0 * absx + 1.0 / (sqrt(x * x + 1.0) + absx));
56    }
57    else {                              /* 2**-28 <= |x| < 2= */ double t = x*x; w = m_log1p(absx + t / (1.0 + sqrt(1.0 + t))); } return copysign(w, x); } #endif /* HAVE_ASINH */ #ifndef HAVE_ATANH /* atanh(x) * Method : * 1.Reduced x to positive by atanh(-x) = -atanh(x) * 2.For x>=0.5
58 *                  1              2x                          x
59 *      atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * -------)
60 *                  2             1 - x                      1 - x
61 *
62 *      For x<0.5 * atanh(x) = 0.5*log1p(2x+2x*x/(1-x)) * * Special cases: * atanh(x) is NaN if |x| >= 1 with signal;
63 *      atanh(NaN) is that NaN with no signal;
64 *
65 */
66
67double
68_Py_atanh(double x)
69{
70    double absx;
71    double t;
72
73    if (Py_IS_NAN(x)) {
74        return x+x;
75    }
76    absx = fabs(x);
77    if (absx >= 1.) {                   /* |x| >= 1 */
78        errno = EDOM;
79#ifdef Py_NAN
80        return Py_NAN;
81#else
82        return x / zero;
83#endif
84    }
85    if (absx < two_pow_m28) {           /* |x| < 2**-28 */
86        return x;
87    }
88    if (absx < 0.5) {                   /* |x| < 0.5 */
89        t = absx+absx;
90        t = 0.5 * m_log1p(t + t*absx / (1.0 - absx));
91    }
92    else {                              /* 0.5 <= |x| <= 1.0 */ t = 0.5 * m_log1p((absx + absx) / (1.0 - absx)); } return copysign(t, x); } #endif /* HAVE_ATANH */ #ifndef HAVE_EXPM1 /* Mathematically, expm1(x) = exp(x) - 1. The expm1 function is designed to avoid the significant loss of precision that arises from direct evaluation of the expression exp(x) - 1, for x near 0. */ double _Py_expm1(double x) { /* For abs(x) >= log(2), it's safe to evaluate exp(x) - 1 directly; this
93       also works fine for infinities and nans.
94       For smaller x, we can use a method due to Kahan that achieves close to
95       full accuracy.
96    
97    double y;
98    if (fabs(x) < DBL_EPSILON / 2.) {
99        return x;
100    }
101    else if (-0.5 <= x && x <= 1.) {
102        /* WARNING: it's possible that an overeager compiler
103           will incorrectly optimize the following two lines
104           to the equivalent of "return log(1.+x)". If this
105           happens, then results from log1p will be inaccurate
106           for small x. */
107        y = 1.+x;
108        return log(y) - ((y - 1.) - x) / y;
109    }
110    else {
111        /* NaNs and infinities should end up here */
112        return log(1.+x);
113    }
114}
115

پیاده‌سازی PyPy

پیاده‌سازی PyPy، یک مفسر برای زبان پایتون محسوب می‌شود. برای پیاده‌سازی این مفسر، از یک زیر مجموعه خاصی از زبان پایتون به نام RPython استفاده شده است. این زبان، از نوع زبان‌های Statically-Typed است؛ یعنی، نوع‌داده‌ای متغیرها باید به طور صریح مشخص شده باشد (به عبارت دیگر، در زمان بررسی کدها و نه در زمان اجرا، نوع دده‌ای متغیرها مشخص می‌شود). این مفسر پایتون از ویژگی مشخصه‌ای به نام کامپایلر Just-in-Time یا JIT استفاده می‌کند.

در کامپایل کردن Just-in-Time کدها، عمل کامپایل کردن کدهای نوشته شده، به جای اینکه پیش از اجرای برنامه انجام شود، در «زمان اجرا» (Run-Time) انجام می‌شود. مفسر PyPy برای کامپایل کردن Just-in-Time کدها، از Back-End های مختلفی نظیر C ،CLI و «ماشین مجازی جاوا» (Java Virtual Machine | JVM) پشتیبانی می‌کند.

هدف از توسعه مفسر PyPy، ایجاد مطابقت حداکثری با پیاده‌سازی CPython زبان برنامه‌نویسی پایتون و ارتقاء عملکرد مفسر پایتون هنگام اجرای کدها است. در صورتی کاربر، برنامه‌نویس یا توسعه‌دهنده به دنبال افزایش عملکرد کدهای پایتون در زمان اجرا باشد، استفاده از مفسر PyPy یکی از بهترین گزینه‌های ممکن خواهد بود. با توجه به معیارهای مختلف ارزیابی عملکرد کدها، سرعت اجرای کدها در مفسر PyPy چیزی حدود پنج برابر بیشتر از مفسر CPython است. مفسر PyPy از نسخه 2.7 مفسر پایتون پشتیبانی می‌کند و مفسر PyPy3 برای پشتیبانی از نسخه 3 پایتون توسعه داده شده است.

در ادامه، نمونه‌ای از پیاده‌سازی‌های انجام شده برای برخی ار توابع ریاضی، در مفسر PyPy نمایش داده شده است. برای پیاده‌سازی این توابع ریاضی از زبان RPython‎ استفاده شده است.

1# Package initialisation
2from pypy.interpreter.mixedmodule import MixedModule
3
4class Module(MixedModule):
5    appleveldefs = {
6       'factorial' : 'app_math.factorial'
7    }
8
9    interpleveldefs = {
10       'e'              : 'interp_math.get(space).w_e', 
11       'pi'             : 'interp_math.get(space).w_pi', 
12       'pow'            : 'interp_math.pow',
13       'cosh'           : 'interp_math.cosh',
14       'copysign'       : 'interp_math.copysign',
15       'ldexp'          : 'interp_math.ldexp',
16       'hypot'          : 'interp_math.hypot',
17       'tan'            : 'interp_math.tan',
18       'asin'           : 'interp_math.asin',
19       'fabs'           : 'interp_math.fabs',
20       'floor'          : 'interp_math.floor',
21       'sqrt'           : 'interp_math.sqrt',
22       'frexp'          : 'interp_math.frexp',
23       'degrees'        : 'interp_math.degrees',
24       'log'            : 'interp_math.log',
25       'log10'          : 'interp_math.log10',
26       'fmod'           : 'interp_math.fmod',
27       'atan'           : 'interp_math.atan',
28       'ceil'           : 'interp_math.ceil',
29       'sinh'           : 'interp_math.sinh',
30       'cos'            : 'interp_math.cos',
31       'tanh'           : 'interp_math.tanh',
32       'radians'        : 'interp_math.radians',
33       'sin'            : 'interp_math.sin',
34       'atan2'          : 'interp_math.atan2',
35       'modf'           : 'interp_math.modf',
36       'exp'            : 'interp_math.exp',
37       'expm1'          : 'interp_math.expm1',
38       'acos'           : 'interp_math.acos',
39       'isinf'          : 'interp_math.isinf',
40       'isnan'          : 'interp_math.isnan',
41       'trunc'          : 'interp_math.trunc',
42       'fsum'           : 'interp_math.fsum',
43       'asinh'          : 'interp_math.asinh',
44       'acosh'          : 'interp_math.acosh',
45       'atanh'          : 'interp_math.atanh',
46       'log1p'          : 'interp_math.log1p',
47       'expm1'          : 'interp_math.expm1',
48       'erf'            : 'interp_math.erf',
49       'erfc'           : 'interp_math.erfc',
50       'gamma'          : 'interp_math.gamma',
51       'lgamma'         : 'interp_math.lgamma',
52}
53
54import sys, math
55
56INFINITY = 1e200 * 1e200
57NAN = abs(INFINITY / INFINITY)
58
59def isinf(x):
60    return x == INFINITY or x == -INFINITY
61
62def isnan(v):
63    return v != v
64
65def positiveinf(x):
66    return isinf(x) and x > 0.0
67
68def negativeinf(x):
69    return isinf(x) and x < 0.0
70
71def finite(x):
72    return not isinf(x) and not isnan(x)
73
74unary_math_functions = ['acos', 'asin', 'atan',
75                        'ceil', 'cos', 'cosh', 'exp', 'fabs', 'floor',
76                        'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'log', 'log10',
77                        'acosh', 'asinh', 'atanh', 'log1p', 'expm1']
78binary_math_functions = ['atan2', 'fmod', 'hypot', 'pow']
79
80class MathTests:
81
82    REGCASES = []
83    for name in unary_math_functions:
84        try:
85            input, output = (0.3,), getattr(math, name)(0.3)
86        except AttributeError:
87            # cannot test this function
88            continue
89        except ValueError:
90            input, output = (1.3,), getattr(math, name)(1.3)
91        REGCASES.append((name, input, output))
92
93    IRREGCASES = [
94        ('atan2', (0.31, 0.123), math.atan2(0.31, 0.123)),
95        ('fmod',  (0.31, 0.123), math.fmod(0.31, 0.123)),
96        ('hypot', (0.31, 0.123), math.hypot(0.31, 0.123)),
97        ('pow',   (0.31, 0.123), math.pow(0.31, 0.123)),
98        ('pow',   (-0.31, 0.123), ValueError),
99        ('pow',   (-0.5, 2.0), 0.25),
100        ('pow',   (-0.5, 1.0), -0.5),
101        ('pow',   (-0.5, 0.0), 1.0),
102        ('pow',   (-0.5, -1.0), -2.0),
103        ('ldexp', (3.375, 2), 13.5),
104        ('ldexp', (1.0, -10000), 0.0),   # underflow
105        ('frexp', (-1.25,), lambda x: x == (-0.625, 1)),
106        ('modf',  (4.25,), lambda x: x == (0.25, 4.0)),
107        ('modf',  (-4.25,), lambda x: x == (-0.25, -4.0)),
108        ('copysign', (1.5, 0.0), 1.5),
109        ('copysign', (1.5, -0.0), -1.5),
110        ('copysign', (1.5, INFINITY), 1.5),
111        ('copysign', (1.5, -INFINITY), -1.5),
112        ('copysign', (1.5, NAN), 1.5),
113        ('copysign', (1.75, -NAN), -1.75),      # special case for -NAN here
114        ]
115
116    OVFCASES = [
117        ('cosh', (9999.9,), OverflowError),
118        ('sinh', (9999.9,), OverflowError),
119        ('exp', (9999.9,), OverflowError),
120        ('pow', (10.0, 40000.0), OverflowError),
121        ('ldexp', (10.0, 40000), OverflowError),
122        ('log', (0.0,), ValueError), #cpython does it this way
123        ('log1p', (-1.0,), OverflowError),
124        ('log', (-1.,), ValueError),
125        ('log10', (0.0,), ValueError), #cpython does it this way
126        ]
127
128    INFCASES = [
129        ('acos', (INFINITY,), ValueError),
130        ('acos', (-INFINITY,), ValueError),
131        ('asin', (INFINITY,), ValueError),
132        ('asin', (-INFINITY,), ValueError),
133        ('atan', (INFINITY,), math.pi / 2),
134        ('atan', (-INFINITY,), -math.pi / 2),
135        ('atanh', (INFINITY,), ValueError),
136        ('atanh', (-INFINITY,), ValueError),
137        ('ceil', (INFINITY,), positiveinf),
138        ('ceil', (-INFINITY,), negativeinf),
139        ('cos', (INFINITY,), ValueError),
140        ('cos', (-INFINITY,), ValueError),
141        ('cosh', (INFINITY,), positiveinf),
142        ('cosh', (-INFINITY,), positiveinf),
143        ('exp', (INFINITY,), positiveinf),
144        ('exp', (-INFINITY,), 0.0),
145        ('fabs', (INFINITY,), positiveinf),
146        ('fabs', (-INFINITY,), positiveinf),
147        ('floor', (INFINITY,), positiveinf),
148        ('floor', (-INFINITY,), negativeinf),
149        ('sin', (INFINITY,), ValueError),
150        ('sin', (-INFINITY,), ValueError),
151        ('sinh', (INFINITY,), positiveinf),
152        ('sinh', (-INFINITY,), negativeinf),
153        ('sqrt', (INFINITY,), positiveinf),
154        ('sqrt', (-INFINITY,), ValueError),
155        ('tan', (INFINITY,), ValueError),
156        ('tan', (-INFINITY,), ValueError),
157        ('tanh', (INFINITY,), 1.0),
158        ('tanh', (-INFINITY,), -1.0),
159        ('log', (INFINITY,), positiveinf),
160        ('log', (-INFINITY,), ValueError),
161        ('log10', (INFINITY,), positiveinf),
162        ('log10', (-INFINITY,), ValueError),
163        ('frexp', (INFINITY,), lambda x: isinf(x[0])),
164        ('ldexp', (INFINITY, 3), positiveinf),
165        ('ldexp', (-INFINITY, 3), negativeinf),
166        ('modf',  (INFINITY,), lambda x: positiveinf(x[1])),
167        ('modf',  (-INFINITY,), lambda x: negativeinf(x[1])),
168        ('pow', (INFINITY, 0.0), 1.0),
169        ('pow', (INFINITY, 0.001), positiveinf),
170        ('pow', (INFINITY, -0.001), 0.0),
171        ('pow', (-INFINITY, 0.0), 1.0),
172        ('pow', (-INFINITY, 0.001), positiveinf),
173        ('pow', (-INFINITY, -0.001), 0.0),
174        ('pow', (-INFINITY, 3.0), negativeinf),
175        ('pow', (-INFINITY, 6.0), positiveinf),
176        ('pow', (-INFINITY, -13.0), -0.0),
177        ('pow', (-INFINITY, -128.0), 0.0),
178        ('pow', (1.001, INFINITY), positiveinf),
179        ('pow', (1.0,   INFINITY), 1.0),
180        ('pow', (0.999, INFINITY), 0.0),
181        ('pow', (-0.999,INFINITY), 0.0),
182        #('pow', (-1.0, INFINITY), 1.0, but strange, could also be -1.0),
183        ('pow', (-1.001,INFINITY), positiveinf),
184        ('pow', (1.001, -INFINITY), 0.0),
185        ('pow', (1.0,   -INFINITY), 1.0),
186        #('pow', (0.999, -INFINITY), positiveinf, but get OverflowError),
187        #('pow', (INFINITY, INFINITY), positiveinf, but get OverflowError),
188        ('pow', (INFINITY, -INFINITY), 0.0),
189        ('pow', (-INFINITY, INFINITY), positiveinf),
190        ]
191
192    IRREGERRCASES = [
193        #
194        ('atan2', (INFINITY, -2.3), math.pi / 2),
195        ('atan2', (INFINITY, 0.0), math.pi / 2),
196        ('atan2', (INFINITY, 3.0), math.pi / 2),
197        #('atan2', (INFINITY, INFINITY), ?strange),
198        ('atan2', (2.1, INFINITY), 0.0),
199        ('atan2', (0.0, INFINITY), 0.0),
200        ('atan2', (-0.1, INFINITY), -0.0),
201        ('atan2', (-INFINITY, 0.4), -math.pi / 2),
202        ('atan2', (2.1, -INFINITY), math.pi),
203        ('atan2', (0.0, -INFINITY), math.pi),
204        ('atan2', (-0.1, -INFINITY), -math.pi),
205        #
206        ('fmod', (INFINITY, 1.0), ValueError),
207        ('fmod', (1.0, INFINITY), 1.0),
208        ('fmod', (1.0, -INFINITY), 1.0),
209        ('fmod', (INFINITY, INFINITY), ValueError),
210        #
211        ('hypot', (-INFINITY, 1.0), positiveinf),
212        ('hypot', (-2.3, -INFINITY), positiveinf),
213        ]
214
215    binary_math_functions = ['atan2', 'fmod', 'hypot', 'pow']
216
217    NANCASES1 = [
218        (name, (NAN,), isnan) for name in unary_math_functions]
219    NANCASES2 = [
220        (name, (NAN, 0.1), isnan) for name in binary_math_functions]
221    NANCASES3 = [
222        (name, (-0.2, NAN), isnan) for name in binary_math_functions]
223    NANCASES4 = [
224        (name, (NAN, -INFINITY), isnan) for name in binary_math_functions
225                                        if name != 'hypot']
226    NANCASES5 = [
227        (name, (INFINITY, NAN), isnan) for name in binary_math_functions
228                                       if name != 'hypot']
229    NANCASES6 = [
230        ('frexp', (NAN,), lambda x: isnan(x[0])),
231        ('ldexp', (NAN, 2), isnan),
232        ('hypot', (NAN, INFINITY), positiveinf),
233        ('hypot', (NAN, -INFINITY), positiveinf),
234        ('hypot', (INFINITY, NAN), positiveinf),
235        ('hypot', (-INFINITY, NAN), positiveinf),
236        ('modf', (NAN,), lambda x: (isnan(x[0]) and isnan(x[1]))),
237        ]
238
239    # The list of test cases.  Note that various tests import this,
240    # notably in rpython/lltypesystem/module and in translator/c/test.
241    TESTCASES = (REGCASES + IRREGCASES + OVFCASES + INFCASES + IRREGERRCASES
242                 + NANCASES1 + NANCASES2 + NANCASES3 + NANCASES4 + NANCASES5
243                 + NANCASES6)
244
245
246def get_tester(expected):
247    if type(expected) is type(Exception):
248        def tester(value):
249            return False
250    elif callable(expected):
251        def tester(value):
252            ok = expected(value)
253            assert isinstance(ok, bool)
254            return ok
255    else:
256        assert finite(expected), "badly written test"
257        def tester(got):
258            gotsign = expectedsign = 1
259            if got < 0.0: gotsign = -gotsign
260            if expected < 0.0: expectedsign = -expectedsign
261            return finite(got) and (got == expected and
262                                    gotsign == expectedsign)
263    return tester
264

پیاده‌سازی Jython

پیاده‌سازی Jython، یک پیاده‌سازی ارائه شده از مفسر پایتون محسوب می‌شود که کدهای پایتون را به «بایت‌کدهای» (ByteCodes) جاوا تبدیل می‌کند. سپس، بایت‌کدهای تولید شده توسط ماشین مجازی جاوا اجرا می‌شوند. همچنین، این امکان در مفسر Jython فراهم شده است تا کلاس‌های جاوا را مانند ماژول‌های پایتون Import کرد و مورد استفاده قرار داد.

در صورتی که برنامه‌نویس یا توسعه‌دهنده تمایل داشته باشد جهت  کد نویسی برنامه‌های کاربردی از زبان پایتون استفاده کند، ولی منابع و کتابخانه‌هایی باید استفاده شوند که از کدهای نوشته شده به زبان جاوا به عنوان پاید کد استفاده می‌کنند، بهترین راه استفاده از مفسر Jython است. مفسر Jython تا نسخه 2.7 زبان پایتون را پشتیبانی می‌کند. با این حال، مفسر Jython پشتیبانی کننده از نسخه 3 پایتون در حال توسعه است و در مرحله بتا قرار دارد.

در ادامه، نمونه‌ای از پیاده‌سازی‌های انجام شده برای برخی ار توابع ریاضی، در مفسر Jython نمایش داده شده است. برای پیاده‌سازی این توابع ریاضی از زبان جاوا استفاده شده است.

1package org.python.modules;
2
3import org.python.core.Py;
4import org.python.core.PyComplex;
5import org.python.core.PyException;
6import org.python.core.PyFloat;
7import org.python.core.PyInstance;
8import org.python.core.PyObject;
9import org.python.core.PyTuple;
10
11public class cmath {
12
13    public static final PyFloat pi = new PyFloat(Math.PI);
14    public static final PyFloat e = new PyFloat(Math.E);
15
16    /** 2<sup></sup> (Ref: Abramowitz & Stegun [1972], p2). */
17    private static final double ROOT_HALF = 0.70710678118654752440;
18    /** ln({@link Double#MAX_VALUE}) or a little less */
19    private static final double NEARLY_LN_DBL_MAX = 709.4361393;
20    
21    private static final double ATLEAST_27LN2 = 18.72;
22    private static final double HALF_E2 = 0.5 * Math.E * Math.E;
23
24    /** log<sub>10</sub>e (Ref: Abramowitz & Stegun [1972], p3). */
25    private static final double LOG10E = 0.43429448190325182765;
26
27    private static PyComplex complexFromPyObject(PyObject obj) {
28        // If op is already of type PyComplex_Type, return its value
29        if (obj instanceof PyComplex) {
30            return (PyComplex)obj;
31        }
32
33        // If not, use op's __complex__ method, if it exists
34        PyObject newObj = null;
35        if (obj instanceof PyInstance) {
36            // this can go away in python 3000
37            if (obj.__findattr__("__complex__") != null) {
38                newObj = obj.invoke("__complex__");
39            }
40            // else try __float__
41        } else {
42            PyObject complexFunc = obj.getType().lookup("__complex__");
43            if (complexFunc != null) {
44                newObj = complexFunc.__call__(obj);
45            }
46        }
47
48        if (newObj != null) {
49            if (!(newObj instanceof PyComplex)) {
50                throw Py.TypeError("__complex__ should return a complex object");
51            }
52            return (PyComplex)newObj;
53        }
54
55        // If neither of the above works, interpret op as a float giving the real part of
56        // the result, and fill in the imaginary part as 0
57        return new PyComplex(obj.asDouble(), 0);
58    }
59
60    public static PyComplex acos(PyObject w) {
61        return _acos(complexFromPyObject(w));
62    }
63
64    
65    private static PyComplex _acos(PyComplex w) {
66
67        // Let z = x + iy and w = u + iv.
68        double x, y, u = w.real, v = w.imag;
69
70        if (Math.abs(u) > 0x1p27 || Math.abs(v) > 0x1p27) {
71            /*
72             * w is large: approximate 2cos(z) by exp(i(x+iy)) or exp(-i(x+iy)), whichever
73             * dominates. Hence, z = x+iy = i ln(2(u+iv)) or -i ln(2(u+iv))
74             */
75            x = Math.atan2(Math.abs(v), u);
76            y = Math.copySign(logHypot(u, v) + math.LN2, -v);
77
78        } else if (Double.isNaN(v)) {
79            // Special cases
80            x = (u == 0.) ? Math.PI / 2. : v;
81            y = v;
82
83        } else {
84            // Normal case, without risk of overflow.
85            PyComplex a = sqrt(new PyComplex(1. - u, -v)); // a = sqrt(1-w) = sqrt(2) sin(z/2)
86            PyComplex b = sqrt(new PyComplex(1 + u, v));   // b = sqrt(1+w) = sqrt(2) cos(z/2)
87            // Arguments here are sin(x/2)cosh(y/2), cos(x/2)cosh(y/2) giving tan(x/2)
88            x = 2. * Math.atan2(a.real, b.real);
89            // 2 (cos(x/2)**2+sin(x/2)**2) sinh(y/2)cosh(y/2) = sinh y
90            y = math.asinh(a.imag * b.real - a.real * b.imag);
91        }
92
93        // If that generated a nan, and there wasn't one in the argument, raise a domain error.
94        return exceptNaN(new PyComplex(x, y), w);
95    }
96
97    
98    public static PyComplex acosh(PyObject w) {
99        return _acosh(complexFromPyObject(w));
100    }
101
102    
103    private static PyComplex _acosh(PyComplex w) {
104
105        // Let z = x + iy and w = u + iv.
106        double x, y, u = w.real, v = w.imag;
107
108        if (Math.abs(u) > 0x1p27 || Math.abs(v) > 0x1p27) {
109            /*
110             * w is large: approximate 2cosh(z) by exp(x+iy) or exp(-x-iy), whichever dominates.
111             * Hence, z = x+iy = ln(2(u+iv)) or -ln(2(u+iv))
112             */
113            x = logHypot(u, v) + math.LN2;
114            y = Math.atan2(v, u);
115
116        } else if (v == 0. && !Double.isNaN(u)) {
117            /*
118             * We're on the real axis (and maybe the branch cut). u = cosh x cos y. In all cases,
119             * the sign of y follows v.
120             */
121            if (u >= 1.) {
122                // As real library, cos y = 1, u = cosh x.
123                x = math.acosh(u);
124                y = v;
125            } else if (u < -1.) {
126                // Left part of cut: cos y = -1, u = -cosh x
127                x = math.acosh(-u);
128                y = Math.copySign(Math.PI, v);
129            } else {
130                // -1 <= u <= 1: cosh x = 1, u = cos y.
131                x = 0.;
132                y = Math.copySign(Math.acos(u), v);
133            }
134
135        } else {
136            // Normal case, without risk of overflow.
137            PyComplex a = sqrt(new PyComplex(u - 1., v)); // a = sqrt(w-1) = sqrt(2) sinh(z/2)
138            PyComplex b = sqrt(new PyComplex(u + 1., v)); // b = sqrt(w+1) = sqrt(2) cosh(z/2)
139            // 2 sinh(x/2)cosh(x/2) (cos(y/2)**2+sin(y/2)**2) = sinh x
140            x = math.asinh(a.real * b.real + a.imag * b.imag);
141            // Arguments here are cosh(x/2)sin(y/2) and cosh(x/2)cos(y/2) giving tan y/2
142            y = 2. * Math.atan2(a.imag, b.real);
143        }
144
145        // If that generated a nan, and there wasn't one in the argument, raise a domain error.
146        return exceptNaN(new PyComplex(x, y), w);
147    }
148
149    public static PyComplex asin(PyObject w) {
150        return asinOrAsinh(complexFromPyObject(w), false);
151    }
152
153    public static PyComplex asinh(PyObject w) {
154        return asinOrAsinh(complexFromPyObject(w), true);
155    }
156
157    private static PyComplex asinOrAsinh(PyComplex w, boolean h) {
158        double u, v, x, y;
159        PyComplex z;
160
161        if (h) {
162            // We compute z = asinh(w). Let z = x + iy and w = u + iv.
163            u = w.real;
164            v = w.imag;
165            // Then the function body computes x + iy = asinh(w).
166        } else {
167            // We compute w = asin(z). Unusually, let w = u - iv, so u + iv = iw.
168            v = w.real;
169            u = -w.imag;
170            // Then as before, the function body computes asinh(u+iv) = asinh(iw) = i asin(w),
171            // but we finally return z = y - ix = -i asinh(iw) = asin(w).
172        }
173
174        if (Double.isNaN(u)) {
175            // Special case for nan in real part. Default clause deals naturally with v=nan.
176            if (v == 0.) {
177                x = u;
178                y = v;
179            } else if (Double.isInfinite(v)) {
180                x = Double.POSITIVE_INFINITY;
181                y = u;
182            } else { // Any other value of v -> nan+nanj
183                x = y = u;
184            }
185
186        } else if (Math.abs(u) > 0x1p27 || Math.abs(v) > 0x1p27) {
187            /*
188             * w is large: approximate 2sinh(z) by exp(x+iy) or -exp(-x-iy), whichever dominates.
189             * Hence, z = x+iy = ln(2(u+iv)) or -ln(-2(u+iv))
190             */
191            x = logHypot(u, v) + math.LN2;
192            if (Math.copySign(1., u) > 0.) {
193                y = Math.atan2(v, u);
194            } else {
195                // Adjust for sign, choosing the angle so that -pi/2 < y < pi/2
196                x = -x;
197                y = Math.atan2(v, -u);
198            }
199
200        } else {
201            // Normal case, without risk of overflow.
202            PyComplex a = sqrt(new PyComplex(1. + v, -u)); // a = sqrt(1-iw)
203            PyComplex b = sqrt(new PyComplex(1. - v, u));  // b = sqrt(1+iw)
204            // Combine the parts so as that terms in y cancel, leaving us with sinh x:
205            x = math.asinh(a.real * b.imag - a.imag * b.real);
206            // The arguments are v = cosh x sin y, and cosh x cos y
207            y = Math.atan2(v, a.real * b.real - a.imag * b.imag);
208        }
209
210        // Compose the result w according to whether we're computing asin(w) or asinh(w).
211        if (h) {
212            z = new PyComplex(x, y);    // z = x + iy = asinh(u+iv).
213        } else {
214            z = new PyComplex(y, -x);   // z = y - ix = -i asinh(v-iu) = asin(w)
215        }
216
217        // If that generated a nan, and there wasn't one in the argument, raise a domain error.
218        return exceptNaN(z, w);
219    }
220
221
222    public static PyComplex atan(PyObject w) {
223        return atanOrAtanh(complexFromPyObject(w), false);
224    }
225
226    
227    public static PyComplex atanh(PyObject w) {
228        return atanOrAtanh(complexFromPyObject(w), true);
229    }
230
231    private static PyComplex atanOrAtanh(PyComplex w, boolean h) {
232        double u, v, x, y;
233        PyComplex z;
234
235        if (h) {
236            // We compute z = atanh(w). Let z = x + iy and w = u + iv.
237            u = w.real;
238            v = w.imag;
239            // Then the function body computes x + iy = atanh(w).
240        } else {
241            // We compute w = atan(z). Unusually, let w = u - iv, so u + iv = iw.
242            v = w.real;
243            u = -w.imag;
244            // Then as before, the function body computes atanh(u+iv) = atanh(iw) = i atan(w),
245            // but we finally return z = y - ix = -i atanh(iw) = atan(w).
246        }
247
248        double absu = Math.abs(u), absv = Math.abs(v);
249
250        if (absu >= 0x1p511 || absv >= 0x1p511) {
251            // w is large: approximate atanh(w) by 1/w + i pi/2. 1/w = conjg(w)/|w|**2.
252            if (Double.isInfinite(absu) || Double.isInfinite(absv)) {
253                x = Math.copySign(0., u);
254            } else {
255                // w is also too big to square, carry a 2**-N scaling factor.
256                int N = 520;
257                double uu = Math.scalb(u, -N), vv = Math.scalb(v, -N);
258                double mod2w = uu * uu + vv * vv;
259                x = Math.scalb(uu / mod2w, -N);
260            }
261            // We don't need the imaginary part of 1/z. Just pi/2 with the sign of v. (If not nan.)
262            if (Double.isNaN(v)) {
263                y = v;
264            } else {
265                y = Math.copySign(Math.PI / 2., v);
266            }
267
268        } else if (absu < 0x1p-53) {
269            // u is small enough that u**2 may be neglected relative to 1.
270            if (absv > 0x1p-27) {
271                // v is not small, but is not near overflow either.
272                double v2 = v * v;
273                double d = 1. + v2;
274                x = Math.copySign(Math.log1p(4. * absu / d), u) * 0.25;
275                y = Math.atan2(2. * v, 1. - v2) * 0.5;
276            } else {
277                // v is also small enough that v**2 may be neglected (or is nan). So z = w.
278                x = u;
279                y = v;
280            }
281
282        } else if (absu == 1. && absv < 0x1p-27) {
283            // w is close to +1 or -1: needs a different expression, good as v->0
284            x = Math.copySign(Math.log(absv) - math.LN2, u) * 0.5;
285            if (v == 0.) {
286                y = Double.NaN;
287            } else {
288                y = Math.copySign(Math.atan2(2., absv), v) * 0.5;
289            }
290
291        } else {
292            /*
293             * Normal case, without risk of overflow. The basic expression is z =
294             * 0.5*ln((1+w)/(1-w)), which for positive u we rearrange as 0.5*ln(1+2w/(1-w)) and for
295             * negative u as -0.5*ln(1-2w/(1+w)). By use of absu, we reduce the difference between
296             * the expressions fo u>=0 and u<0 to a sign transfer.
297             */
298            double lmu = (1. - absu), lpu = (1. + absu), v2 = v * v;
299            double d = lmu * lmu + v2;
300            x = Math.copySign(Math.log1p(4. * absu / d), u) * 0.25;
301            y = Math.atan2(2. * v, lmu * lpu - v2) * 0.5;
302        }
303
304        // Compose the result w according to whether we're computing atan(w) or atanh(w).
305        if (h) {
306            z = new PyComplex(x, y);    // z = x + iy = atanh(u+iv).
307        } else {
308            z = new PyComplex(y, -x);   // z = y - ix = -i atanh(v-iu) = atan(w)
309        }
310
311        // If that generated a nan, and there wasn't one in the argument, raise a domain error.
312        return exceptNaN(z, w);
313    }

پیاده‌سازی IronPython

پیاده‌سازی IronPython، پیاده‌سازی مفسر پایتون برای محیط یا پلتفرم NET. محسوب می‌شود. مفسر IronPython به برنامه‌نویس و توسعه‌دهنده اجازه می‌دهد تا از کتابخانه‌های پایتون و NET. در برنامه‌های خود استفاده کنند. همچنین، IronPython امکان دسترسی به کدهای تمامی زبان‌های موجود در پلتفرم NET. را به توسعه‌دهندگان و برنامه‌نویسان پایتون می‌دهد.

ابزار Python Tools for Visual Studio، پیاده‌سازی IronPython از زبان پایتون را با محیط توسعه «ویژوال استودیو» (Visual Studio) یکپارچه می‌کند. چنین ابزاری برای کسانی که به دنبال استفاده از پایتون برای توسعه برنامه‌های ویندوز در محیط ویژوال استودیو هستند، بسیار ایده‌آل است. مفسر IronPython از نسخه 2.7 زبان پایتون پشتیبانی می‌کند. همچنین، ، مفسر IronPython پشتیبانی کننده از نسخه 3 پایتون در حال توسعه است و در مرحله بتا قرار دارد.

در ادامه، نمونه‌ای از پیاده‌سازی‌های انجام شده برای برخی ار توابع ریاضی، در مفسر IronPython نمایش داده شده است. برای پیاده‌سازی این توابع ریاضی از زبان سی‌شارپ (یا دیگر زبان‌های NET.) استفاده شده است.

1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.Linq;
6
7using Microsoft.Scripting;
8using Microsoft.Scripting.Runtime;
9using Microsoft.Scripting.Utils;
10
11using IronPython.Runtime;
12using IronPython.Runtime.Operations;
13using IronPython.Runtime.Types;
14
15using System.Numerics;
16
17[assembly: PythonModule("math", typeof(IronPython.Modules.PythonMath))]
18namespace IronPython.Modules {
19    public static partial class PythonMath {
20        public const string __doc__ = "Provides common mathematical functions.";
21
22        public const double pi = Math.PI;
23        public const double e = Math.E;
24
25        private const double degreesToRadians = Math.PI / 180.0;
26        private const int Bias = 0x3FE;
27
28        public static double degrees(double radians) {
29            return Check(radians, radians / degreesToRadians);
30        }
31
32        public static double radians(double degrees) {
33            return Check(degrees, degrees * degreesToRadians);
34        }
35
36        public static double fmod(double v, double w) {
37            return Check(v, w, v % w);
38        }
39
40        private static double sum(List<double> partials) {
41            // sum the partials the same was as CPython does
42            var n = partials.Count;
43            var hi = 0.0;
44
45            if (n == 0) return hi;
46
47            var lo = 0.0;
48
49            // sum exact
50            while (n > 0) {
51                var x = hi;
52                var y = partials[--n];
53                hi = x + y;
54                lo = y - (hi - x);
55                if (lo != 0.0)
56                    break;
57            }
58
59            if (n == 0) return hi;
60
61            // half-even rounding
62            if (lo < 0.0 && partials[n - 1] < 0.0 || lo > 0.0 && partials[n - 1] > 0.0) {
63                var y = lo * 2.0;
64                var x = hi + y;
65                var yr = x - hi;
66                if (y == yr)
67                    hi = x;
68            }
69            return hi;
70        }
71
72        public static double fsum(IEnumerable e) {
73            // msum from https://code.activestate.com/recipes/393090/
74            var partials = new List<double>();
75            foreach (var v in e.Cast<object>().Select(o => Converter.ConvertToDouble(o))) {
76                var x = v;
77                var i = 0;
78                for (var j = 0; j < partials.Count; j++) {
79                    var y = partials[j];
80                    if (Math.Abs(x) < Math.Abs(y)) {
81                        var t = x;
82                        x = y;
83                        y = t;
84                    }
85                    var hi = x + y;
86                    var lo = y - (hi - x);
87                    if (lo != 0) {
88                        partials[i++] = lo;
89                    }
90                    x = hi;
91                }
92                partials.RemoveRange(i, partials.Count - i);
93                partials.Add(x);
94            }
95
96            return sum(partials);
97        }
98
99        public static PythonTuple frexp(double v) {
100            if (Double.IsInfinity(v) || Double.IsNaN(v)) {
101                return PythonTuple.MakeTuple(v, 0.0);
102            }
103            int exponent = 0;
104            double mantissa = 0;
105
106            if (v == 0) {
107                mantissa = 0;
108                exponent = 0;
109            } else {
110                byte[] vb = BitConverter.GetBytes(v);
111                if (BitConverter.IsLittleEndian) {
112                    DecomposeLe(vb, out mantissa, out exponent);
113                } else {
114                    throw new NotImplementedException();
115                }
116            }
117
118            return PythonTuple.MakeTuple(mantissa, exponent);
119        }
120
121        public static PythonTuple modf(double v) {
122            if (double.IsInfinity(v)) {
123                return PythonTuple.MakeTuple(0.0, v);
124            }
125            double w = v % 1.0;
126            v -= w;
127            return PythonTuple.MakeTuple(w, v);
128        }
129
130        public static double ldexp(double v, BigInteger w) {
131            if (v == 0.0 || double.IsInfinity(v)) {
132                return v;
133            }
134            return Check(v, v * Math.Pow(2.0, (double)w));
135        }
136
137        public static double hypot(double v, double w) {
138            if (double.IsInfinity(v) || double.IsInfinity(w)) {
139                return double.PositiveInfinity;
140            }
141            return Check(v, w, MathUtils.Hypot(v, w));
142        }
143
144        public static double pow(double v, double exp) {
145            if (v == 1.0 || exp == 0.0) {
146                return 1.0;
147            } else if (double.IsNaN(v) || double.IsNaN(exp)) {
148                return double.NaN;
149            } else if (v == 0.0) {
150                if (exp > 0.0) {
151                    return 0.0;
152                }
153                throw PythonOps.ValueError("math domain error");
154            } else if (double.IsPositiveInfinity(exp)) {
155                if (v > 1.0 || v < -1.0) {
156                    return double.PositiveInfinity;
157                } else if (v == -1.0) {
158                    return 1.0;
159                } else {
160                    return 0.0;
161                }
162            } else if (double.IsNegativeInfinity(exp)) {
163                if (v > 1.0 || v < -1.0) {
164                    return 0.0;
165                } else if (v == -1.0) {
166                    return 1.0;
167                } else {
168                    return double.PositiveInfinity;
169                }
170            }
171            return Check(v, exp, Math.Pow(v, exp));
172        }
173
174        public static double log(double v0) {
175            if (v0 <= 0.0) {
176                throw PythonOps.ValueError("math domain error");
177            }
178            return Check(v0, Math.Log(v0));
179        }
180
181        public static double log(double v0, double v1) {
182            if (v0 <= 0.0 || v1 == 0.0) {
183                throw PythonOps.ValueError("math domain error");
184            } else if (v1 == 1.0) {
185                throw PythonOps.ZeroDivisionError("float division");
186            } else if (v1 == Double.PositiveInfinity) {
187                return 0.0;
188            }
189            return Check(Math.Log(v0, v1));
190        }
191
192        public static double log(BigInteger value) {
193            if (value.Sign <= 0) {
194                throw PythonOps.ValueError("math domain error");
195            }
196            return value.Log();
197        }
198
199        public static double log(object value) {
200            // CPython tries float first, then double, so we need
201            // an explicit overload which properly matches the order here
202            double val;
203            if (Converter.TryConvertToDouble(value, out val)) {
204                return log(val);
205            } else {
206                return log(Converter.ConvertToBigInteger(value));
207            }
208        }
209
210        public static double log(BigInteger value, double newBase) {
211            if (newBase <= 0.0 || value <= 0) {
212                throw PythonOps.ValueError("math domain error");
213            } else if (newBase == 1.0) {
214                throw PythonOps.ZeroDivisionError("float division");
215            } else if (newBase == Double.PositiveInfinity) {
216                return 0.0;
217            }
218            return Check(value.Log(newBase));
219        }
220
221        public static double log(object value, double newBase) {
222            // CPython tries float first, then double, so we need
223            // an explicit overload which properly matches the order here
224            double val;
225            if (Converter.TryConvertToDouble(value, out val)) {
226                return log(val, newBase);
227            } else {
228                return log(Converter.ConvertToBigInteger(value), newBase);
229            }
230        }
231
232        public static double log10(double v0) {
233            if (v0 <= 0.0) {
234                throw PythonOps.ValueError("math domain error");
235            }
236            return Check(v0, Math.Log10(v0));
237        }

پیاده‌سازی PythonNet

ابزار PythonNet (که مخفف Python for .NET است)، عکس رویکرد IronPython را جهت استفاده از پایتون در محیط NET. اتخاذ کرده است. این ابزار به برنامه‌نویسان و توسعه‌دهندگان وب اجازه می‌دهد تا مفسر پایتون نصب شده در سیستم را با مؤلفه ماشین مجازی محیط NET. (که به آن، مؤلفه CLR یا Common Language Runtime گفته می‌شود) یکپارچه کنند.

با ترکیب ابزار PythonNet با Mono (پلتفرم نرم‌افزاری که به برنامه‌نویسان اجازه می‌دهد برنامه‌‌های کاربردی چندسکویی (Cross-Platform) را به راحتی توسعه دهند)، این امکان فراهم می‌شود تا مفسر پایتون نصب شده در سیستم‌های عامل غیر ویندوز نظیر لینوکس و مک، بتواند با محیط NET. تعامل داشته باشد و درون آن فعالیت کند. یکی از مزیت‌های مهم ابزار PythonNet، پشتیبانی از نسخه‌های 2.6 تا 3.5 مفسر پایتون است.

مجموعه آموزش‌های مرتبط با زبان برنامه‌نویسی پایتون که در مجله فرادرس تهیه شده‌اند و برای عموم مخاطبان و خوانندگان در دسترس قرار گرفته‌اند، در اینجا گردآوری شده‌اند.

در صورتی که تمایل دارید با زبان برنامه‌نویسی پایتون و نحوه کدنویسی در این زبان آشنا شوید، توصیه می‌شود که آموزش‌های ارائه شده در این مطلب را مطالعه کنید. در اینجا به پایان مطلب برنامه نویسی اندروید با پایتون می‌رسیم.

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

^^

بر اساس رای ۶ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
Python-Guide
نظر شما چیست؟

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