bağlam
Aşağıdaki Python kodum olduğunu varsayalım:
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_functionburada nslistedeki öğelerin her birini gözden geçirip, sonuçları biriktirirken 3 kez yarıya indiriyoruz. Bu komut dosyasını çalıştırmanın çıktısı basitçe:
2.0
1 / (2 ^ 3) * (1 + 3 + 12) = 2 olduğundan.
Şimdi, diyelim ki (herhangi bir nedenle, belki hata ayıklama veya günlük kaydı), attığı ara adımlar hakkında bir tür bilgi görüntülemek istiyorum example_function. Belki daha sonra bu işlevi böyle bir şeye yeniden yazarım:
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
şimdi, daha önce olduğu gibi aynı argümanlarla çağrıldığında, aşağıdakileri çıkarır:
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
Bu tam olarak istediğim şeyi başarıyor. Bununla birlikte, bu, bir işlevin sadece bir şey yapması gerektiği ilkesine aykırıdır ve şimdi kodu biraz example_functiondaha uzun ve daha karmaşıktır. Böyle basit bir işlev için bu bir sorun değildir, ancak benim bağlamımda birbirimizi çağıran oldukça karmaşık işlevlerim var ve yazdırma ifadeleri genellikle burada gösterilenden daha karmaşık adımlar içeriyor ve bu da kodumun karmaşıklığında önemli bir artışa neden oluyor. fonksiyonlarımın gerçek amacı ile ilgili satırlardan daha fazla günlük kaydı ile ilgili kod satırları vardı!).
Ayrıca, daha sonra artık işlevimde herhangi bir yazdırma ifadesi istemediğime karar verirsem, example_functiontüm bu printifadelerle ilgili tüm değişkenler ile birlikte tüm ifadeleri manuel olarak gözden geçirip silmem gerekir, hem sıkıcı hem de hata olan bir işlem -yatkın.
İşlev yürütme sırasında her zaman yazdırma veya yazdırmama olasılığım olursa durum daha da kötüleşir, bu da beni ya çok benzer iki işlevi (biri printifadeleri olan, biri olmadan) bildirmeye yönlendirir , gibi bir şey tanımlamak için:
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
basit bir durumda bile şişkin ve (umarım) gereksiz yere karmaşık bir fonksiyonla sonuçlanır example_function.
Soru
Baskı işlevselliğini orijinal işlevinden "ayırmak" için bir pythonic yolu var mı example_function?
Daha genel olarak, isteğe bağlı işlevselliği bir işlevin ana amacından ayırmanın pythonic bir yolu var mı?
Şimdiye kadar denedim:
Şu anda bulduğum çözüm, ayırma için geri aramalar kullanıyor. Örneğin, şu şekilde yeniden yazılabilir example_function:
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
ve sonra istediğim yazdırma işlevini gerçekleştiren bir geri arama işlevi tanımlamak için:
def print_callback(locals):
print(locals['number'])
ve şöyle çağırıyor example_function:
ns = [1, 3, 12]
example_function(ns, 3, callback=print_callback)
daha sonra çıktılar:
0.5
0.25
0.125
1.5
0.75
0.375
6.0
3.0
1.5
2.0
Bu, yazdırma işlevini, temel işlevinden başarıyla ayırır example_function. Bununla birlikte, bu yaklaşımla ilgili temel sorun, geri arama işlevinin yalnızca example_function(bu durumda geçerli sayıyı yarıya indirdikten hemen sonra) belirli bir kısmında çalıştırılabilmesi ve tüm baskının tam olarak orada olması gerektiğidir. Bu bazen geri arama işlevinin tasarımını oldukça karmaşık hale getirir (ve bazı davranışların gerçekleştirilmesini imkansız hale getirir).
Örneğin, sorunun önceki bir bölümünde yaptığımla aynı türden bir baskı elde etmek isterseniz (karşılık gelen yarılanmalarla birlikte hangi sayının işlendiğini gösterir) ortaya çıkan geri arama:
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)
bu da öncekiyle tam olarak aynı çıktıyı verir:
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
ama yazmak, okumak ve hata ayıklamak için bir acıdır.
loggingModülün burada nasıl yardımcı olacağını görmüyorum . Her ne kadar sorum printbağlamı ayarlarken ifadeler kullanıyor olsa da , aslında herhangi bir isteğe bağlı işlevsellik işlevinin ana amacından nasıl çözüleceğine dair bir çözüm arıyorum. Örneğin, belki bir işlevi çalışır gibi şeyler çizmek istiyorum. Bu durumda loggingmodülün uygulanamayacağına inanıyorum .
logginggösterdiği gibi) ele alabiliriz, ancak rastgele kodların nasıl ayrılacağını değil.
loggingmodülünü inceleyin