
Hai Saya menjalankan feed @pythonetc dengan tips tentang Python pada khususnya dan pemrograman pada umumnya. Mulai bulan ini, kami meluncurkan serangkaian pilihan dengan posting terbaik bulan ini yang diterjemahkan ke dalam bahasa Rusia.
Transfer data berantai panggilan
Saat Anda ingin mentransfer beberapa informasi di sepanjang rantai panggilan, Anda biasanya menggunakan cara paling sederhana: meneruskan data dalam bentuk argumen ke fungsi.
Tetapi kadang-kadang sangat tidak nyaman untuk mengubah semua fungsi dalam rantai, hanya untuk mentransfer data baru. Dalam kasus seperti itu, lebih baik membuat semacam konteks yang akan digunakan fungsi. Bagaimana cara melakukannya?
Solusi paling sederhana adalah variabel global. Dalam Python, Anda dapat menggunakan modul dan kelas sebagai penjaga konteks, karena, sebenarnya, mereka juga variabel global. Mungkin Anda melakukan ini dari waktu ke waktu, katakanlah, ketika Anda membuat penebang.
Jika Anda memiliki aplikasi multi-utas, maka variabel global biasa tidak akan membantu, karena mereka tidak aman utas. Pada saat yang sama, beberapa rantai panggilan dapat dieksekusi, dan masing-masing akan membutuhkan konteksnya sendiri. Modul threading
menyediakan objek threading.local()
aman-thread. Simpan data apa pun di dalamnya, cukup merujuk ke atribut: threading.local().symbol = '@'
.
Namun, kedua pendekatan ini tidak aman-konkurensi, yaitu, mereka tidak akan bekerja dalam rantai panggilan coroutine di mana coroutine mungkin tidak memanggil coroutine lain, tetapi menunggu mereka. Jika coroutine dalam keadaan siaga, loop acara dapat memicu coroutine lain dari rantai yang berbeda. Opsi ini tidak akan berfungsi:
import asyncio import sys global_symbol = '.' async def indication(timeout): while True: print(global_symbol, end='') sys.stdout.flush() await asyncio.sleep(timeout) async def sleep(t, indication_t, symbol='.'): loop = asyncio.get_event_loop() global global_symbol global_symbol = symbol loop.create_task(indication(indication_t)) await asyncio.sleep(t) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather( sleep(1, 0.1, '0'), sleep(1, 0.1, 'a'), sleep(1, 0.1, 'b'), sleep(1, 0.1, 'c'), ))
Anda dapat memecahkan masalah dengan memaksa loop acara untuk menyimpan dan mengembalikan konteks setiap kali Anda kembali ke coroutine. Inilah aiotask_context
modul aiotask_context
, yang, menggunakan loop.set_task_factory
mengubah cara Anda membuat objek tugas. Opsi ini akan berfungsi:
import asyncio import sys import aiotask_context as context async def indication(timeout): while True: print(context.get('symbol'), end='') sys.stdout.flush() await asyncio.sleep(timeout) async def sleep(t, indication_t, symbol='.'): loop = asyncio.get_event_loop() context.set(key='symbol', value=symbol) loop.create_task(indication(indication_t)) await asyncio.sleep(t) loop = asyncio.get_event_loop() loop.set_task_factory(context.task_factory) loop.run_until_complete(asyncio.gather( sleep(1, 0.1, '0'), sleep(1, 0.1, 'a'), sleep(1, 0.1, 'b'), sleep(1, 0.1, 'c'), ))
Buat SVG
SVG adalah format grafik vektor yang menyimpan informasi gambar dalam bentuk semua bentuk dan angka yang diperlukan untuk rendering dalam XML. Misalnya, lingkaran oranye dapat direpresentasikan sebagai berikut:
<svg xmlns="http://www.w3.org/2000/svg"> <circle cx="125" cy="125" r="75" fill="orange"/> </svg>
Karena SVG adalah bagian dari XML, cukup mudah untuk membuat file SVG dalam bahasa apa pun. Termasuk dalam Python, misalnya, menggunakan lxml. Tetapi ada juga modul svgwrite, dibuat hanya untuk membuat SVG.
Berikut adalah contoh bagaimana Anda dapat menampilkan urutan Recamann dalam bentuk diagram yang Anda lihat di awal artikel.
Akses ke lingkup eksternal
Saat Anda menggunakan variabel dengan Python, pertama-tama akan mencari variabel tersebut dalam lingkup saat ini. Jika tidak menemukannya, maka ia sudah mencari di wilayah satu tingkat lebih tinggi. Dan seterusnya, hingga mencapai namespace global.
x = 1 def scope(): x = 2 def inner_scope(): print(x)
Tetapi tugas variabel bekerja secara berbeda. Variabel baru selalu dibuat dalam lingkup saat ini, kecuali global
atau nonlocal
:
x = 1 def scope(): x = 2 def inner_scope(): x = 3 print(x)
global
memungkinkan Anda untuk menggunakan variabel namespace global, dan dalam kasus nonlocal
Python mencari variabel dalam konteks langsung di sekitarnya. Bandingkan:
x = 1 def scope(): x = 2 def inner_scope(): global x x = 3 print(x)
Eksekusi skrip
python
mendukung beberapa cara untuk menjalankan skrip. Perintah python foo.py
hanya mengeksekusi foo.py
Anda juga dapat menggunakan konstruksi python -m foo
. Jika foo
bukan paket, maka sistem akan menemukan foo.py
di sys.path
dan jalankan. Jika ya, Python akan menjalankan foo/__init__.py
, dan kemudian foo/__main__.py
. Harap dicatat bahwa variabel __name__
membutuhkan __name__
saat runtime __init__.py
dan __main__.py
saat runtime __main__
.
Anda juga dapat menggunakan bentuk python dir/
atau bahkan python dir.zip
. Kemudian python
akan mencari dir/__main__.py
, dan jika itu dir/__main__.py
, ia akan mengeksekusi.
$ ls foo __init__.py __main__.py $ cat foo/__init__.py print(__name__) $ cat foo/__main__.py print(__name__) $ python -m foo foo __main__ $ python foo/ __main__ $ python foo/__init__.py __main__
Jumlah detik sejak awal suatu zaman
Sebelum Python 3.3, sulit untuk mengkonversi objek datetime
ke jumlah detik sejak awal era Unix.
Cara paling logis adalah dengan menggunakan metode strftime
, yang dapat memformat datetime
. Dengan mengambil %s
sebagai format, Anda bisa mendapatkan cap waktu.
naive_time = datetime(2018, 3, 31, 12, 0, 0) utc_time = pytz.utc.localize(naive_time) ny_time = utc_time.astimezone( pytz.timezone('US/Eastern'))
ny_time
persis sama dengan utc_time
, tetapi direkam dalam bentuk yang biasa di New York:
# utc_time datetime.datetime(2018, 3, 31, 12, 0, tzinfo=<UTC>) # utc_time datetime.datetime(2018, 3, 31, 8, 0, tzinfo=<DstTzInfo 'US/Eastern' ...>)
Jika waktunya sama, maka cap waktu harus sama:
In : int(utc_time.strftime('%s')), int(ny_time.strftime('%s')) Out: (1522486800, 1522468800)
Uh, apa? Mengapa mereka berbeda? Faktanya adalah Anda tidak dapat menggunakan strftime
untuk menyelesaikan masalah ini. Dalam Python, strftime
tidak mendukung %s
sebagai argumen sama sekali, dan ini hanya berfungsi karena fungsi strftime()
dari platform C library disebut inside. Tapi, seperti yang Anda lihat, zona datetime
objek datetime
diabaikan sepenuhnya.
Hasil yang benar dapat diperoleh dengan pengurangan sederhana:
In : epoch_start = pytz.utc.localize( datetime(1970, 1, 1)) In : (utc_time - epoch_start).total_seconds() Out: 1522497600.0 In : (utc_time - epoch_start).total_seconds() Out: 1522497600.0
Dan jika Anda menggunakan Python 3.3+, maka Anda bisa menyelesaikan masalah menggunakan metode timestamp
dari kelas datetime
: utc_time.timestamp()
.