هذا هو الاختيار التاسع لنصائح Python والبرمجة من خلاصتيpythonetc.
التحديدات السابقة .
المقارنة الهيكلية
في بعض الأحيان عند الاختبار ، من الضروري مقارنة الهياكل المعقدة ، وتجاهل بعض القيم. يمكن القيام بذلك عادة عن طريق مقارنة قيم محددة من مثل هذا الهيكل:
>>> d = dict(a=1, b=2, c=3) >>> assert d['a'] == 1 >>> assert d['c'] == 3
ومع ذلك ، يمكنك إنشاء قيمة خاصة تساوي أي قيمة أخرى:
>>> assert d == dict(a=1, b=ANY, c=3)
يتم ذلك بسهولة باستخدام طريقة السحر
__eq__
:
>>> class AnyClass: ... def __eq__(self, another): ... return True ... >>> ANY = AnyClass()
المعياري
sys.stdout عبارة عن مجمّع يسمح لك بكتابة قيم السلسلة وليس البايتات. يتم ترميز قيم السلسلة هذه تلقائيًا باستخدام
sys.stdout.encoding
:
>>> sys.stdout.write('Straße\n') Straße >>> sys.stdout.encoding 'UTF-8' sys.stdout.encoding
للقراءة فقط ويساوي الترميز الافتراضي ، والذي يمكن تهيئته باستخدام
PYTHONIOENCODING
البيئة
PYTHONIOENCODING
:
$ PYTHONIOENCODING=cp1251 python3 Python 3.6.6 (default, Aug 13 2018, 18:24:23) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.stdout.encoding 'cp1251'
إذا كنت ترغب في كتابة وحدات البايت إلى
stdout
، فيمكنك تخطي التشفير التلقائي باستخدام
sys.stdout.buffer
المؤقت الموجود في برنامج التضمين:
>>> sys.stdout <_io.TextIOWrapper name='<stdut>' mode='w' encoding='cp1251'> >>> sys.stdout.buffer <_io.BufferedWriter name='<stdut>'> >>> sys.stdout.buffer.write(b'Stra\xc3\x9fe\n') Straße sys.stdout.buffer
أيضا غلاف. يمكن تجاوزه عن طريق الاتصال مع واصف الملف مع
sys.stdout.buffer.raw
:
>>> sys.stdout.buffer.raw.write(b'Stra\xc3\x9fe') Straße
الحذف المستمر
بيثون لديها عدد قليل جدا من الثوابت المضمنة. واحد منهم ،
Ellipsis
، يمكن كتابته أيضًا كـ
...
بالنسبة للمترجم الفوري ، لا يحتوي هذا الثابت على أي قيمة محددة ، ولكن يتم استخدامه عندما يكون بناء الجملة مناسبًا.
numpy
يدعم
Ellipsis
كوسيطة إلى
__getitem__
، على سبيل المثال ، ترجع
x[...]
جميع عناصر
x
.
تعرّف PEP 484 قيمة أخرى لهذا الثابت: تتيح لك
Callable[..., type]
تحديد أنواع المستدعي دون تحديد أنواع الوسائط.
أخيرًا ، يمكنك استخدام
...
للإشارة إلى أن الوظيفة لم يتم تنفيذها بعد. هذا صحيح تماما رمز بيثون:
def x(): ...
ومع ذلك ، في Python 2
Ellipsis
لا يمكن كتابته كـ
...
الاستثناء الوحيد هو
a[...]
، والذي يتم تفسيره على أنه
a[Ellipsis]
.
بناء الجملة هذا صحيح لبيثون 3 ، ولكن السطر الأول هو الصحيح لبيثون 2 فقط:
a[...] a[...:2:...] [..., ...] {...:...} a = ... ... is ... def a(x=...): ...
إعادة استيراد وحدات
لن يتم تحميل الوحدات النمطية المستوردة بالفعل مرة أخرى. أمر
import foo
ببساطة لا يفعل شيئًا. ومع ذلك ، من المفيد إعادة استيراد الوحدات النمطية عند العمل في بيئة تفاعلية. في Python 3.4+ ، يجب استخدام importlib لهذا:
In [1]: import importlib In [2]: with open('foo.py', 'w') as f: ...: f.write('a = 1') ...: In [3]: import foo In [4]: foo.a Out[4]: 1 In [5]: with open('foo.py', 'w') as f: ...: f.write('a = 2') ...: In [6]: foo.a Out[6]: 1 In [7]: import foo In [8]: foo.a Out[8]: 1 In [9]: importlib.reload(foo) Out[9]: <module 'foo' from '/home/v.pushtaev/foo.py'> In [10]: foo.a Out[10]: 2
يوجد أيضًا ملحق
ipython
التلقائي لـ
ipython
، والذي سيقوم تلقائيًا
ipython
استيراد الوحدات النمطية إذا لزم الأمر:
In [1]: %load_ext autoreload In [2]: %autoreload 2 In [3]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=1') ...: In [4]: import foo LOADED In [5]: foo.a Out[5]: 1 In [6]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=2') ...: In [7]: import foo LOADED In [8]: foo.a Out[8]: 2 In [9]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=3') ...: In [10]: foo.a LOADED Out[10]: 3
\ ز
في بعض اللغات ، يمكنك استخدام تعبير
\G
يبحث عن تطابق من الموضع الذي انتهى به البحث السابق. هذا يسمح لنا بكتابة آلات الحالة المحدودة التي تعالج قيم السلسلة كلمة كلمة (يتم تحديد الكلمة بالتعبير العادي).
في Python ، لا يوجد شيء مثل هذا التعبير ، ويمكنك تنفيذ وظيفة مماثلة من خلال تتبع الموضع ونقل جزء من السلسلة يدويًا إلى وظائف التعبير العادية:
import re import json text = '<a><b>foo</b><c>bar</c></a><z>bar</z>' regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data print(json.dumps(tree, indent=4))
في المثال أعلاه ، يمكنك توفير وقت المعالجة دون كسر الخط مرارًا وتكرارًا ، واطلب من الوحدة النمطية البدء في البحث من موضع آخر.
للقيام بذلك ، تحتاج إلى إجراء بعض التغييرات على الكود. أولاً ، لا يدعم
re.search
تحديد موضع بداية البحث ، لذلك يجب عليك ترجمة التعبير العادي يدويًا. ثانياً ، تشير
^
إلى بداية قيمة السلسلة ، وليس موضع بداية البحث ، لذلك تحتاج إلى التحقق يدويًا من أن المطابقة موجودة في نفس الموضع.
import re import json text = '<a><b>foo</b><c>bar</c></a><z>bar</z>' * 10 def print_tree(tree): print(json.dumps(tree, indent=4)) def xml_to_tree_slow(text): regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data def xml_to_tree_slow(text): regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data return tree _regex = re.compile('(?:<([az]+)>|</([az]+)>|([az]+))') def _error_message(text, pos): return text[pos:] def xml_to_tree_fast(text): stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = _regex.search(text, pos=pos) begin, end = found.span(0) assert begin == pos, _error_message(text, pos) assert found, _error_message(text, pos) pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, _error_message(text, pos) if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data return tree print_tree(xml_to_tree_fast(text))
النتائج:
In [1]: from example import * In [2]: %timeit xml_to_tree_slow(text) 356 µs ± 16.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) In [3]: %timeit xml_to_tree_fast(text) 294 µs ± 6.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
تقريب الأرقام
كتب هذا العنصر بواسطة orsinium ، مؤلف قناة Telegramitgram_channel.round
الدالة
round
رقمًا إلى العدد المحدد من المنازل العشرية.
>>> round(1.2) 1 >>> round(1.8) 2 >>> round(1.228, 1) 1.2
يمكنك أيضًا ضبط دقة التقريب السلبي:
>>> round(413.77, -1) 410.0 >>> round(413.77, -2) 400.0 round
تُرجع قيمة من نفس نوع رقم الإدخال:
>>> type(round(2, 1)) <class 'int'> >>> type(round(2.0, 1)) <class 'float'> >>> type(round(Decimal(2), 1)) <class 'decimal.Decimal'> >>> type(round(Fraction(2), 1)) <class 'fractions.Fraction'>
بالنسبة إلى الفصول الدراسية الخاصة بك ، يمكنك تحديد المعالجة
round
باستخدام طريقة
__round__
:
>>> class Number(int): ... def __round__(self, p=-1000): ... return p ... >>> round(Number(2)) -1000 >>> round(Number(2), -2) -2
هنا يتم تقريب القيم إلى أقرب مضاعفات
10 ** (-precision)
. على سبيل المثال ،
precision=1
سيتم تقريب القيمة إلى مضاعفات 0.1:
round(0.63, 1)
0.6
. إذا كان الرقمان المتقاربان متكافئين ، فسيتم التقريب إلى رقم زوجي:
>>> round(0.5) 0 >>> round(1.5) 2
في بعض الأحيان ، قد يؤدي تقريب رقم الفاصلة العائمة إلى إعطاء نتيجة غير متوقعة:
>>> round(2.85, 1) 2.9
الحقيقة هي أن معظم الكسور العشرية لا يمكن التعبير عنها بدقة باستخدام رقم الفاصلة العائمة (
https://docs.python.org/3.7/tutorial/floatingpoint.html ):
>>> format(2.85, '.64f') '2.8500000000000000888178419700125232338905334472656250000000000000'
إذا كنت تريد
decimal.Decimal
إلى النصف ، فاستخدم
decimal.Decimal
:
>>> from decimal import Decimal, ROUND_HALF_UP >>> Decimal(1.5).quantize(0, ROUND_HALF_UP) Decimal('2') >>> Decimal(2.85).quantize(Decimal('1.0'), ROUND_HALF_UP) Decimal('2.9') >>> Decimal(2.84).quantize(Decimal('1.0'), ROUND_HALF_UP) Decimal('2.8')