Functools -Python'da Yüksek Dereceli Fonksiyonların Gücü

Kaynak Düğüm: 1865357

Bu makale, Veri Bilimi Blogathon

Giriş

Python Standart Kitaplığı, kodunuzu daha temiz ve daha basit tutmanıza yardımcı olacak birçok harika modüle sahiptir ve işlevsel araçlar kesinlikle biri

önbelleğe alma

Modül functools'un en basit ama güçlü işlevlerinden bazılarıyla başlayalım. Önbelleğe alma işlevleriyle (ayrıca dekoratörlerle) başlayalım – lru_cache,cache ve cached_property. Bunlardan ilki - lru_cache işlevlerin yürütülmesinin son sonuçlarının önbelleğini sağlar veya başka bir deyişle çalışmalarının sonucunu hatırlar:

from functools import lru_cache
import requests @lru_cache(maxsize=32)
def get_with_cache(url): try: r = requests.get(url) return r.text except: return "Not Found" for url in ["https://google.com/", "https://reddit.com/", "https://google.com/", "https://google.com/"]: get_with_cache(url) print(get_with_cache.cache_info())
# CacheInfo(hits=2, misses=4, maxsize=32, currsize=4)
print(get_with_cache.cache_parameters())
# {'maxsize': 32, 'typed': False}

Bu örnekte, bir dekoratör kullanarak GET istekleri yapıyoruz ve sonuçlarını (en fazla 32 sonuç) önbelleğe alıyoruz. @lru_cache. Önbelleğe almanın gerçekten işe yarayıp yaramadığını görmek için, bir yöntem kullanarak işlev önbellek bilgilerini kontrol edebilirsiniz. cache_infobu, önbellek isabetlerinin ve isabetlerinin sayısını gösterir. Bir dekoratör ayrıca yöntemler sağlar clear_cacheve cache_parameterssırasıyla önbelleğe alınan sonuçların ve test parametrelerinin iptali için.

Daha ayrıntılı önbelleğe almaya ihtiyacınız varsa, farklı türdeki bağımsız değişkenleri ayrı ayrı önbelleğe almanıza olanak tanıyan isteğe bağlı bir typed=true bağımsız değişkeni ekleyebilirsiniz.

Functools'da önbelleğe alma için başka bir dekoratör, basitçe olarak adlandırılan bir işlevdir. cache. Bu basit bir sarıcı lru_cachebu, max_size bağımsız değişkenini atlar, azaltır ve eski değerleri kaldırmaz.

Önbelleğe almak için kullanabileceğiniz başka bir dekoratör cached_property. Adından da anlaşılacağı gibi, sınıf özniteliklerinin sonuçlarını önbelleğe almak için kullanılır. Bu mekanik, hesaplanması pahalı ama aynı kalan bir mülkünüz varsa çok kullanışlıdır.

from functools import cached_property class Page: @cached_property def render(self, value): # Do something with supplied value... # Long computation that renders HTML page... return html

Bu basit örnek gösterir. Kullanılabileceği gibi, önbelleğe alınmış özellik, örneğin kullanıcıya tekrar tekrar gösterilmesi gereken işlenmiş bir HTML sayfasını önbelleğe almak için. Aynısı, belirli veritabanı sorguları veya uzun matematik hesaplamaları için de yapılabilir.

başka bir güzellik cached_propertyyalnızca aramada çalışmasıdır, bu nedenle özniteliğin değerini değiştirmemize izin verir. Özniteliği değiştirdikten sonra, önceden önbelleğe alınan değer değişmez, bunun yerine yeni değer hesaplanır ve önbelleğe alınır. Önbelleği de temizleyebilirsiniz ve yapmanız gereken tek şey özelliği kaldırmaktır.

Bu bölümü, yukarıdaki dekoratörlerin tümü hakkında bir uyarı ile bitirmek istiyorum - işleviniz bazı yan etkilere sahipse veya her çağrıldığında değişebilir nesneler oluşturuyorsa, bunları kullanmayın, çünkü bunlar açıkça önbelleğe almak istediğiniz işlevler değildir. .

Karşılaştırma ve sipariş

Python'da aşağıdaki gibi karşılaştırma operatörlerini uygulayabileceğinizi muhtemelen zaten biliyorsunuzdur. <, >=or ==Ile lt, gtor eq. Ancak, her birinin farkına varmak oldukça sinir bozucu olabilir. eq, lt, le, gtor ge. Neyse ki, functools'ta bir dekoratör var @total_orderingbu bize bu konuda yardımcı olabilir, çünkü uygulamamız gereken tek şey eqkalan yöntemlerden biri ve dekoratörün geri kalanı otomatik olarak oluşturulacaktır.

from functools import total_ordering @total_ordering
class Number: def __init__(self, value): self.value = value def __lt__(self, other): return self.value Number(3))
# True
print(Number(1) = Number(15))
# True
print(Number(10) <= Number(2))
# False

