ทำความเข้าใจเกี่ยวกับฟังก์ชั่นแผนที่


311
map(function, iterable, ...)

ใช้ฟังก์ชั่นกับทุกรายการของ iterable และส่งคืนรายการผลลัพธ์ หากมีการส่งผ่านอาร์กิวเมนต์ที่ซ้ำได้เพิ่มเติมฟังก์ชันต้องใช้อาร์กิวเมนต์จำนวนมากและนำไปใช้กับไอเท็มจาก iterables ทั้งหมดพร้อมกัน

หากหนึ่ง iterable สั้นกว่าอีกมันจะถือว่าเป็นขยายด้วยไม่มีรายการ

หากฟังก์ชั่นฟังก์ชั่นNoneตัวตนจะถือว่า; หากมีหลายอาร์กิวเมนต์ให้map()ส่งคืนรายการที่ประกอบด้วย tuples ที่มีรายการที่สอดคล้องจาก iterables ทั้งหมด (ชนิดของการดำเนินการแปลงข้อมูล)

ข้อโต้แย้งที่ทำซ้ำอาจเป็นลำดับหรือวัตถุที่ทำซ้ำได้; ผลลัพธ์จะเป็นรายการเสมอ

สิ่งนี้มีบทบาทอย่างไรในการสร้างผลิตภัณฑ์คาร์ทีเซียน?

content = map(tuple, array)

เอฟเฟกต์ใดที่ทำให้สิ่งอันดับในนั้นมี? ฉันยังสังเกตเห็นว่าหากไม่มีฟังก์ชั่นแผนที่เอาต์พุตabcก็จะเป็นa, b, cไปตามนั้น

ฉันต้องการเข้าใจฟังก์ชั่นนี้อย่างสมบูรณ์ คำจำกัดความการอ้างอิงก็ยากที่จะเข้าใจเช่นกัน ปุยแฟนซีมากเกินไป


2
สิ่งใดที่คุณต้องการจริงเพื่อให้บรรลุและทำไมโดยเฉพาะคุณต้องการที่จะใช้งานmap?
Kris Harper

3
@WebMaster ใช่ตามประโยคแรกในเอกสารที่คุณวาง - "ใช้ฟังก์ชั่นกับทุกรายการของ iterable" ส่วนที่เหลือของวรรคเป็นเรื่องเกี่ยวกับกรณีที่ซับซ้อนมากขึ้น - เช่นผลัดกันออกไปทำmap(None, a, b, c) zip(a, b, c)แต่คุณไม่ค่อยเห็นว่าในทางปฏิบัติแม่นยำเพราะการzipโทรนั้นเทียบเท่ากัน
lvc

9
ฉันพยายามอย่างหนักเพื่อเรียนรู้หลามและเมื่อใดก็ตามที่ฉันเปิดคำจำกัดความใน python.org หลังจากประโยคแรกฉันไม่เข้าใจอะไรเลย Alright ขอบคุณ.
เว็บมาสเตอร์

2
tupleเป็นฟังก์ชั่น (ดีของตนที่เหมาะสมยิ่งขึ้นกว่า แต่ก็จะทำงานเช่นฟังก์ชั่น) ที่ใช้เวลา iterable และช่วยให้คุณ tuple ด้วยองค์ประกอบเดียวกัน - เพื่อให้เทียบเท่ากับtuple([1, 2, 3]) (1, 2, 3)สำหรับmap(tuple, array), arrayจะเป็น iterable ของ iterables (คิดว่า list), และมันให้คุณย้อนกลับแต่ละ list ภายในกลายเป็น tuple.
lvc

1
โดยทั่วไปแล้วมันเป็นประโยคแรกของเอกสารของฟังก์ชั่นใด ๆ ที่สำคัญที่สุด หากคุณเข้าใจสิ่งนั้นคุณจะได้รับส่วนสำคัญ ส่วนที่เหลือของมันระบุพฤติกรรมในรายละเอียดที่ดีและบางส่วนนั้นจะค่อนข้างทึบแสงเริ่มต้นด้วยและคุณอาจต้องเจอสำนวนแปลก ๆ โดยยึดตามก่อนที่คุณจะเห็น "โอ้นั่นคือความหมาย!" แต่เมื่อคุณได้รับช่วงเวลาหลอดไฟสำหรับบิวด์อินบางตัวคุณควรเริ่มเข้าใจเอกสารได้ง่ายขึ้น
lvc

คำตอบ:


441

mapไม่ได้เป็น pythonic โดยเฉพาะ ฉันอยากจะแนะนำให้ใช้ list comprehensions แทน:

map(f, iterable)

โดยทั่วไปเทียบเท่ากับ:

[f(x) for x in iterable]

