numbers --- کلاسهای پایه انتزاعی عددی¶
کد منبع: Lib/numbers.py
ماژول numbers (PEP 3141) سلسلهمراتبی از کلاسهای پایه انتزاعی عددی را تعریف میکند که بهتدریج عملیات بیشتری را تعریف میکنند. هیچکدام از انواع تعریفشده در این ماژول برای نمونهسازی در نظر گرفته نشدهاند.
- class numbers.Number¶
ریشهی سلسلهمراتب عددی. اگر فقط میخواهید بررسی کنید که آرگومان x یک عدد است، بدون توجه به نوع آن، از
isinstance(x, Number)استفاده کنید.
برج عددی¶
- class numbers.Complex¶
زیرکلاسهای این نوع، اعداد مختلط را توصیف میکنند و شامل عملیاتهایی میشوند که بر نوع توکار
complexاعمال میشوند. این موارد عبارتاند از: تبدیلها بهcomplexوbool،real،imag،+،-،*،/،**،abs()،conjugate()،==، و!=. همه بهجز-و!=انتزاعی هستند.- real¶
انتزاعی. کامپوننت حقیقی این عدد را بازیابی میکند.
- imag¶
انتزاعی. کامپوننت موهومی این عدد را بازیابی میکند.
- abstractmethod conjugate()¶
انتزاعی. مزدوج مختلط را برمیگرداند. برای مثال،
(1+3j).conjugate() == (1-3j).
- class numbers.Real¶
به
Complex، کلاسRealعملگرهایی را میافزاید که روی اعداد حقیقی کار میکنند.بهطور خلاصه، آنها عبارتند از: تبدیل به
float،math.trunc()،round()،math.floor()،math.ceil()،divmod()،//،%،<،<=،>، و>=.Real همچنین برای
complex()،real،imagوconjugate()پیشفرضهایی فراهم میکند.
- class numbers.Rational¶
زیرنوعی از
Realاست و ویژگیهایnumeratorوdenominatorرا میافزاید. همچنین یک پیادهسازی پیشفرض برایfloat()فراهم میکند.مقدارهای
numeratorوdenominatorباید نمونههایی ازIntegralباشند و باید در سادهترین شکل باشند، بهطوری کهdenominatorمثبت باشد.- numerator¶
انتزاعی. صورت این عدد گویا.
- denominator¶
انتزاعی. مخرج این عدد گویا.
نکاتی برای پیادهسازان نوع¶
پیادهسازان باید دقت کنند که اعداد برابر، برابر باشند و برای آنها مقادیر هش یکسانی تولید کنند. اگر دو گسترش متفاوت از اعداد حقیقی وجود داشته باشد، این موضوع ممکن است ظریف باشد. همچنین Hashing of numeric types را ببینید.
افزودن کلاسهای پایه انتزاعی عددی بیشتر¶
البته، کلاسهای پایه انتزاعی (ABC) بیشتری برای اعداد امکانپذیر هستند، و این سلسلهمراتب ضعیفی خواهد بود اگر امکان افزودن آنها را از بین ببرد. میتوانید MyFoo را بین Complex و Real به این صورت اضافه کنید:
class MyFoo(Complex): ...
MyFoo.register(Real)
پیادهسازی عملیات حسابی¶
ما میخواهیم عملیات حسابی را بهگونهای پیادهسازی کنیم که عملیات حالت مختلط، یا یک پیادهسازی را فراخوانی کنند که نویسندهی آن از نوع هر دو آرگومان آگاه بوده است، یا هر دو را به نزدیکترین نوع توکار تبدیل کنند و عملیات را در آنجا انجام دهند. برای زیرنوعهای Integral، این بدان معنا است که __add__() و __radd__() باید بهصورت زیر تعریف شوند:
class MyIntegral(Integral):
def __add__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(self, other)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(self, other)
else:
return NotImplemented
def __radd__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(other, self)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(other, self)
elif isinstance(other, Integral):
return int(other) + int(self)
elif isinstance(other, Real):
return float(other) + float(self)
elif isinstance(other, Complex):
return complex(other) + complex(self)
else:
return NotImplemented
برای یک عملیات با نوع مختلط روی زیرکلاسهای Complex، ۵ حالت مختلف وجود دارد. تمام کد بالا را که به MyIntegral و OtherTypeIKnowAbout ارجاع نمیدهد، «کد پیشساخته (boilerplate)» مینامم. a نمونهای از A خواهد بود که زیرنوعی از Complex است (a : A <: Complex)، و b : B <: Complex. a + b را در نظر میگیرم:
اگر
Aمتد__add__()را تعریف کند کهbرا بپذیرد، همهچیز درست است.اگر
Aبه کد پیشساخته متوسل شود و قرار باشد مقداری را از__add__()برگرداند، این امکان را از دست میدهیم کهB__radd__()هوشمندتری تعریف کرده باشد؛ بنابراین کد پیشساخته بایدNotImplementedرا از__add__()برگرداند. (یا ممکن استAاصلاً__add__()را پیادهسازی نکرده باشد.)سپس نوبت به
__radd__()متعلق بهBمیرسد. اگرaرا بپذیرد، همهچیز درست است.اگر به کد پیشساخته (boilerplate) بازگردد، دیگر هیچ متد ممکنی برای امتحان باقی نمانده است، بنابراین این جایی است که پیادهسازی پیشفرض باید در آن قرار داشته باشد.
اگر
B <: Aباشد، پایتونB.__radd__را پیش ازA.__add__امتحان میکند. این مورد مشکلی ندارد، زیرا با آگاهی ازAپیادهسازی شده است، بنابراین میتواند آن نمونهها را پیش از واگذاری بهComplexمدیریت کند.
اگر A <: Complex و B <: Real باشد و هیچ دانش دیگری بین آنها مشترک نباشد، عملیات مشترک مناسب همان عملیاتی است که شامل complex توکار میشود، و هر دو __radd__() به آن میرسند؛ بنابراین a+b == b+a.
از آنجا که بیشتر عملیات روی هر نوع دادهای بسیار مشابه خواهد بود، تعریف یک تابع کمکی که نمونههای مستقیم و معکوس هر عملگر معین را تولید میکند، میتواند مفید باشد. برای مثال، fractions.Fraction از این روش استفاده میکند:
def _operator_fallbacks(monomorphic_operator, fallback_operator):
def forward(a, b):
if isinstance(b, (int, Fraction)):
return monomorphic_operator(a, b)
elif isinstance(b, float):
return fallback_operator(float(a), b)
elif isinstance(b, complex):
return fallback_operator(complex(a), b)
else:
return NotImplemented
forward.__name__ = '__' + fallback_operator.__name__ + '__'
forward.__doc__ = monomorphic_operator.__doc__
def reverse(b, a):
if isinstance(a, Rational):
# Includes ints.
return monomorphic_operator(a, b)
elif isinstance(a, Real):
return fallback_operator(float(a), float(b))
elif isinstance(a, Complex):
return fallback_operator(complex(a), complex(b))
else:
return NotImplemented
reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
reverse.__doc__ = monomorphic_operator.__doc__
return forward, reverse
def _add(a, b):
"""a + b"""
return Fraction(a.numerator * b.denominator +
b.numerator * a.denominator,
a.denominator * b.denominator)
__add__, __radd__ = _operator_fallbacks(_add, operator.add)
# ...