Bu şekilde, sadece eq ve elle lt olmasına rağmen tüm genişletilmiş karşılaştırma işlemlerini uygulayabiliriz. En belirgin fayda, tüm bu ek sihirli yöntemleri yazmak zorunda kalmamanız, ancak kod miktarını ve daha iyi okunabilirliğini azaltmak muhtemelen daha önemlidir.

Aşırı yük

Muhtemelen hepimize Python'da aşırı yükleme olmadığı öğretilmiştir, ancak bunu functools'un iki işlevini, yani tek gönderme ve/veya tek gönderme yöntemini kullanarak uygulamanın kolay bir yolu vardır. Bu işlevler, Python gibi dinamik olarak yazılan programlama dillerinin çalışma zamanında türleri ayırt etmesine izin veren çoklu gönderme algoritması dediğimiz şeyi uygulamamıza yardımcı olur.

Kısmi

Hepimiz, çoğu zaman uyumsuz işlemler veya olayları dinleme gibi geri aramaları iletmemizi gerektiren işlevler ve arabirimler sağlayan çeşitli dış kitaplıklar veya çerçevelerle çalışıyoruz. Bu yeni bir şey değil, ama ya geri arama ile birlikte bazı argümanları da iletmemiz gerekirse. Kullanışlı functools burada devreye giriyor.partial. Kullanılabilir partialbasitleştirilmiş bir işlev imzasıyla yeni bir nesne oluşturarak bir işleve ilişkin argümanların bir kısmını (veya tamamını) dondurmak. Kafası karışmış? Bazı pratik örneklere bir göz atalım:

def output_result(result, log=None): if log is not None: log.debug(f"Result is: {result}") def concat(a, b): return a + b import logging
from multiprocessing import Pool
from functools import partial logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("default") p = Pool()
p.apply_async(concat, ("Hello ", "World"), callback=partial(output_result, log=logger))
p.close()
p.join()

Yukarıdaki kod, onu nasıl kullanabileceğinizi gösterir. partialbir işlevi geçmek için ( output_result) bir argümanla birlikte ( log=logger) bir geri arama olarak. Bu durumda, işlevin sonucunu eşzamansız olarak hesaplayan multiprocessing.apply_async kullanacağız ( concat) ve geri aramanın sonucunu döndürür. Yine de, apply_asyncsonucu her zaman ilk argüman olarak iletir ve log=logger'da olduğu gibi herhangi bir ek argüman eklemek istiyorsak, kısmi kullanmamız gerekir.

Oldukça gelişmiş bir kullanım durumu düşündük ve daha basit bir örnek, stdout yerine stderr'de yazan bir işlevin olağan oluşturulması olabilir:

import sys
from functools import partial print_stderr = partial(print, file=sys.stderr)
print_stderr("This goes to standard error output")

Bu basit numara ile her zaman geçecek yeni bir çağrılabilir fonksiyon yarattık. file=sys.stderrBu, kodumuzu basitleştirmemize ve her seferinde adlandırılmış bağımsız değişkenin değerini belirtmemize gerek kalmadan çıktıya adlandırılmış bir argüman olarak.

Ve son bir güzel örnek. Kullanabiliriz partialaz bilinen bir işlevle birlikte iterçağrılabilir bir nesneyi ileterek bir yineleyici oluşturmak ve sentinelin iter, bu şekilde uygulanabilir:

from functools import partial RECORD_SIZE = 64 # Read binary file...
with open("file.data", "rb") as file: records = iter(partial(file.read, RECORD_SIZE), b'') for r in records: # Do something with the record...

