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 ต้องมีจำนวนอาร์กิวเมนต์ใหม่เข้าที่ต้องการ ก่อนที่เราจะไปยังตัวอย่าง เป็นสิ่งสำคัญที่คุณจะต้องใส่ใจดังต่อไปนี้:

  1. ใน Python 2 ฟังก์ชัน map() จะส่งคืนรายการ ใน Python 3 ฟังก์ชันจะส่งคืน map object ซึ่งเป็น generator object. ในการรับผลลัพธ์เป็นรายการสามารถเรียกใช้ฟังก์ชันบิลต์อิน list() บนวัตถุแผนที่ ได้แก่ list(map(func, *iterables))
  2. จำนวนอาร์กิวเมนต์ใน 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():

  1. ไม่เหมือน map() มีเพียง iterable เดียวที่ถูกต้อง
  2. อาร์กิวเมนต์ func จำเป็นต้องคืนค่า boolean type ถ้าไม่คืนค่าแบบนี้ filter จะส่งคืน iterable ที่ผ่านให้ได้ และเนื่องจากใช้ iterable เดียว มันเป็นกลางว่าฟังก์ชันนี้จะต้องรับเพียงอาร์กิวเมนต์เดียวเท่านั้น
  3. 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() ควรจำไว้:

  1. func ต้องการอาร์กิวเมนต์สองตัว, ตัวแรกของมันคือองค์ประกอบแรกใน iterable (ถ้ามี initial จะไม่ใช้) และองค์ประกอบที่สองใน iterable. ถ้ามี initial จะใช้เป็นอาร์กิวเมนต์แรกไปยัง func และองค์ประกอบแรกใน iterable จะกลายเป็นองค์ประกอบที่สอง
  2. 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!

Previous Tutorial Next Tutorial Take the Test
Copyright © learnpython.org. Read our Terms of Use and Privacy Policy