mapด้วยตัวเองไม่สามารถทำผลิตภัณฑ์คาร์ทีเซียนได้เนื่องจากความยาวของรายการเอาท์พุทจะเหมือนกับรายการอินพุตเสมอ คุณสามารถทำผลิตภัณฑ์คาร์ทีเซียนเล็กน้อยด้วยความเข้าใจในรายการว่า:

[(a, b) for a in iterable_a for b in iterable_b]

ไวยากรณ์มีความสับสนเล็กน้อยซึ่งโดยทั่วไปเทียบเท่ากับ:

result = []
for a in iterable_a:
    for b in iterable_b:
        result.append((a, b))

36
ฉันพบว่าใช้mapverbose น้อยกว่าความเข้าใจในรายการอย่างน้อยสำหรับกรณีที่คุณกำลังสาธิต
marbel

1
ฉันจะใช้แผนที่สำหรับคุณสมบัติได้อย่างไร เป็นสิ่งที่mapเทียบเท่าของ[v.__name__ for v in (object, str)]?
Sz

@ASz เกี่ยวกับmap(lambda v: v.__name__, list)อะไร
Kilian

10
แผนที่เร็วขึ้นเนื่องจากไม่เรียกใช้ฟังก์ชั่นตามความยาวของตัววนซ้ำ .. ฟังก์ชั่นการโทรมีโอเวอร์เฮด .. ดู 6:00 youtube.com/watch?v=SiXyyOA6RZg&t=813s
anati

1
@anati ผมคิดว่าmapเป็นบางครั้งเร็วกว่า comprehensions บางครั้งไม่แม่นยำเพราะฟังก์ชั่นค่าใช้จ่ายในการโทร? โดยเฉพาะอย่างยิ่งฮิวริสติกที่ฉันได้เรียนรู้คือเมื่อใช้งานmapคุณต้องแนะนำการเรียกฟังก์ชั่นพิเศษความเข้าใจเร็วขึ้นหรือไม่ เช่นฉันถูกชักนำให้เชื่อว่าmap(lambda foo: foo.bar, my_list)ช้ากว่าfoo.bar for foo in my_listและแม้แต่map(operator.add, my_list_of_pairs)ช้ากว่าx + y for x, y in my_list_of_pairsแม่นยำเพราะการเรียกใช้ฟังก์ชันเพิ่มเติม
mtraceur

86

mapไม่เกี่ยวข้องกับผลิตภัณฑ์ Cartesian ที่ทุกคน mapแต่ผมคิดว่ามีคนดีมีประสบการณ์ในการเขียนโปรแกรมการทำงานอาจเกิดขึ้นกับบางอย่างเป็นไปไม่ได้ที่จะเข้าใจวิธีการสร้างโดยใช้

map ใน Python 3 เทียบเท่ากับสิ่งนี้:

def map(func, iterable):
    for i in iterable:
        yield func(i)

และข้อแตกต่างเพียงอย่างเดียวใน Python 2 ก็คือมันจะสร้างรายการผลลัพธ์ทั้งหมดเพื่อส่งคืนพร้อมกันแทนที่จะกลับมาyieldใหม่

แม้ว่าโดยทั่วไปแล้วการประชุมงูหลามจะชอบรายการความเข้าใจ (หรือนิพจน์เครื่องกำเนิดไฟฟ้า) เพื่อให้ได้ผลลัพธ์เดียวกันกับการเรียกmapโดยเฉพาะอย่างยิ่งถ้าคุณใช้นิพจน์แลมบ์ดาเป็นอาร์กิวเมนต์แรก:

[func(i) for i in iterable]

เป็นตัวอย่างของสิ่งที่คุณถามในความคิดเห็นเกี่ยวกับคำถาม - "เปลี่ยนสตริงเป็นอาร์เรย์" โดย 'array' คุณอาจต้องการ tuple หรือรายการ (ทั้งคู่ทำตัวเหมือนอาร์เรย์จากภาษาอื่น) -

 >>> a = "hello, world"
 >>> list(a)
['h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd']
>>> tuple(a)
('h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd')

การใช้mapที่นี่จะเป็นถ้าคุณเริ่มต้นด้วยรายการสตริงแทนที่จะเป็นสตริงเดียว - mapสามารถฟังรายการเหล่านั้นทั้งหมดทีละรายการ:

>>> a = ["foo", "bar", "baz"]
>>> list(map(list, a))
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]

โปรดทราบว่าmap(list, a)เทียบเท่าใน Python 2 แต่ใน Python 3 คุณต้องมีการlistโทรหากคุณต้องการทำสิ่งอื่นนอกเหนือจากฟีดลงในforลูป (หรือฟังก์ชันการประมวลผลเช่นsumนั้นต้องการเพียง iterable และไม่ใช่ลำดับ) แต่โปรดทราบอีกครั้งว่าปกติแล้วความเข้าใจในรายการมักเป็นที่ต้องการ:

