Konteks
Misalkan saya memiliki kode Python berikut:
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
for _ in range(n_iters):
number = halve(number)
sum_all += number
return sum_all
ns = [1, 3, 12]
print(example_function(ns, 3))
example_functiondi sini hanya melalui masing-masing elemen dalam nsdaftar dan membagi dua 3 kali, sambil mengumpulkan hasilnya. Output dari menjalankan skrip ini adalah:
2.0
Sejak 1 / (2 ^ 3) * (1 + 3 + 12) = 2.
Sekarang, katakanlah (untuk alasan apa pun, mungkin debugging, atau logging), saya ingin menampilkan beberapa jenis informasi tentang langkah-langkah perantara yang example_functionsedang dilakukan. Mungkin saya akan menulis ulang fungsi ini menjadi sesuatu seperti ini:
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
print(number)
sum_all += number
print('sum_all:', sum_all)
return sum_all
yang sekarang, ketika dipanggil dengan argumen yang sama seperti sebelumnya, menampilkan yang berikut:
Processing number 1
0.5
0.25
0.125
sum_all: 0.125
Processing number 3
1.5
0.75
0.375
sum_all: 0.5
Processing number 12
6.0
3.0
1.5
sum_all: 2.0
Ini mencapai apa yang saya maksudkan. Namun, ini sedikit bertentangan dengan prinsip bahwa suatu fungsi seharusnya hanya melakukan satu hal, dan sekarang kode untuk example_functionsligthly lebih panjang dan lebih kompleks. Untuk fungsi sederhana seperti ini, ini bukan masalah, tetapi dalam konteks saya, saya memiliki fungsi yang cukup rumit saling memanggil, dan pernyataan pencetakan sering melibatkan langkah-langkah yang lebih rumit daripada yang ditunjukkan di sini, menghasilkan peningkatan substansial dalam kompleksitas kode saya (untuk satu fungsi saya ada lebih banyak baris kode yang terkait dengan logging daripada ada baris yang terkait dengan tujuan sebenarnya!).
Selain itu, jika nanti saya memutuskan bahwa saya tidak ingin ada lagi pernyataan pencetakan dalam fungsi saya, saya harus melalui example_functiondan menghapus semua printpernyataan secara manual, bersama dengan variabel apa pun yang terkait dengan fungsi ini, suatu proses yang membosankan dan juga salah. -Rentan.
Situasi semakin memburuk jika saya ingin selalu memiliki kemungkinan untuk mencetak atau tidak mencetak selama pelaksanaan fungsi, membuat saya mendeklarasikan dua fungsi yang sangat mirip (satu dengan printpernyataan, satu tanpa), yang mengerikan untuk dijaga, atau untuk mendefinisikan sesuatu seperti:
def example_function(numbers, n_iters, debug_mode=False):
sum_all = 0
for number in numbers:
if debug_mode:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
if debug_mode:
print(number)
sum_all += number
if debug_mode:
print('sum_all:', sum_all)
return sum_all
yang menghasilkan fungsi rumit dan mudah-mudahan tidak perlu, bahkan dalam kasus sederhana kami example_function.
Pertanyaan
Apakah ada cara pythonic untuk "memisahkan" fungsi pencetakan dari fungsi asli example_function?
Secara umum, adakah cara pythonic untuk memisahkan fungsi opsional dari tujuan utama suatu fungsi?
Apa yang saya coba sejauh ini:
Solusi yang saya temukan saat ini adalah menggunakan callback untuk decoupling. Misalnya, seseorang dapat menulis ulang example_functionseperti ini:
def example_function(numbers, n_iters, callback=None):
sum_all = 0
for number in numbers:
for i_iter in range(n_iters):
number = number/2
if callback is not None:
callback(locals())
sum_all += number
return sum_all
dan kemudian mendefinisikan fungsi panggilan balik yang melakukan fungsi pencetakan mana pun yang saya inginkan:
def print_callback(locals):
print(locals['number'])
dan memanggil example_functionseperti ini:
ns = [1, 3, 12]
example_function(ns, 3, callback=print_callback)
yang kemudian menghasilkan:
0.5
0.25
0.125
1.5
0.75
0.375
6.0
3.0
1.5
2.0
Ini berhasil memisahkan fungsi pencetakan dari fungsionalitas dasar example_function. Namun, masalah utama dengan pendekatan ini adalah bahwa fungsi callback hanya dapat dijalankan pada bagian tertentu dari example_function(dalam hal ini tepat setelah mengurangi separuh nomor saat ini), dan semua pencetakan harus terjadi tepat di sana. Ini kadang-kadang memaksa desain fungsi callback menjadi cukup rumit (dan membuat beberapa perilaku tidak mungkin untuk dicapai).
Sebagai contoh, jika seseorang ingin mencapai jenis pencetakan yang sama persis seperti yang saya lakukan di bagian sebelumnya dari pertanyaan (menunjukkan nomor mana yang sedang diproses, bersama dengan separuh yang sesuai) panggilan balik yang dihasilkan adalah:
def complicated_callback(locals):
i_iter = locals['i_iter']
number = locals['number']
if i_iter == 0:
print('Processing number', number*2)
print(number)
if i_iter == locals['n_iters']-1:
print('sum_all:', locals['sum_all']+number)
yang menghasilkan output yang sama persis seperti sebelumnya:
Processing number 1.0
0.5
0.25
0.125
sum_all: 0.125
Processing number 3.0
1.5
0.75
0.375
sum_all: 0.5
Processing number 12.0
6.0
3.0
1.5
sum_all: 2.0
tetapi sulit untuk menulis, membaca, dan men-debug.
loggingmodul akan membantu di sini. Meskipun pertanyaan saya menggunakan printpernyataan ketika mengatur konteks, saya sebenarnya mencari solusi bagaimana memisahkan segala jenis fungsi opsional dari tujuan utama suatu fungsi. Misalnya, mungkin saya ingin fungsi untuk merencanakan berbagai hal saat berjalan. Dalam hal ini saya percaya bahwa loggingmodul itu bahkan tidak akan berlaku.
loggingmenunjukkan), tetapi tidak bagaimana memisahkan kode arbitrer.
loggingmodul python