วิธีใช้ตัวกรองแผนที่และลดใน Python 3


321

filter, mapและreduceทำงานอย่างสมบูรณ์ใน Python 2 นี่คือตัวอย่าง:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55

แต่ใน Python 3 ฉันได้รับผลลัพธ์ต่อไปนี้:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined

ฉันจะขอบคุณถ้ามีคนอธิบายให้ฉันฟังว่าทำไมถึงเป็นเช่นนั้น

สกรีนช็อตของรหัสเพื่อความชัดเจนเพิ่มเติม:

IDLE ของ Python 2 และ 3 เคียงข้างกัน


1
ในระยะสั้นรายการไม่ได้เป็นประเภทข้อมูลเท่านั้น ถ้าคุณต้องการรายการพูดว่าคุณต้องการรายการ แต่ในกรณีส่วนใหญ่คุณต้องการอย่างอื่นอยู่แล้ว
Veky

คำตอบ:


346

คุณสามารถอ่านเกี่ยวกับการเปลี่ยนแปลงในมีอะไรใหม่ในหลาม 3.0 คุณควรอ่านให้ละเอียดเมื่อคุณย้ายจาก 2.x เป็น 3.x เนื่องจากมีการเปลี่ยนแปลงมาก

คำตอบทั้งหมดที่นี่เป็นคำพูดจากเอกสาร

มุมมองและการวนซ้ำแทนรายการ

API ที่รู้จักกันดีบางรายการไม่ส่งคืนรายการอีกต่อไป:

  • [ ... ]
  • map()และfilter()iterators ผลตอบแทน หากคุณต้องการรายการจริงๆการแก้ไขด่วนคือเช่นlist(map(...))แต่การแก้ไขที่ดีกว่ามักจะใช้ list comprehension (โดยเฉพาะอย่างยิ่งเมื่อรหัสดั้งเดิมใช้แลมบ์ดา) หรือเขียนรหัสใหม่ดังนั้นจึงไม่จำเป็นต้องมีรายการเลย โดยเฉพาะอย่างยิ่งหากินถูกmap()เรียกใช้สำหรับผลข้างเคียงของฟังก์ชั่น; การแปลงที่ถูกต้องคือการใช้forลูปปกติ(ตั้งแต่สร้างรายการก็จะสิ้นเปลือง)
  • [ ... ]

builtins

  • [ ... ]
  • ลบออกreduce()แล้ว ใช้functools.reduce()ถ้าคุณต้องการจริงๆ อย่างไรก็ตาม 99 เปอร์เซ็นต์ของเวลาที่forวนลูปชัดเจนสามารถอ่านได้มากขึ้น
  • [ ... ]

21
การเพิ่มlist(map(...) ทุกที่ .. วิธีการในโลกที่ช่วยให้สามารถอ่านได้ .. pythonดูเหมือนจะไม่สามารถจัดการแอปพลิเคชันที่ก้าวหน้า / สตรีมมิ่งของ Combinators ที่ใช้งานได้ ภาษาอื่นที่ฉันสามารถโยงการดำเนินการโหลกับคอลเลกชันในแถวและสามารถอ่านได้ ที่นี่? สิ่งที่คุณต้องการ - วิธีซ้อนหลายสิบin?
javadba

11
หากคุณกำลังทำงานในบริบทที่จำเป็นดังนั้น for-loop น่าจะเป็นตัวเลือกที่อ่านง่ายขึ้น แต่มีเหตุผลที่ดีที่ชอบบริบทของการใช้งาน - และการแยกจากสิ่งนั้นกลับไปสู่ขั้นตอนอาจเป็นเรื่องที่น่าเกลียด
MatrixManAtYrService

2
@javadba คุณแน่ใจหรือไม่ว่าใน "แอพพลิเคชั่นสตรีมมิ่ง" คุณต้องเพิ่มการlistโทรเลย? ฉันคิดว่าความหมายของ "การสตรีม" คือ "ไม่มีการสร้างรายการเลยประมวลผลองค์ประกอบการป้อนข้อมูลแต่ละอย่างเต็มที่ก่อนที่จะดำเนินการต่อไป"
คืนที่ผ่านมาไม่ได้ใน

@MatrixManAtYrService หากคุณแน่ใจว่า python 2 นั้นเป็นสิ่งที่คุณต้องการคุณสามารถกำหนดใหม่mapได้เสมอ
คืนที่ผ่านมาไม่ได้ใน

6
ฉันยังคงไม่เข้าใจว่าข้อโต้แย้งการอ่านทำให้เกิดการเปลี่ยนแปลงอย่างไร ถ้ามันเป็นผลการดำเนินงานสำหรับเหตุผลที่ผมอาจจะเข้าใจ ...
มินาโตะ

86

ฟังก์ชั่นของmapและfilterเปลี่ยนโดยเจตนาเพื่อส่งคืนตัววนซ้ำและการลดถูกลบออกจากการมีอยู่functools.reduceแล้วภายใน

ดังนั้นสำหรับfilterและmapคุณสามารถปิดพวกเขาด้วยlist()เพื่อดูผลลัพธ์ที่คุณเคยทำมาก่อน

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>

คำแนะนำตอนนี้คือคุณแทนที่การใช้แผนที่และตัวกรองด้วยนิพจน์กำเนิดหรือรายการความเข้าใจ ตัวอย่าง:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

พวกเขาบอกว่าสำหรับลูปคือร้อยละ 99 ของเวลาที่อ่านง่ายกว่าการลด แต่ฉันแค่ติดfunctools.reduceอยู่

แก้ไข : ตัวเลข 99 เปอร์เซ็นต์จะถูกดึงจากหน้าWhat's New In Python 3.0โดยตรงที่เขียนโดย Guido van Rossum


5
คุณไม่จำเป็นต้องสร้างฟังก์ชั่นพิเศษในรายการความเข้าใจ ใช้เพียง[i*i*i for i in range(1,11)]
เซี่ยว

2
คุณถูกต้องอย่างแน่นอน ฉันเก็บฟังก์ชันไว้ในตัวอย่างความเข้าใจในรายการเพื่อให้มันดูคล้ายกับตัวอย่างตัวกรอง / แผนที่
Joshua D. Boyd

5
i ** 3 ก็เท่ากับ i * i * i
Breezer

5
@Breezer จริง ๆ แล้วi**3จะเรียกi.__pow__(3)และi*i*i i.__mul__(i).__mul__(i)(หรืออะไรทำนองนั้น) ด้วย ints มันไม่สำคัญ แต่ด้วยจำนวน numpy / คลาสที่กำหนดเองมันอาจให้ผลลัพธ์ที่แตกต่างกัน
syntonym

1
ฉันสังเกตเห็นว่าเมื่อใดก็ตามที่เราได้ยินว่า "Guido ตัดสินใจ X" ความเจ็บปวดนั้นเป็นผลที่น่าจะเกิดขึ้น นี่เป็นตัวอย่างที่ดีlist(list(list(.. )))ในการทำสิ่งที่มีอยู่แล้วในไพ ธ อน
javadba

12

ในฐานะที่เป็นภาคผนวกของคำตอบอื่น ๆ ฟังดูเหมือนเป็นประโยชน์กรณีสำหรับผู้จัดการบริบทที่จะแมปชื่อของฟังก์ชั่นเหล่านี้กับคนที่กลับรายการและแนะนำreduceใน namespace ทั่วโลก

การนำไปปฏิบัติอย่างรวดเร็วอาจมีลักษณะเช่นนี้:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func

ด้วยการใช้งานที่มีลักษณะเช่นนี้:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))

