บทความนี้เผยแพร่โดยเป็นส่วนหนึ่งของไฟล์ Blogathon วิทยาศาสตร์ข้อมูล
บทนำ
Python Standard Library มีโมดูลที่ยอดเยี่ยมมากมายที่จะช่วยให้โค้ดของคุณสะอาดขึ้นและเรียบง่ายขึ้นและเครื่องมือ functools นั้น แน่นอนหนึ่งใน
แคช
มาเริ่มกันที่ฟังก์ชันที่เรียบง่ายแต่ทรงพลังของโมดูล functools เริ่มต้นด้วยฟังก์ชั่นแคช (เช่นเดียวกับมัณฑนากร) – lru_cache
,cache
และ cached_property
. คนแรกของพวกเขา - lru_cache
ให้แคชของผลลัพธ์สุดท้ายของการทำงานของฟังก์ชั่นหรือกล่าวอีกนัยหนึ่งคือจดจำผลงานของพวกเขา:
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}
ในตัวอย่างนี้ เราส่งคำขอ GET และแคชผลลัพธ์ (สูงสุด 32 ผลลัพธ์) โดยใช้มัณฑนากร @lru_cache
. หากต้องการดูว่าการแคชใช้งานได้จริงหรือไม่ คุณสามารถตรวจสอบข้อมูลแคชของฟังก์ชันได้โดยใช้เมธอด cache_info
ที่แสดงจำนวนแคชและ Hit มัณฑนากรยังมีวิธีการ clear_cache
และ cache_parameters
สำหรับการยกเลิกผลลัพธ์แคชและพารามิเตอร์การทดสอบตามลำดับ
หากคุณต้องการแคชที่ละเอียดยิ่งขึ้น คุณสามารถรวมอาร์กิวเมนต์ทางเลือก typed=true ซึ่งช่วยให้คุณแคชอาร์กิวเมนต์ประเภทต่างๆ แยกกันได้
มัณฑนากรอีกตัวหนึ่งสำหรับการแคชใน functools คือฟังก์ชันที่เรียกว่าเพียง cache
. เป็นกระดาษห่อธรรมดา lru_cache
ที่ละเว้นอาร์กิวเมนต์ max_size ลดลง และไม่ลบค่าเก่า
มัณฑนากรอื่นที่คุณสามารถใช้สำหรับแคชคือ cached_property
. ตามชื่อที่แนะนำ มันถูกใช้เพื่อแคชผลลัพธ์ของแอตทริบิวต์คลาส ช่างนี้มีประโยชน์มากหากคุณมีคุณสมบัติที่มีราคาแพงในการคำนวณ แต่ยังคงเหมือนเดิม
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
ตัวอย่างง่ายๆนี้แสดงให้เห็น ตามที่สามารถใช้ได้ คุณสมบัติแคช ตัวอย่างเช่น เพื่อแคชหน้า HTML ที่แสดงผลซึ่งจำเป็นต้องแสดงให้ผู้ใช้เห็นซ้ำแล้วซ้ำอีก สามารถทำได้เช่นเดียวกันสำหรับการสืบค้นฐานข้อมูลบางรายการหรือการคำนวณทางคณิตศาสตร์ที่ยาวนาน
สวยไปอีกแบบ cached_property
คือมันทำงานเฉพาะในการค้นหา ดังนั้นจึงทำให้เราสามารถเปลี่ยนแปลงค่าของแอตทริบิวต์ได้ หลังจากเปลี่ยนแอตทริบิวต์ ค่าที่แคชไว้ก่อนหน้านี้จะไม่เปลี่ยนแปลง แต่ค่าใหม่จะถูกคำนวณและแคชแทน คุณยังสามารถล้างแคชได้ และสิ่งที่คุณต้องทำคือลบแอตทริบิวต์
ฉันต้องการจบส่วนนี้โดยมีข้อแม้เกี่ยวกับมัณฑนากรด้านบนทั้งหมด อย่าใช้หากฟังก์ชันของคุณมีผลข้างเคียงหรือถ้ามันสร้างวัตถุที่เปลี่ยนแปลงได้ทุกครั้งที่มีการเรียกใช้ เนื่องจากสิ่งเหล่านี้ไม่ใช่ฟังก์ชันที่คุณต้องการแคชอย่างชัดเจน .
การเปรียบเทียบและการสั่งซื้อ
คุณอาจรู้อยู่แล้วว่าคุณสามารถใช้ตัวดำเนินการเปรียบเทียบใน Python เช่น <
, >=
or ==
มี lt
, gt
or eq
. อย่างไรก็ตาม มันค่อนข้างน่าหงุดหงิดที่จะตระหนักถึงแต่ละ eq
, lt
, le
, gt
or ge
. โชคดีที่ functools มีมัณฑนากร @total_ordering
ที่สามารถช่วยเราได้ เพราะสิ่งที่เราต้องทำคือ eq
หนึ่งในวิธีที่เหลือและมัณฑนากรที่เหลือจะถูกสร้างขึ้นโดยอัตโนมัติ
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
ด้วยวิธีนี้ เราสามารถใช้การดำเนินการเปรียบเทียบแบบขยายทั้งหมดได้ แม้ว่าจะมีเพียง eq และด้วยมือ lt ประโยชน์ที่ชัดเจนที่สุดคือความสะดวก ซึ่งก็คือคุณไม่จำเป็นต้องเขียนวิธีการเพิ่มเติมเหล่านี้ทั้งหมด แต่อาจมีความสำคัญมากกว่าที่จะลดจำนวนโค้ดและความสามารถในการอ่านที่ดีขึ้น
เกินพิกัด
เราทุกคนคงได้รับการสอนว่าไม่มีการโอเวอร์โหลดใน Python แต่จริงๆ แล้วมีวิธีง่ายๆ ในการใช้งานโดยใช้สองฟังก์ชันจาก functools คือ single dispatch และ/หรือ single dispatch method ฟังก์ชันเหล่านี้ช่วยให้เราสามารถนำสิ่งที่เราเรียกว่าอัลกอริธึมการจัดส่งหลายรายการมาใช้ได้ ซึ่งช่วยให้ภาษาโปรแกรมที่พิมพ์แบบไดนามิก เช่น Python สามารถแยกแยะระหว่างประเภทต่างๆ ได้ในขณะใช้งานจริง
เป็นบางส่วน
เราทุกคนทำงานกับไลบรารีหรือเฟรมเวิร์กภายนอกต่างๆ ซึ่งหลายแห่งมีฟังก์ชันและอินเทอร์เฟซที่ต้องการให้เราส่งการเรียกกลับ เช่น สำหรับการดำเนินการแบบอะซิงโครนัสหรือการฟังเหตุการณ์ นี่ไม่ใช่สิ่งใหม่ แต่ถ้าเราต้องส่งข้อโต้แย้งไปพร้อมกับการโทรกลับด้วย นี่คือที่มาใน functools ที่มีประโยชน์.partial
. สามารถใช้ได้ partial
เพื่อตรึงอาร์กิวเมนต์บางส่วน (หรือทั้งหมด) ของฟังก์ชันโดยการสร้างออบเจ็กต์ใหม่ที่มีลายเซ็นฟังก์ชันแบบง่าย สับสน? มาดูตัวอย่างการใช้งานจริงกัน:
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()
รหัสด้านบนแสดงวิธีการใช้งาน partial
เพื่อส่งผ่านฟังก์ชัน ( output_result
) พร้อมกับอาร์กิวเมนต์ ( log=logger
) เป็นการโทรกลับ ในกรณีนี้ เราจะใช้ multiprocessing.apply_async ซึ่งคำนวณผลลัพธ์ของฟังก์ชันแบบอะซิงโครนัส ( concat
) และส่งคืนผลลัพธ์ของการโทรกลับ อย่างไรก็ตาม, apply_async
มันจะส่งผ่านผลลัพธ์เป็นอาร์กิวเมนต์แรกเสมอ และถ้าเราต้องการรวมอาร์กิวเมนต์เพิ่มเติม เช่นกรณีของ log=logger เราจำเป็นต้องใช้บางส่วน
เราได้พิจารณากรณีการใช้งานที่ค่อนข้างสูงแล้ว และตัวอย่างที่ง่ายกว่าคือการสร้างฟังก์ชันตามปกติที่เขียนด้วย stderr แทนที่จะเป็น stdout:
import sys
from functools import partial print_stderr = partial(print, file=sys.stderr)
print_stderr("This goes to standard error output")
ด้วยเคล็ดลับง่ายๆ นี้ เราได้สร้างฟังก์ชัน callable ใหม่ที่จะผ่านเสมอ file=sys.stderr
เป็นอาร์กิวเมนต์ที่มีชื่อสำหรับเอาต์พุต ซึ่งช่วยให้เราลดความซับซ้อนของโค้ดของเราและไม่ต้องระบุค่าของอาร์กิวเมนต์ที่มีชื่อทุกครั้ง
และตัวอย่างที่ดีประการสุดท้าย เราสามารถใช้ partial
ร่วมกับฟังก์ชันที่ไม่ค่อยมีใครรู้จัก iter
เพื่อสร้างตัววนซ้ำโดยส่งผ่านวัตถุที่เรียกได้และ sentinel
in iter
ซึ่งสามารถนำไปใช้ได้ดังนี้
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...
โดยปกติ เมื่ออ่านไฟล์ เราต้องการวนซ้ำบนบรรทัด แต่ในกรณีของข้อมูลไบนารี เราอาจต้องวนซ้ำระเบียนที่มีขนาดคงที่ คุณสามารถทำได้โดยการสร้างวัตถุที่เรียกได้โดยใช้ partial
ที่อ่านกลุ่มข้อมูลที่ระบุและส่งผ่านใน iter
เพื่อสร้างตัววนซ้ำ ตัววนซ้ำนี้จะเรียกใช้ฟังก์ชันการอ่านจนกว่าจะถึงจุดสิ้นสุดของไฟล์ โดยจะใช้เฉพาะขนาดก้อนที่ระบุเท่านั้น ( RECORD_SIZE
). สุดท้าย เมื่อถึงจุดสิ้นสุดของไฟล์ ค่า sentinel
(NS ") ถูกส่งกลับและการวนซ้ำหยุด
ตกแต่ง
เราได้พูดคุยเกี่ยวกับนักตกแต่งบางส่วนแล้วในส่วนก่อนหน้า แต่ไม่ใช่นักตกแต่งเพื่อสร้างนักตกแต่งเพิ่มเติม มัณฑนากรคนหนึ่งคือ functools.wraps
. เพื่อทำความเข้าใจว่าทำไมคุณถึงต้องการมัน มาดูตัวอย่างกัน:
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
ตัวอย่างนี้แสดงให้เห็นว่ามัณฑนากรแบบง่ายสามารถนำไปใช้ได้อย่างไร เราห่อฟังก์ชันที่ทำงานเฉพาะ ( actual_func
) กับมัณฑนากรภายนอก และกลายเป็นมัณฑนากรเอง ซึ่งสามารถนำไปใช้กับฟังก์ชันอื่นได้ เช่น กรณีที่มี greet
. เมื่อคุณเรียกใช้ฟังก์ชัน greet
คุณจะเห็นว่ามันพิมพ์ข้อความทั้งจาก actual_func
และด้วยตัวของมันเอง ดูไม่เป็นไรใช่ไหม แต่จะเกิดอะไรขึ้นถ้าเราทำสิ่งนี้:
print(greet.__name__)
# actual_func
print(greet.__doc__)
# Inner function within decorator, which does the actual work
เมื่อเราเรียกชื่อและเอกสารประกอบของฟังก์ชั่นการตกแต่ง เราตระหนักว่ามันถูกแทนที่ด้วยค่าจากฟังก์ชั่นมัณฑนากร สิ่งนี้ไม่ดีเพราะเราไม่สามารถเขียนชื่อฟังก์ชันและเอกสารทั้งหมดของเราใหม่เมื่อเราใช้มัณฑนากรบางตัว ปัญหานี้สามารถแก้ไขได้อย่างไร? แน่นอนด้วย functools.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
จุดประสงค์เดียวของฟังก์ชัน wraps
คือการคัดลอกชื่อ เอกสาร รายการอาร์กิวเมนต์ ฯลฯ เพื่อป้องกันการเขียนทับ พิจารณาว่า wraps
นอกจากนี้ยังเป็นมัณฑนากรคุณสามารถเพิ่มลงใน actual_func ของเราและปัญหาได้รับการแก้ไข!
ลด
สุดท้ายแต่ไม่ท้ายสุดในโมดูล functools คือการลดลงนี้ บางทีจากภาษาอื่น ๆ คุณอาจจะรู้ว่ามันเป็น fold
(ฮาสเคล). ฟังก์ชันนี้ใช้ iterable และพับ (เพิ่ม) ค่าทั้งหมดให้เป็นค่าเดียว มีแอปพลิเคชันมากมายสำหรับสิ่งนี้ ตัวอย่างเช่น:
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
ดังที่คุณเห็นจากรหัส reduce
สามารถลดความซับซ้อนหรือย่อโค้ดให้เป็นบรรทัดเดียวได้ มิฉะนั้นอาจยาวกว่านั้นมาก จากที่กล่าวมา มักจะเป็นความคิดที่ไม่ดีที่จะใช้ฟังก์ชันนี้ในทางที่ผิดเพียงเพื่อลดขนาดโค้ด ทำให้ "ฉลาดขึ้น" เนื่องจากจะกลายเป็นเรื่องที่น่ากลัวและอ่านไม่ได้อย่างรวดเร็ว ด้วยเหตุนี้ในความคิดของฉันจึงควรใช้เท่าที่จำเป็น
และถ้าคุณจำได้ว่ามัน reduce
มักจะย่อทุกอย่างให้เหลือบรรทัดเดียวก็สามารถนำมารวมกันได้อย่างลงตัว partial
:
product = partial(reduce, operator.mul) print(product([1, 2, 3]))
# 6
และสุดท้าย ถ้าคุณต้องการมากกว่าแค่ผลลัพธ์ "ยุบ" สุดท้าย คุณสามารถใช้ accumulate
– จากโมดูลที่ยอดเยี่ยมอื่น itertools
. คำนวณค่าสูงสุดได้ดังนี้
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]
สรุป
อย่างที่คุณเห็น functools มีฟังก์ชันที่มีประโยชน์มากมายและตัวตกแต่งที่สามารถทำให้ชีวิตของคุณง่ายขึ้น แต่นี่เป็นเพียงส่วนเล็กๆ ของภูเขาน้ำแข็ง อย่างที่ฉันบอกไปในตอนต้น มีฟังก์ชันมากมายในไลบรารีมาตรฐาน Python ที่ช่วยให้คุณเขียนโค้ดได้ดีขึ้น ดังนั้นนอกเหนือจากฟังก์ชันที่เรากล่าวถึงในที่นี้ คุณยังสามารถให้ความสนใจกับโมดูลอื่นๆ เช่น operator
or itertool.
สำหรับข้อสงสัยใด ๆ คุณสามารถกดช่องแสดงความคิดเห็นของฉัน ฉันจะพยายามอย่างเต็มที่เพื่อแก้ปัญหาอินพุตของคุณและหวังว่าจะได้ผลลัพธ์ที่คุณต้องการ คุณสามารถติดต่อฉันได้ทาง LinkedIn:-
https://www.linkedin.com/in/shivani-sharma-aba6141b6/
สื่อที่แสดงในบทความนี้ไม่ใช่ของ Analytics Vidhya และใช้ดุลยพินิจของผู้เขียน
ที่เกี่ยวข้อง
- '
- "
- 9
- เพิ่มเติม
- ขั้นตอนวิธี
- ทั้งหมด
- การวิเคราะห์
- การใช้งาน
- ข้อโต้แย้ง
- บทความ
- ร้านเสริมสวยเกาหลี
- ที่ดีที่สุด
- กล่อง
- โทรศัพท์
- เปลี่ยนแปลง
- รหัส
- คำนวณ
- การสร้าง
- ข้อมูล
- ฐานข้อมูล
- ส่งไป
- ฯลฯ
- เหตุการณ์
- การปฏิบัติ
- ในที่สุด
- ชื่อจริง
- แข็ง
- ฟังก์ชัน
- ดี
- ยิ่งใหญ่
- มีประโยชน์
- โปรดคลิกที่นี่เพื่ออ่านรายละเอียดเพิ่มเติม
- สรุป ความน่าเชื่อถือของ Olymp Trade?
- HTTPS
- ความคิด
- ข้อมูล
- IT
- การเก็บรักษา
- ภาษา
- ห้องสมุด
- Line
- รายการ
- การฟัง
- นาน
- ค้นหา
- การทำ
- คณิตศาสตร์
- ภาพบรรยากาศ
- คือ
- ชื่อ
- ตัวเลข
- ถูก
- การดำเนินการ
- ความคิดเห็น
- อื่นๆ
- ชำระ
- สระ
- อำนาจ
- การเขียนโปรแกรม
- การเขียนโปรแกรมภาษา
- คุณสมบัติ
- หลาม
- การอ่าน
- บันทึก
- ลด
- REST
- ผลสอบ
- รับคืน
- วิทยาศาสตร์
- ง่าย
- ขนาด
- So
- แก้
- เริ่มต้น
- ทดสอบ
- เวลา
- us
- ความคุ้มค่า
- ภายใน
- คำ
- งาน
- โรงงาน
- โลก
- X