Genellikle, bir dosyayı okurken satırlar üzerinde yineleme yapmak isteriz, ancak ikili veriler söz konusu olduğunda, sabit boyuttaki kayıtlar üzerinde yineleme yapmamız gerekebilir. Bunu kullanarak çağrılabilir bir nesne oluşturarak yapabilirsiniz. partialbelirtilen veri yığınını okuyan ve onu ileten iterbir yineleyici oluşturmak için. Bu yineleyici daha sonra dosyanın sonuna ulaşana kadar okuma işlevini çağırır ve her zaman yalnızca belirtilen yığın boyutunu alır ( RECORD_SIZE). Son olarak, dosyanın sonuna gelindiğinde değer sentinel(B ") döndürülür ve yineleme durur.

Dekoratörler

Önceki bölümlerde bazı dekoratörlerden bahsetmiştik, ancak daha fazla dekoratör yaratmak için dekoratörlerden bahsetmedik. Böyle bir dekoratör functools.wraps. Neden buna ihtiyacınız olduğunu anlamak için bir örneğe bakalım:

def decorator(func): def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") greet("Martin")
# Before Calling greet
# Hello, Martin!
# After Calling greet

Bu örnek, basit bir dekoratörün nasıl uygulanabileceğini gösterir. Belirli bir görevi yerine getiren bir işlevi sarıyoruz ( actual_func) harici bir dekoratör ile ve daha sonra örneğin olduğu gibi diğer işlevlere uygulanabilen bir dekoratörün kendisi haline gelir. greet. Fonksiyonu çağırdığınızda, greet her ikisinden de mesajları yazdırdığını göreceksiniz. actual_funcve kendi başına. İyi görünüyor, değil mi? Ama bunu yaparsak ne olur:

print(greet.__name__)
# actual_func
print(greet.__doc__)
# Inner function within decorator, which does the actual work

Dekore edilmiş fonksiyonun adını ve dokümantasyonunu çağırdığımızda onların yerine dekoratör fonksiyonundan gelen değerlerle değiştirildiğini fark ederiz. Bazı dekoratörleri kullandığımızda tüm işlev adlarımızı ve belgelerimizi yeniden yazamayacağımız için bu kötü. Bu sorun nasıl çözülebilir? Tabii ki, functools ile.wraps:

from functools import wraps def decorator(func): @wraps(func) def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") print(greet.__name__)
# greet
print(greet.__doc__)
# Says hello to somebody

Fonksiyonun tek amacı wrapsüzerine yazmayı önlemek için adı, belgeleri, argüman listesini vb. kopyalamaktır. Hesaba katıldığında wrapsaynı zamanda bir dekoratördür, onu gerçek_işlevimize ekleyebilirsiniz ve sorun çözülür!

Azaltmak

Modül functools'daki son fakat en az değil, bu azalmadır. Belki diğer dillerden, olarak biliyor olabilirsiniz fold(Haskel). Bu işlev bir yinelenebilir alır ve tüm değerlerini bire katlar (ekler). Bunun için birçok uygulama var, örneğin:

from functools import reduce
import operator def product(iterable): return reduce(operator.mul, iterable, 1) def factorial(n): return reduce(operator.mul, range(1, n)) def sum(numbers): # Use `sum` function from standard library instead return reduce(operator.add, numbers, 1) def reverse(iterable): return reduce(lambda x, y: y+x, iterable) print(product([1, 2, 3]))
# 6
print(factorial(5))
# 24
print(sum([2, 6, 8, 3]))
# 20
print(reverse("hello"))
# olleh

Koddan da görebileceğiniz gibi, reduce kodu basitleştirebilir veya tek bir satıra sıkıştırabilir, aksi takdirde çok daha uzun olurdu. Bununla birlikte, bu işlevi yalnızca kodu küçültmek için kötüye kullanmak genellikle kötü bir fikirdir, bu da onu "daha akıllı" hale getirir, çünkü hızla korkutucu ve okunamaz hale gelir. Bu sebeple bence dikkatli kullanılmalıdır.

Ve eğer bunu hatırlıyorsan reduce genellikle her şeyi bir satıra kısaltır, mükemmel bir şekilde birleştirilebilir partial:

product = partial(reduce, operator.mul) print(product([1, 2, 3]))
# 6

Ve son olarak, yalnızca nihai "daraltılmış" sonuçtan daha fazlasına ihtiyacınız varsa, kullanabilirsiniz. accumulate– başka bir harika modülden itertools. Maksimumu hesaplamak için aşağıdaki gibi kullanılabilir:

from itertools import accumulate data = [3, 4, 1, 3, 5, 6, 9, 0, 1] print(list(accumulate(data, max)))
# [3, 4, 4, 4, 5, 6, 9, 9, 9]

Sonuç

İşlev araçlarını görebileceğiniz gibi, hayatınızı kolaylaştırabilecek birçok kullanışlı işlev ve dekoratör var, ancak bu sadece buzdağının görünen kısmı. Başta söylediğim gibi, Python standart kütüphanesinde daha iyi kod yazmanıza yardımcı olan birçok fonksiyon vardır, bu yüzden burada ele aldığımız fonksiyonlara ek olarak, aşağıdaki gibi diğer modüllere de dikkat edebilirsiniz. operatoror itertool. Herhangi bir sorunuz için yorum kutuma tıklayabilirsiniz. Girdilerinizi çözmek için elimden gelenin en iyisini yapacağım ve size istediğiniz çıktıları vermeyi umuyorum. Bana LinkedIn'den de ulaşabilirsiniz: -

https://www.linkedin.com/in/shivani-sharma-aba6141b6/

Bu makalede gösterilen medya Analytics Vidhya'ya ait değildir ve Yazarın takdirine bağlı olarak kullanılır.

Kaynak: https://www.analyticsvidhya.com/blog/2021/08/functools-the-power-of-higher-order-functions-in-python/

Zaman Damgası:

Den fazla Analitik Vidhya