สิ่งที่พิมพ์:

190
[1, 2]

แค่ 2 เซ็นต์ของฉัน :-)


1
pythonเป็นภาษาเป็นระเบียบ - แต่มันก็มีวีดีไปยังห้องสมุดยอดเยี่ยม: numpy, pandas, statsmodelsและเพื่อน .. ฉันได้รับการ buliding ห้องสมุดความสะดวกสบายเหมือนที่คุณแสดงที่นี่เพื่อลดอาการปวดของภาษาพื้นเมือง - แต่ได้สูญเสียพลังงานและพยายามที่จะไม่ หลงทางไกลจากdata.frame/ หรือdatatable xarrayแต่ความรุ่งโรจน์สำหรับการพยายาม ..
javadba

7

เนื่องจากreduceวิธีการได้ถูกลบออกจากฟังก์ชั่นในตัวจาก Python3 อย่าลืมที่จะนำเข้าfunctoolsในรหัสของคุณ โปรดดูข้อมูลโค้ดด้านล่าง

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)

2

นี่คือตัวอย่างของตัวกรองแผนที่และลดฟังก์ชั่น

หมายเลข = [10,11,12,22,34,43,54,34,67,87,88,88,98,99,87,44,66]

//กรอง

oddNumbers = รายการ (ตัวกรอง (แลมบ์ดา x: x% 2! = 0, ตัวเลข)

พิมพ์ (oddNumbers)

//แผนที่

multiplyOf2 = รายการ (แผนที่ (แลมบ์ดา x: x * 2, ตัวเลข)

พิมพ์ (multiplyOf2)

//ลด

ฟังก์ชั่นลดขนาดเนื่องจากไม่ได้ใช้กันทั่วไปถูกลบออกจากฟังก์ชั่นในตัวใน Python 3 ซึ่งยังคงมีอยู่ในโมดูลฟังก์ชั่นoolsดังนั้นคุณสามารถทำสิ่งต่อไปนี้

จาก functools นำเข้าลดลง

sumOfNumbers = ลด (แลมบ์ดา x, y: x + y, ตัวเลข)

พิมพ์ (sumOfNumbers)


0

ข้อดีอย่างหนึ่งของแผนที่ตัวกรองและการลดลงคือความชัดเจนในการใช้งานเมื่อคุณ "โยง" เข้าด้วยกันเพื่อทำสิ่งที่ซับซ้อน อย่างไรก็ตามไวยากรณ์ในตัวไม่สามารถอ่านได้และเป็น "ถอยหลัง" ทั้งหมด ดังนั้นฉันแนะนำให้ใช้PyFunctionalแพ็คเกจ ( https://pypi.org/project/PyFunctional/ ) นี่คือการเปรียบเทียบของทั้งสอง:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}

รุ่น PyFunctional

ไวยากรณ์ที่ชัดเจนมาก คุณสามารถพูดได้:

"ฉันมีจุดหมายปลายทางของเที่ยวบินตามลำดับซึ่งฉันต้องการรับคีย์ dict หากเมืองอยู่ในค่า dict ในที่สุดกรองรายการว่างที่ฉันสร้างขึ้นในกระบวนการ"

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \

เวอร์ชัน Python เริ่มต้น

มันย้อนกลับทั้งหมด คุณต้องพูดว่า:

"ตกลงมีรายการฉันต้องการกรองรายการที่ว่างเปล่าออกมาทำไมเพราะฉันได้รับคีย์ dict หากเมืองอยู่ในค่า dict โอ้รายการที่ฉันทำเช่นนี้คือ flight_destinations_dict "

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.