Ini adalah koleksi kesembilan tips Python dan pemrograman dari feed @pythonetc saya.
Pilihan sebelumnya .
Perbandingan Struktural
Kadang-kadang ketika menguji perlu untuk membandingkan struktur yang kompleks, mengabaikan beberapa nilai. Ini biasanya dapat dilakukan dengan membandingkan nilai-nilai spesifik dari struktur seperti itu:
>>> d = dict(a=1, b=2, c=3) >>> assert d['a'] == 1 >>> assert d['c'] == 3
Namun, Anda dapat membuat nilai khusus yang sama dengan yang lain:
>>> assert d == dict(a=1, b=ANY, c=3)
Ini mudah dilakukan dengan menggunakan metode ajaib
__eq__
:
>>> class AnyClass: ... def __eq__(self, another): ... return True ... >>> ANY = AnyClass()
stdout
sys.stdout adalah pembungkus yang memungkinkan Anda untuk menulis nilai string, bukan byte. Nilai-nilai string ini secara otomatis dikodekan menggunakan
sys.stdout.encoding
:
>>> sys.stdout.write('Straße\n') Straße >>> sys.stdout.encoding 'UTF-8' sys.stdout.encoding
read-only dan sama dengan encoding default, yang dapat dikonfigurasi menggunakan
PYTHONIOENCODING
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 melewati penyandian otomatis dengan menggunakan
sys.stdout.buffer
buffer yang ditempatkan di wrapper:
>>> 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
juga bungkusnya. Itu dapat dilewati dengan menghubungi deskriptor file dengan
sys.stdout.buffer.raw
:
>>> sys.stdout.buffer.raw.write(b'Stra\xc3\x9fe') Straße
Elipsis konstan
Python memiliki sedikit konstanta bawaan. Salah satunya,
Ellipsis
, juga bisa ditulis
...
Untuk penerjemah, konstanta ini tidak memiliki nilai spesifik, tetapi digunakan jika sintaksinya sesuai.
numpy
mendukung
Ellipsis
sebagai argumen untuk
__getitem__
, misalnya,
x[...]
mengembalikan semua elemen
x
.
PEP 484 menetapkan nilai lain untuk konstanta ini:
Callable[..., type]
memungkinkan Anda untuk menentukan tipe-tipe yang dipanggil tanpa menentukan tipe argumen.
Akhirnya, Anda dapat menggunakan
...
untuk menunjukkan bahwa suatu fungsi belum diimplementasikan. Ini adalah kode Python yang sepenuhnya benar:
def x(): ...
Namun, dalam Python 2
Ellipsis
tidak dapat ditulis sebagai
...
Satu-satunya pengecualian adalah
a[...]
, yang ditafsirkan sebagai
a[Ellipsis]
.
Sintaks ini benar untuk Python 3, tetapi hanya baris pertama yang benar untuk Python 2:
a[...] a[...:2:...] [..., ...] {...:...} a = ... ... is ... def a(x=...): ...
Impor kembali modul
Modul yang sudah diimpor tidak akan memuat lagi. Perintah
import foo
tidak melakukan apa-apa. Namun, ini berguna untuk menerapkan kembali modul ketika bekerja di lingkungan yang interaktif. Dalam Python 3.4+, importlib harus digunakan untuk ini:
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
Ada juga ekstensi
autoreload
untuk
ipython
, yang secara otomatis akan memasukkan 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 ekspresi
\G
Ia mencari kecocokan dari posisi di mana pencarian sebelumnya berakhir. Ini memungkinkan kita untuk menulis mesin keadaan terbatas yang memproses nilai string kata demi kata (kata ditentukan oleh ekspresi reguler).
Dalam Python, tidak ada yang seperti ekspresi ini, dan Anda dapat menerapkan fungsi serupa dengan secara manual melacak posisi dan meneruskan bagian dari string ke fungsi ekspresi reguler:
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))
Dalam contoh di atas, Anda dapat menghemat waktu pemrosesan tanpa memutus garis berulang-ulang, dan minta modul
re
untuk mulai mencari dari posisi lain.
Untuk melakukan ini, Anda perlu membuat beberapa perubahan pada kode. Pertama,
re.search
tidak mendukung penentuan posisi awal pencarian, jadi Anda harus mengkompilasi ekspresi reguler secara manual. Kedua,
^
menunjukkan awal dari nilai string, bukan posisi awal pencarian, jadi Anda perlu memeriksa secara manual bahwa kecocokan ditemukan di 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)
Angka pembulatan
Item ini ditulis oleh orsinium , penulis saluran Telegram @itgram_channel.Fungsi putaran
round
angka ke jumlah tempat desimal yang ditentukan.
>>> round(1.2) 1 >>> round(1.8) 2 >>> round(1.228, 1) 1.2
Anda juga dapat mengatur akurasi pembulatan negatif:
>>> round(413.77, -1) 410.0 >>> round(413.77, -2) 400.0 round
mengembalikan nilai dari jenis yang sama dengan 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
round
menggunakan metode
__round__
:
>>> class Number(int): ... def __round__(self, p=-1000): ... return p ... >>> round(Number(2)) -1000 >>> round(Number(2), -2) -2
Di sini nilai dibulatkan ke kelipatan terdekat dari
10 ** (-precision)
. Misalnya, dengan
precision=1
nilai akan dibulatkan ke kelipatan 0,1:
round(0.63, 1)
mengembalikan
0.6
. Jika dua bilangan ganda sama-sama dekat, maka pembulatan dilakukan ke bilangan genap:
>>> round(0.5) 0 >>> round(1.5) 2
Terkadang membulatkan angka floating point dapat memberikan hasil yang tidak terduga:
>>> round(2.85, 1) 2.9
Faktanya adalah bahwa sebagian besar pecahan desimal tidak dapat diekspresikan secara akurat menggunakan angka floating point (
https://docs.python.org/3.7/tutorial/floatingpoint.html ):
>>> format(2.85, '.64f') '2.8500000000000000888178419700125232338905334472656250000000000000'
Jika Anda ingin
decimal.Decimal
dua, gunakan
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')