Ini adalah pilihan baru tips dan trik tentang Python dan pemrograman dari saluran-Telegram saya @pythonetc.
Publikasi sebelumnya .
Membandingkan struktur
Terkadang Anda ingin membandingkan struktur yang rumit dalam pengujian dengan mengabaikan beberapa nilai. Biasanya, itu dapat dilakukan dengan membandingkan nilai-nilai tertentu dengan struktur:
>>> d = dict(a=1, b=2, c=3) >>> assert d['a'] == 1 >>> assert d['c'] == 3 
Namun, Anda dapat membuat nilai khusus yang laporannya setara dengan nilai lainnya:
 >>> assert d == dict(a=1, b=ANY, c=3) 
Itu dapat dengan mudah dilakukan dengan mendefinisikan metode 
__eq__ :
 >>> class AnyClass: ... def __eq__(self, another): ... return True ... >>> ANY = AnyClass() 
sys.stdout adalah pembungkus yang memungkinkan Anda untuk menulis string, bukan byte mentah. String dikodekan secara otomatis menggunakan 
sys.stdout.encoding :
 >>> _ = sys.stdout.write('Straße\n') Straße >>> sys.stdout.encoding 'UTF-8' 
sys.stdout.encoding adalah read-only dan sama dengan encoding default Python, yang dapat diubah dengan mengatur variabel lingkungan 
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' 
Jika Anda ingin menulis byte ke 
stdout Anda dapat memotong encoding otomatis dengan mengakses buffer yang dibungkus dengan 
sys.stdout.buffer :
 >>> sys.stdout <_io.TextIOWrapper name='<stdout>' mode='w' encoding='cp1251'> >>> sys.stdout.buffer <_io.BufferedWriter name='<stdout>'> >>> _ = sys.stdout.buffer.write(b'Stra\xc3\x9fe\n') Straße 
sys.stdout.buffer juga merupakan pembungkus yang melakukan buffering untuk Anda. Itu dapat dilewati dengan mengakses file handler mentah dengan 
sys.stdout.buffer.raw :
 >>> _ = sys.stdout.buffer.raw.write(b'Stra\xc3\x9fe') Straße 
Ellipsis konstan
Python memiliki daftar konstanta bawaan yang sangat pendek. Salah satunya adalah 
Ellipsis yang juga bisa ditulis 
... Konstanta ini tidak memiliki arti khusus untuk penerjemah tetapi digunakan di tempat-tempat di mana sintaksis seperti itu terlihat tepat.
numpy mendukung 
Ellipsis sebagai argumen 
__getitem__ , misal 
x[...] mengembalikan semua elemen 
x .
PEP 484 mendefinisikan arti tambahan: 
Callable[..., type] adalah cara untuk mendefinisikan jenis callable tanpa tipe argumen yang ditentukan.
Akhirnya, Anda dapat menggunakan 
... untuk menunjukkan bahwa fungsi tersebut belum diimplementasikan. Ini adalah kode Python yang benar-benar valid:
 def x(): ... 
Namun, dalam Python 2 
Ellipsis tidak dapat ditulis sebagai 
... Satu-satunya pengecualian adalah 
a[...] yang berarti 
a[Ellpsis] .
Semua sintaks berikut ini berlaku untuk Python 3, tetapi hanya baris pertama yang valid untuk Python 2:
 a[...] a[...:2:...] [..., ...] {...:...} a = ... ... is ... def a(x=...): ... 
Modul mengimport ulang
Modul yang sudah diimpor tidak akan dimuat lagi. 
import foo tidak melakukan apa-apa. Namun, terbukti berguna untuk mengimpor kembali modul saat bekerja di lingkungan yang interaktif. Cara yang tepat untuk melakukan ini dalam Python 3.4+ adalah dengan menggunakan 
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 juga memiliki ekstensi 
autoreload yang secara otomatis 
autoreload kembali modul jika perlu:
 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
Dalam beberapa bahasa, Anda dapat menggunakan pernyataan 
\G Ini cocok pada posisi di mana pertandingan sebelumnya berakhir. Itu memungkinkan penulisan automata terbatas yang berjalan melalui kata demi kata string (di mana kata didefinisikan oleh regex).
Namun, tidak ada hal seperti itu di Python. Solusi yang tepat adalah melacak posisi secara manual dan meneruskan fungsi substring ke regex:
 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)) 
Pada contoh sebelumnya, kita dapat menghemat waktu dengan menghindari mengiris string berulang kali tetapi meminta modul ulang untuk mencari mulai dari posisi yang berbeda.
Itu membutuhkan beberapa perubahan. Pertama, 
re.search tidak mendukung pencarian dari posisi khusus, jadi kami harus mengkompilasi ekspresi reguler secara manual. Kedua, 
^ berarti awal sebenarnya untuk string, bukan posisi di mana pencarian dimulai, jadi kami harus memeriksa secara manual bahwa kecocokan terjadi pada posisi yang sama.
 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)) 
Hasil:
 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) 
Fungsi bulat
Posting hari ini ditulis oleh orsinium , penulis @itgram_channel.Fungsi putaran 
round angka ke presisi tertentu dalam digit desimal.
 >>> round(1.2) 1 >>> round(1.8) 2 >>> round(1.228, 1) 1.2 
Anda juga dapat mengatur presisi negatif:
 >>> round(413.77, -1) 410.0 >>> round(413.77, -2) 400.0 
nilai pengembalian 
round tipe nomor input:
 >>> 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'> 
Untuk kelas Anda sendiri, Anda dapat mendefinisikan pemrosesan putaran dengan metode 
__round__ :
 >>> class Number(int): ... def __round__(self, p=-1000): ... return p ... >>> round(Number(2)) -1000 >>> round(Number(2), -2) -2 
Nilai dibulatkan ke kelipatan terdekat dari 
10 ** (-precision) . Misalnya, untuk 
precision=1 nilai akan dibulatkan menjadi kelipatan 0,1: 
round(0.63, 1) mengembalikan 
0.6 . Jika dua kelipatan sama-sama dekat, pembulatan dilakukan menuju pilihan genap:
 >>> round(0.5) 0 >>> round(1.5) 2 
Terkadang pembulatan mengapung bisa sedikit mengejutkan:
 >>> round(2.85, 1) 2.9 
Ini karena sebagian besar pecahan desimal tidak dapat direpresentasikan dengan tepat sebagai pelampung (https://docs.python.org/3.7/tutorial/floatingpoint.html):
 >>> format(2.85, '.64f') '2.8500000000000000888178419700125232338905334472656250000000000000' 
Jika Anda ingin membulatkan setengah Anda dapat menggunakan 
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')