>>> [list(b) for b in a]
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]

แผนที่ (สนุก x -> (x, x)) ดูเหมือนจะไม่ยากที่จะเข้าใจ ... (แม้ว่าจะได้รับสินค้าที่มีคาร์ทีเซียนจริงออกจากแผนที่จะเป็นไปไม่ได้อะไรแผนที่ผลิตอยู่เสมอรูปแบบของรายการบางส่วน)
Kristopher Micinski

36

map สร้างรายการใหม่โดยใช้ฟังก์ชั่นกับทุกองค์ประกอบของแหล่งที่มา:

xs = [1, 2, 3]

# all of those are equivalent — the output is [2, 4, 6]
# 1. map
ys = map(lambda x: x * 2, xs)
# 2. list comprehension
ys = [x * 2 for x in xs]
# 3. explicit loop
ys = []
for x in xs:
    ys.append(x * 2)

n-ary mapเทียบเท่ากับการบีบอัดอินพุตที่ใส่ได้ร่วมกันแล้วใช้ฟังก์ชั่นการแปลงในทุกองค์ประกอบของรายการซิปกลาง มันไม่ใช่ผลิตภัณฑ์คาร์ทีเซียน:

xs = [1, 2, 3]
ys = [2, 4, 6]

def f(x, y):
    return (x * 2, y // 2)

# output: [(2, 1), (4, 2), (6, 3)]
# 1. map
zs = map(f, xs, ys)
# 2. list comp
zs = [f(x, y) for x, y in zip(xs, ys)]
# 3. explicit loop
zs = []
for x, y in zip(xs, ys):
    zs.append(f(x, y))

ผมเคยใช้zipที่นี่ แต่mapพฤติกรรมจริงแตกต่างกันเล็กน้อยเมื่อ iterables ไม่ได้ขนาดเดียวกัน - ตามที่ระบุไว้ในเอกสารของมันก็ขยาย iterables Noneจะมี


1
ซับซ้อนและพยายามแยกแยะโพสต์นี้
Web Master

1
@ WebMaster มีความซับซ้อนเกี่ยวกับอะไร
Jossie Calderon

คำตอบที่ดีที่สุดในความคิดของฉัน การใช้แลมบ์ดาในตัวอย่างเป็นฟังก์ชันทำให้ชัดเจนมาก
sheldonzy

น่าเสียดายที่สิ่งเหล่านี้ไม่เท่ากันเอาท์พุทใช้[2,4,6]สำหรับความเข้าใจในรายการและลูปอย่างชัดเจน แต่แผนที่ส่งคืนออบเจกต์แผนที่ - ตัวอย่างเช่นฉันได้รับ: <map at 0x123a49978>ซึ่งฉันจะต้องเชื่อมโยงกับรายการ
leerssej

20

ทำให้ง่ายขึ้นเล็กน้อยคุณสามารถจินตนาการได้ว่าmap()ทำสิ่งนี้:

def mymap(func, lst):
    result = []
    for e in lst:
        result.append(func(e))
    return result

อย่างที่คุณเห็นมันต้องใช้ฟังก์ชั่นและรายการและส่งคืนรายการใหม่ด้วยผลลัพธ์ของการใช้ฟังก์ชันกับองค์ประกอบแต่ละรายการในรายการอินพุต ฉันพูดว่า "ลดความซับซ้อนเล็กน้อย" เพราะในความเป็นจริงmap()สามารถประมวลผลได้มากกว่าหนึ่ง iterable:

หากมีการส่งผ่านอาร์กิวเมนต์ที่ซ้ำได้เพิ่มเติมฟังก์ชันต้องใช้อาร์กิวเมนต์จำนวนมากและนำไปใช้กับไอเท็มจาก iterables ทั้งหมดพร้อมกัน หากหนึ่ง iterable สั้นกว่าอีกมันจะถือว่าเป็นขยายด้วยไม่มีรายการ

สำหรับส่วนที่สองของคำถาม: สิ่งนี้มีบทบาทอย่างไรในการสร้างผลิตภัณฑ์คาร์ทีเซียน ดีmap() สามารถใช้สำหรับการสร้างผลิตภัณฑ์คาร์ทีเซียนของรายการเช่นนี้:

lst = [1, 2, 3, 4, 5]

from operator import add
reduce(add, map(lambda i: map(lambda j: (i, j), lst), lst))

... แต่เพื่อบอกความจริงการใช้product()เป็นวิธีที่ง่ายและเป็นธรรมชาติมากในการแก้ปัญหา:

from itertools import product
list(product(lst, lst))

ไม่ว่าจะด้วยวิธีใดผลลัพธ์คือผลิตภัณฑ์คาร์ทีเซียนlstที่กำหนดไว้ด้านบน:

[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
 (2, 1), (2, 2), (2, 3), (2, 4), (2, 5),
 (3, 1), (3, 2), (3, 3), (3, 4), (3, 5),
 (4, 1), (4, 2), (4, 3), (4, 4), (4, 5),
 (5, 1), (5, 2), (5, 3), (5, 4), (5, 5)]

17

map()ฟังก์ชั่นจะมีการใช้วิธีการเดียวกันทุกรายการในโครงสร้างข้อมูล iterable เช่นรายการ, เครื่องปั่นไฟ, สตริงและสิ่งอื่น ๆ

ลองดูตัวอย่าง: map()สามารถวนซ้ำทุกไอเท็มในรายการและใช้ฟังก์ชั่นกับแต่ละไอเท็มได้มากกว่าที่มันจะส่งคืน (ให้คุณคืน) รายการใหม่

ลองนึกภาพคุณมีฟังก์ชันที่ใช้ตัวเลขบวก 1 กับตัวเลขนั้นและส่งคืน:

def add_one(num):
  new_num = num + 1
  return new_num

คุณมีรายการหมายเลข:

my_list = [1, 3, 6, 7, 8, 10]

หากคุณต้องการเพิ่มทุกหมายเลขในรายการคุณสามารถทำสิ่งต่อไปนี้:

>>> map(add_one, my_list)
[2, 4, 7, 8, 9, 11]

หมายเหตุ: อย่างน้อยที่สุดmap()ต้องการอาร์กิวเมนต์สองตัว ก่อนชื่อฟังก์ชั่นและสิ่งที่สองเช่นรายการ

เรามาดูสิ่งดีๆอื่น ๆ ที่map()สามารถทำได้ map()สามารถใช้หลาย iterables (รายการ, สตริง, ฯลฯ ) และผ่านองค์ประกอบจากแต่ละ iterable ไปยังฟังก์ชั่นเป็นอาร์กิวเมนต์

เรามีสามรายการ:

list_one = [1, 2, 3, 4, 5]
list_two = [11, 12, 13, 14, 15]
list_three = [21, 22, 23, 24, 25]

map() สามารถทำให้คุณเป็นรายการใหม่ที่มีการเพิ่มองค์ประกอบที่ดัชนีเฉพาะ

ตอนนี้จำได้ว่าmap()ต้องการฟังก์ชั่น เวลานี้เราจะใช้sum()ฟังก์ชันbuiltin การวิ่งmap()ให้ผลลัพธ์ดังนี้:

>>> map(sum, list_one, list_two, list_three)
[33, 36, 39, 42, 45]

โปรดจำไว้ว่า
ใน Python 2 map()จะวนซ้ำ (ผ่านองค์ประกอบของรายการ) ตามรายการที่ยาวที่สุดและส่งผ่านNoneไปยังฟังก์ชันสำหรับรายการที่สั้นกว่าดังนั้นฟังก์ชันของคุณควรค้นหาNoneและจัดการกับไฟล์เหล่านั้นมิฉะนั้นคุณจะได้รับข้อผิดพลาด ใน Python 3 map()จะหยุดหลังจากเสร็จสิ้นรายการที่สั้นที่สุด นอกจากนี้ใน Python 3 map()จะส่งคืนตัววนซ้ำไม่ใช่รายการ


8

Python3 - แผนที่ (func, iterable)

สิ่งหนึ่งที่ไม่ได้กล่าวถึงอย่างสมบูรณ์ (แม้ว่า @BlooB เอ่ยถึงมัน) คือแผนที่ที่ส่งคืนวัตถุแผนที่ไม่ใช่รายการ นี่คือความแตกต่างใหญ่เมื่อมันมาถึงเวลาในการเริ่มต้นและการทำซ้ำ พิจารณาการทดสอบทั้งสองนี้

import time
def test1(iterable):
    a = time.clock()
    map(str, iterable)
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


def test2(iterable):
    a = time.clock()
    [ x for x in map(str, iterable)]
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


test1(range(2000000))  # Prints ~1.7e-5s   ~8s
test2(range(2000000))  # Prints ~9s        ~8s

อย่างที่คุณเห็นการเริ่มต้นฟังก์ชั่นแผนที่นั้นแทบจะไม่มีเวลาเลย อย่างไรก็ตามการวนซ้ำผ่านวัตถุแผนที่ใช้เวลานานกว่าการวนซ้ำผ่านการวนซ้ำ ซึ่งหมายความว่าฟังก์ชั่นที่ส่งผ่านไปยังแผนที่ () จะไม่ใช้กับแต่ละองค์ประกอบจนกว่าจะถึงองค์ประกอบในการทำซ้ำ หากคุณต้องการรายการใช้รายการความเข้าใจ หากคุณวางแผนที่จะวนซ้ำในวง for และจะหยุดในบางจุดจากนั้นใช้แผนที่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.