Get started learning Python with DataCamp's free Intro to Python tutorial. Learn Data Science by completing interactive coding challenges and watching videos by expert instructors. Start Now!
This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join 11 million other learners and get started learning Python for data science today!
Good news! You can save 25% off your Datacamp annual subscription with the code LEARNPYTHON23ALE25 - Click here to redeem your discount
แผนที่, กรอง, ลด
Map, Filter, and Reduce เป็นพาราไดม์ของการเขียนโปรแกรมเชิงฟังก์ชัน พวกเขาช่วยให้โปรแกรมเมอร์ (คุณ) เขียนโค้ดที่ง่ายขึ้นและสั้นลง โดยไม่จำเป็นต้องวุ่นวายกับรายละเอียดอย่างลูปหรือการแตกแยก
โดยพื้นฐานแล้ว ฟังก์ชันทั้งสามนี้ช่วยให้คุณสามารถใช้ฟังก์ชันบนจำนวนของ iterable ทั้งหมดในครั้งเดียว map
และ filter
มาเป็นมาตรฐานใน Python (อยู่ในโมดูล __builtins__
) และไม่ต้องมีการนำเข้า แต่ reduce
ต้องนำเข้าเนื่องจากอยู่ในโมดูล functools
มาดูกันดีกว่าว่าพวกเขาทำงานอย่างไร โดยเริ่มจาก map
Map
ฟังก์ชัน map()
ใน Python มีไวยากรณ์ดังนี้:
map(func, *iterables)
ที่ซึ่ง func
คือฟังก์ชันที่แต่ละองค์ประกอบใน iterables
(มีจำนวนเท่าไร) จะถูกนำไปใช้ สังเกตว่ามีเครื่องหมายดอกจัน (\*
) บน iterables
? มันหมายความว่าสามารถมีได้หลาย iterables เท่าที่จะทำได้ เนื่องจาก func
ต้องมีจำนวนอาร์กิวเมนต์ใหม่เข้าที่ต้องการ ก่อนที่เราจะไปยังตัวอย่าง เป็นสิ่งสำคัญที่คุณจะต้องใส่ใจดังต่อไปนี้:
- ใน Python 2 ฟังก์ชัน
map()
จะส่งคืนรายการ ใน Python 3 ฟังก์ชันจะส่งคืนmap object
ซึ่งเป็น generator object. ในการรับผลลัพธ์เป็นรายการสามารถเรียกใช้ฟังก์ชันบิลต์อินlist()
บนวัตถุแผนที่ ได้แก่list(map(func, *iterables))
- จำนวนอาร์กิวเมนต์ใน
func
จะต้องตรงกับจำนวนของiterables
ที่ระบุ
มาดูว่ากฎเหล่านี้ทำงานอย่างไรกับตัวอย่างดังต่อไปนี้
กล่าวว่าฉันมีรายการ (iterable
) ของชื่อสัตว์เลี้ยงที่ฉันชื่นชอบทั้งหมดในอักษรตัวเล็กและฉันต้องการให้พวกเขาเป็นอักษรตัวใหญ่ ปกติแล้วใน Python แบบดั้งเดิมฉันจะทำอะไรแบบนี้:
```python my_pets = ['alfred', 'tabitha', 'william', 'arla'] uppered_pets = []
for pet in my_pets: pet_ = pet.upper() uppered_pets.append(pet_)
print(uppered_pets) ```
ซึ่งจะให้เอาท์พุทเป็น ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
ด้วยฟังก์ชัน map()
มันไม่เพียงแต่สะดวกง่ายขึ้นเท่านั้น แต่ยังสามารถยืดหยุ่นได้มากขึ้นด้วย ฉันเพียงแค่ทำแบบนี้:
```python
Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets) ```
ซึ่งจะให้เอาท์พุทเป็นผลลัพธ์เดิม สังเกตว่าการใช้ไวยากรณ์ที่กำหนดไว้ของ map()
ด้านบน func
ในกรณีนี้คือ str.upper
และ iterables
คือรายการ my_pets
ซึ่งเป็นหนึ่งเดียวที่ทำงาน ส่วนบันทึกคือเราไม่ได้เรียกฟังก์ชัน str.upper
(ทำเช่นนี้: str.upper()
) เพราะฟังก์ชัน map ทำงานให้เราใน แต่ละองค์ประกอบในรายการ my_pets
สิ่งที่สำคัญกว่าที่ควรทราบคือฟังก์ชัน str.upper
ต้องการเพียง หนึ่ง อาร์กิวเมนต์ตามที่กำหนดไว้ และดังนั้นเราจึงส่งเพียง หนึ่ง iterable ให้มัน ดังนั้น ถ้าฟังก์ชันที่คุณกำลังส่งต้องการสองหรือสามหรือ n อาร์กิวเมนต์ จากนั้น คุณต้องส่งสองสามหรือ n iterables ให้มัน มาฉันอธิบายกับตัวอย่างอื่น
กล่าวว่าฉันมีรายการของพื้นที่วงกลมที่ฉันวัดไว้ทั้งหมดในห้าหน่วยทศนิยม และฉันจำเป็นต้องปัดเศษแต่ละองค์ประกอบในรายการให้ไปยังตำแหน่งทศนิยมของมันเอง ซึ่งหมายความว่าฉันต้องปัดเศษองค์ประกอบแรกในรายการให้ไปยังหนึ่งตำแหน่งทศนิยม องค์ประกอบที่สองในรายการให้ไปยังสองตำแหน่งทศนิยม องค์ประกอบที่สามในรายการให้ไปยังสามตำแหน่งทศนิยม ฯลฯ ด้วย map()
ง่ายเหมือนกับเค้ก มาดูกัน
Python ได้ให้ฟังก์ชัน round()
ที่เป็น built-in ไว้อยู่แล้วที่ใช้สองอาร์กิวเมนต์ -- ตัวเลขที่จะปัดขึ้นและจำนวนตำแหน่งทศนิยมที่จะปัดตัวเลขขึ้น ดังนั้นเนื่องจากฟังก์ชันต้องการ สอง อาร์กิวเมนต์ เราต้องส่ง สอง iterables.
```python
Python 3
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, range(1, 7)))
print(result) ```
เห็นความงามของ map()
? คุณสามารถจินตนาการถึงความยืดหยุ่นนี้ได้ไหม?
ฟังก์ชัน range(1, 7)
ทำหน้าที่เป็นอาร์กิวเมนต์ที่สองให้กับฟังก์ชัน round
(จำนวนตำแหน่งทศนิยมที่จำเป็นในแต่ละขั้นตอน) ดังนั้นเมื่อ map
เดินทางผ่าน circle_areas
ในการทำงานครั้งแรก องค์ประกอบแรกของ circle_areas
3.56773
ถูกส่งไปพร้อมกับองค์ประกอบแรกของ range(1,7)
, 1
ให้กับ round
ทำให้เป็น round(3.56773, 1)
ในการทำงานครั้งที่สอง องค์ประกอบที่สองของ circle_areas
5.57668
ถูกส่งพร้อมกับองค์ประกอบที่สองของ range(1,7)
, 2
ให้กับ round
ซึ่งทำให้เป็น round(5.57668, 2)
สิ่งนี้เกิดขึ้นจนกระทั่งสิ้นสุดรายการ circle_areas
ฉันแน่ใจว่าคุณคงสงสัย: "ถ้าฉันส่ง iterable ที่น้อยกว่า หรือมากกว่า ความยาวของ iterable ตัวแรกล่ะ? เช่น ถ้าฉันส่ง range(1, 3)
หรือ range(1, 9999)
เป็น iterable ที่สองในฟังก์ชันข้างต้น" และคำตอบนั้นง่าย: ไม่มีอะไรเกิดขึ้น! เอาละ นั่นไม่ใช่ความจริง "ไม่มีอะไร" เกิดขึ้นในแง่ว่า ฟังก์ชัน map()
จะไม่ทำให้เกิดข้อยกเว้นใดๆ มันเพียงแค่วิ่งผ่านองค์ประกอบจนไม่ได้เจออาร์กิวเมนต์ที่สองให้ฟังก์ชัน จากจุดนี้มันหยุดและคืนผลลัพธ์ในทันที
ดังนั้น ตัวอย่างเช่น หากคุณประเมิน result = list(map(round, circle_areas, range(1, 3)))
, คุณจะไม่เกิดข้อผิดพลาดแม้ความยาวของ circle_areas
และความยาวของ range(1, 3)
แตกต่างกัน แทน Python ทำเช่นนี้: มันเอาองค์ประกอบแรกของ circle_areas
และองค์ประกอบแรกของ range(1,3)
และส่งไปให้ round
round
ประเมินและบันทึกผลลัพธ์ จากนั้นไปทำงานครั้งที่สอง องค์ประกอบที่สองของ circle_areas
และองค์ประกอบที่สองของ range(1,3)
round
บันทึกผลลัพธ์อีกครั้ง ตอนนี้ในการทำงานครั้งที่สาม (circle_areas
มีองค์ประกอบที่สาม) Python จะนำองค์ประกอบที่สามของ circle_areas
และพยายามที่จะนำองค์ประกอบที่สามของ range(1,3)
แต่เนื่องจาก range(1,3)
ไม่มีองค์ประกอบที่สาม Python จบและคืนผลลัพธ์ ซึ่งในกรณีนี้จะเป็น [3.6, 5.58]
ลองดูเองสิ
```python
Python 3
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, range(1, 3)))
print(result) ```
สถานการณ์แบบเดียวกันเกิดขึ้นเมื่อ circle_areas
น้อยกว่าความยาวของ iterable ที่สอง Python จบเมื่อไม่เจอองค์ประกอบถัดไปใน iterables เหล่านี้
เพื่อเสริมความเข้าใจในฟังก์ชัน map()
เราจะใช้มันในการสร้างฟังก์ชัน zip()
ของเราเอง ฟังก์ชัน zip()
เป็นฟังก์ชันที่รับจำนวนของ iterables และสร้าง tuple ที่ประกอบด้วยแต่ละองค์ประกอบใน iterables เหล่านั้น เช่นเดียวกับ map()
ใน Python 3 มันส่งคืนเป็น generator object ซึ่งสามารถแปลงเป็นรายการได้อย่างง่ายโดยการเรียกใช้ฟังก์ชันบิลต์อิน list
บนมัน ใช้เซสชันตัวแปลด้านล่างเพื่อทำความเข้าใจฟังก์ชัน zip()
ก่อนที่เราจะสร้างฟังก์ชันด้วย map()
```python
Python 3
my_strings = ['a', 'b', 'c', 'd', 'e'] my_numbers = [1, 2, 3, 4, 5]
results = list(zip(my_strings, my_numbers))
print(results) ```
เป็นโบนัส คุณสามารถเดาได้ไหมว่าจะเกิดอะไรขึ้นในเซสชันข้างบนถ้า my_strings
และ my_numbers
มีความยาวไม่เท่ากัน? ไม่ใช่เหรอ? ลองดูสิ! เปลี่ยนความยาวของทั้งสองดูบ้าง
ต่อไปจะเป็นฟังก์ชัน zip()
ที่เราออกแบบเอง!
```python
Python 3
my_strings = ['a', 'b', 'c', 'd', 'e'] my_numbers = [1, 2, 3, 4, 5]
results = list(map(lambda x, y: (x, y), my_strings, my_numbers))
print(results) ```
ดูสิ! เรามีผลลัพธ์เดียวกันกับ zip
.
คุณได้สังเกตไหมว่าฉันไม่ต้องสร้างฟังก์ชันด้วยวิธีมาตรฐานของ def my_function()
แบบเดิมเลย? นั่นแหละคือความยืดหยุ่นของ map()
และ Python โดยทั่วไป! ฉันใช้ฟังก์ชัน lambda
แทน โดยไม่จำเป็นต้องบอกว่า การใช้วิธีการกำหนดฟังก์ชันมาตรฐาน (ของ def function_name()
) ไม่สามารถใช้ได้ มันยังเป็นที่ยอมรับอยู่ ฉันแค่ต้องการเขียนโค้ดให้น้อยลง (มีความ "Pythonic")
นั่นคือทั้งหมดเกี่ยวกับ map ไปที่ filter()
Filter
ในขณะที่ map()
ผ่านแต่ละองค์ประกอบใน iterable ผ่านฟังก์ชันและคืนผลของทุกองค์ประกอบที่ได้ผ่านฟังก์ชันแล้ว filter()
อย่างแรกฟังก์ชันนั้นจะต้องคืนค่าความจริง (true หรือ false) และผ่านแต่ละองค์ประกอบใน iterable ผ่านฟังก์ชัน "fILTERring" ออกไปซึ่งเป็น false มันมีไวยากรณ์ดังนี้:
filter(func, iterable)
จุดดังต่อไปนี้มีความสำคัญเกี่ยวกับ filter()
:
- ไม่เหมือน
map()
มีเพียง iterable เดียวที่ถูกต้อง - อาร์กิวเมนต์
func
จำเป็นต้องคืนค่า boolean type ถ้าไม่คืนค่าแบบนี้filter
จะส่งคืนiterable
ที่ผ่านให้ได้ และเนื่องจากใช้ iterable เดียว มันเป็นกลางว่าฟังก์ชันนี้จะต้องรับเพียงอาร์กิวเมนต์เดียวเท่านั้น filter
ผ่านแต่ละองค์ประกอบใน iterable ผ่านfunc
และคืนค่าเฉพาะที่ประเมินเป็นจริงเท่านั้น ฉันหมายถึง มันตรงนั้นแล้ว -- "filter".
มาดูตัวอย่างกันบ้าง
รายการต่อไปนี้ (iterable
) ของคะแนนของนักเรียน 10 คนในข้อสอบเคมี มาดูกันว่าใครผ่านด้วยคะแนนมากกว่า 75...โดยใช้ filter
.
```python
Python 3
scores = [66, 90, 68, 59, 76, 60, 88, 74, 81, 65]
def is_A_student(score): return score > 75
over_75 = list(filter(is_A_student, scores))
print(over_75) ```
ตัวอย่างถัดไปจะเป็นตัวตรวจจับคำที่เป็น "palindrome" ซึ่งหมายถึงคำ วลี หรือเส้นที่อ่านได้เหมือนกันไปข้างหน้าและย้อนกลับ มาจัดการกรองคำที่เป็น palindrome จาก tuple (iterable
) ของคำที่สงสัยว่ามันเป็น palindrome.
```python
Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes) ```
ซึ่งควรจะให้ผลลัพธ์เป็น ['madam', 'anutforajaroftuna']
.
ค่อนข้างเจ๋งนะ? ต่อไปเป็น reduce()
Reduce
reduce
ใช้ฟังก์ชัน ของอาร์กิวเมนต์สองตัว ต่อเนื่องไปยังองค์ประกอบของ iterable, สามารถมีอาร์กิวเมนต์เริ่มต้น (initial) ได้ด้วย มันมีไวยากรณ์ดังนี้:
reduce(func, iterable[, initial])
ที่ซึ่ง func
คือฟังก์ชันที่แต่ละองค์ประกอบใน iterable
ถูกนำมาใช้คำนวณต่อเนื่อง และ initial
เป็นค่าที่เลือกได้ซึ่งถูกวางก่อนองค์ประกอบของ iterable ในการคำนวณ, และทำหน้าที่เป็นค่าเริ่มต้นเมื่อ iterable ว่าง จุดหลักต่อไปนี้เกี่ยวกับ reduce()
ควรจำไว้:
func
ต้องการอาร์กิวเมนต์สองตัว, ตัวแรกของมันคือองค์ประกอบแรกในiterable
(ถ้ามีinitial
จะไม่ใช้) และองค์ประกอบที่สองในiterable
. ถ้ามีinitial
จะใช้เป็นอาร์กิวเมนต์แรกไปยังfunc
และองค์ประกอบแรกในiterable
จะกลายเป็นองค์ประกอบที่สองreduce
ลด (I know, forgive me)iterable
ให้เป็นค่าเดียว
ตามปกติ, มาดูตัวอย่างกัน
มาสร้างฟังก์ชัน sum()
ที่เป็นของเราใน Python ซึ่งฟังก์ชัน sum()
คืนค่าผลรวมของทุกองค์ประกอบที่ผ่านไปที่ iterable
```python
Python 3
from functools import reduce
numbers = [3, 4, 6, 9, 34, 12]
def custom_sum(first, second): return first + second
result = reduce(custom_sum, numbers) print(result) ```
ผลลัพธ์, ตามที่คุณคาดเลยว่าเป็น 68
.
ดังนั้นเกิดอะไรขึ้น?
ตามปกติเลย มันคือเรื่องของการทำงานต่อเนื่อง: reduce
ใช้อันแรกและอันที่สองใน numbers
และส่งให้กับ custom_sum
แยกตามลำดับ custom_sum
คำนวณและคืนค่าผลให้กับ reduce
แล้ว reduce
เอาผลลัพธ์นั้นมาใช้เป็นอันแรกให้กับ custom_sum
และนำอันถัดไปใน numbers
เป็นอันที่สองให้ custom_sum
มันทำแบบนี้ต่อเนื่อง (สะสม) ทบที่ numbers
หมด.
มาดูกันว่าจะเกิดอะไรขึ้นเมื่อใช้ค่า initial
ที่เลือกได้
```python
Python 3
from functools import reduce
numbers = [3, 4, 6, 9, 34, 12]
def custom_sum(first, second): return first + second
result = reduce(custom_sum, numbers, 10) print(result) ```
ผลลัพธ์ตามที่คาดคือ 78
เพราะ reduce
เริ่มแรกใช้ 10
เป็นอาร์กิวเมนต์ตัวแรกให้กับ custom_sum
.
นั่นคือทั้งหมดเกี่ยวกับ Map, Reduce, และ Filter ของ Python ลองทำแบบฝึกหัดข้างล่างเพื่อช่วยรวบรวมความเข้าใจของคุณเกี่ยวกับแต่ละฟังก์ชัน
Exercise
ในแบบฝึกหัดนี้ คุณจะได้ใช้แต่ละฟังก์ชันของ map
, filter
, และ reduce
เพื่อแก้ไขโค้ดที่มีปัญหา.
from functools import reduce
# Use map to print the square of each numbers rounded
# to three decimal places
my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]
# Use filter to print only the names that are less than
# or equal to seven letters
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]
# Use reduce to print the product of these numbers
my_numbers = [4, 6, 9, 23, 5]
# Fix all three respectively.
map_result = list(map(lambda x: x, my_floats))
filter_result = list(filter(lambda name: name, my_names, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers, 0)
print(map_result)
print(filter_result)
print(reduce_result)
#### Map
from functools import reduce
my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]
my_numbers = [4, 6, 9, 23, 5]
map_result = list(map(lambda x: round(x ** 2, 3), my_floats))
filter_result = list(filter(lambda name: len(name) <= 7, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers)
print(map_result)
print(filter_result)
print(reduce_result)
test_output_contains("[18.922, 37.088, 10.562, 95.453, 4.666, 78.854, 21.068]")
test_output_contains("['olumide', 'josiah', 'omoseun']")
test_output_contains("24840")
success_msg("Congrats! Nice work.")
This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join over a million other learners and get started learning Python for data science today!