ความแตกต่างระหว่าง chain และ chain.from_iterable ใน itertools คืออะไร?

ฉันไม่พบตัวอย่างที่ถูกต้องบนอินเทอร์เน็ตที่ฉันสามารถเห็นความแตกต่างระหว่างตัวอย่างเหล่านี้และสาเหตุที่ต้องเลือกตัวอย่างอื่น


person user1994660    schedule 21.02.2013    source แหล่งที่มา


คำตอบ (6)


ตัวแรกรับอาร์กิวเมนต์ 0 ตัวขึ้นไป โดยแต่ละตัวสามารถวนซ้ำได้ ส่วนตัวที่สองรับหนึ่งอาร์กิวเมนต์ซึ่งคาดว่าจะสร้างค่าวนซ้ำได้:

from itertools import chain

chain(list1, list2, list3)

iterables = [list1, list2, list3]
chain.from_iterable(iterables)

แต่ iterables สามารถเป็นตัววนซ้ำใด ๆ ที่ให้ผลการวนซ้ำได้:

def gen_iterables():
    for i in range(10):
        yield range(i)

itertools.chain.from_iterable(gen_iterables())

โดยปกติแล้วการใช้รูปแบบที่สองจะเป็นกรณีของความสะดวก แต่เนื่องจากรูปแบบนี้จะวนซ้ำอินพุตที่ทำซ้ำได้อย่างเกียจคร้าน จึงเป็นวิธีเดียวที่คุณสามารถเชื่อมโยงจำนวนตัววนซ้ำที่มีจำกัด ไม่สิ้นสุด ได้:

def gen_iterables():
    while True:
        for i in range(5, 10):
            yield range(i)

chain.from_iterable(gen_iterables())

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

person Martijn Pieters    schedule 21.02.2013
comment
ฉันยังไม่สามารถรับมันได้ คุณช่วยบอกความแตกต่างของเอาต์พุตและกรณีการใช้งานในสถานการณ์จริงว่าจะใช้อะไรได้บ้าง - person user1994660; 22.02.2013
comment
@ user1994660: ไม่มีความแตกต่างเอาต์พุต มันเป็นความแตกต่าง อินพุต ช่วยให้ใช้อินพุตบางอย่างได้ง่ายขึ้น - person Martijn Pieters; 22.02.2013
comment
@ user1994660: ฉันใช้รูปแบบที่สองใน คำตอบนี้ - person Martijn Pieters; 22.02.2013
comment
@ user1994660: usecase ที่ดีสำหรับรูปแบบที่สอง: Python idiom to chain (flatten) an infinite iterable of finite iterables? - person Martijn Pieters; 22.02.2013
comment
@ user1994660: เรียกใช้รหัสนี้: # Return an iterator of iterators def it_it(): return iter( [iter( [11, 22] ), iter( [33, 44] )] ) print( list(itertools.chain.from_iterable(it_it())) ) print( list(itertools.chain(it_it())) ) print( list(itertools.chain(*it_it())) ) อันแรกดีที่สุด อันที่สองไม่ได้รับที่ตัววนซ้ำที่ซ้อนกัน แต่จะส่งคืนตัววนซ้ำแทนที่จะเป็นตัวเลขที่ต้องการ อันที่สามสร้างเอาต์พุตที่ถูกต้อง แต่มันไม่ได้ขี้เกียจอย่างสมบูรณ์: * บังคับให้สร้างตัววนซ้ำทั้งหมด สำหรับอินพุตใบ้นี้ไม่สำคัญ - person ToolmakerSteve; 20.12.2013
comment
โปรดทราบว่าหากการทำซ้ำไม่ใหญ่เกินไป คุณก็สามารถทำได้ itertools.chain(*iterables) - person balki; 15.02.2014
comment
@MartijnPieters คุณจะรังเกียจไหมถ้าฉันอ้างถึงคุณใน วิธีเดียวที่คุณสามารถเชื่อมโยงตัววนซ้ำที่มีจำนวนไม่สิ้นสุด? - person Ryan Haining; 09.06.2014
comment
ฉันเพิ่งเพิ่ม chain.from_iterable ไปยัง โปรเจ็กต์ c++ - person Ryan Haining; 09.06.2014

ฉันไม่พบตัวอย่างที่ถูกต้อง ... ซึ่งฉันสามารถเห็นความแตกต่างระหว่างพวกเขา [chain และ chain.from_iterable] และเหตุใดจึงเลือกตัวอย่างหนึ่งมากกว่าอีกตัวอย่างหนึ่ง

คำตอบที่ยอมรับนั้นละเอียดถี่ถ้วน สำหรับผู้ที่กำลังมองหาใบสมัครที่รวดเร็ว ลองพิจารณาแยกหลายรายการ:

list(itertools.chain(["a", "b", "c"], ["d", "e"], ["f"]))
# ['a', 'b', 'c', 'd', 'e', 'f']

คุณอาจต้องการใช้รายการเหล่านี้ซ้ำในภายหลัง เพื่อให้รายการสามารถทำซ้ำได้:

iterable = (["a", "b", "c"], ["d", "e"], ["f"])

ความพยายาม

อย่างไรก็ตาม การส่งผ่านแบบทำซ้ำได้ไปที่ chain จะให้ผลลัพธ์ที่ไม่เรียบ:

list(itertools.chain(iterable))
# [['a', 'b', 'c'], ['d', 'e'], ['f']]

ทำไม คุณผ่าน หนึ่ง รายการ (สิ่งอันดับ) chain ต้องการแต่ละรายการแยกกัน


โซลูชั่น

เมื่อเป็นไปได้ คุณสามารถแกะไฟล์ที่ทำซ้ำได้:

list(itertools.chain(*iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

list(itertools.chain(*iter(iterable)))
# ['a', 'b', 'c', 'd', 'e', 'f']

โดยทั่วไป ให้ใช้ .from_iterable (เนื่องจากใช้ได้กับตัววนซ้ำไม่จำกัด):

list(itertools.chain.from_iterable(iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

g = itertools.chain.from_iterable(itertools.cycle(iterable))
next(g)
# "a"
person pylang    schedule 17.07.2018

พวกเขาทำสิ่งที่คล้ายกันมาก สำหรับ iterable จำนวนเล็กน้อย itertools.chain(*iterables) และ itertools.chain.from_iterable(iterables) จะทำงานในลักษณะเดียวกัน

ข้อได้เปรียบที่สำคัญของ from_iterables อยู่ที่ความสามารถในการจัดการจำนวนการทำซ้ำจำนวนมาก (อาจไม่มีที่สิ้นสุด) เนื่องจากสิ่งเหล่านั้นทั้งหมดไม่จำเป็นต้องพร้อมใช้งานในขณะที่ทำการโทร

person BiGYaN    schedule 10.07.2017
comment
ไม่มีใครรู้ว่าตัวดำเนินการ * คลาย iterables อย่างเกียจคร้านหรือไม่ - person Rotareti; 25.03.2018
comment
@Rotareti ใช่มันแกะกล่องอย่างเกียจคร้าน (ทีละครั้ง) แต่ในกรณีนี้ itertools.chain(*iterables) เป็นการเรียกใช้ฟังก์ชัน ข้อโต้แย้งทั้งหมดจะต้องปรากฏ ณ เวลาที่โทร - person BiGYaN; 27.03.2018
comment
นี่เป็นเรื่องจริงเหรอ? จากโค้ด CPython ดูเหมือนว่าจะเป็น stackoverflow.com/a/62513808/610569 เดียวกัน - person alvas; 22.06.2020
comment
@alvas ลองเปลี่ยนจำนวนองค์ประกอบให้ใหญ่มาก ในช่วง 10,000 ถึง 1_000_000 และคุณจะเห็นว่า from_iterables เร็วขึ้น - person BiGYaN; 30.06.2020

อีกวิธีในการดู:

chain(iterable1, iterable2, iterable3, ...) ใช้สำหรับเมื่อคุณรู้แล้วว่าคุณมีความสามารถในการวนซ้ำอะไร ดังนั้นคุณจึงสามารถเขียนเป็นอาร์กิวเมนต์ที่คั่นด้วยเครื่องหมายจุลภาคได้

chain.from_iterable(iterable) ใช้สำหรับเมื่อได้รับ iterable ของคุณ (เช่น iterable1, iterable2, iterable3) มาจาก iterable อื่น

person R. Navega    schedule 20.08.2018

การขยาย @martijn-pieters answer

แม้ว่าการเข้าถึงรายการภายในในการวนซ้ำยังคงเหมือนเดิมและการนำไปปฏิบัติอย่างชาญฉลาด

  • itertools_chain_from_iterable (เช่น chain.from_iterable ใน Python) และ
  • chain_new (เช่น chain ใน Python)

ในการใช้งาน CPython นั้นเป็นทั้งประเภทเป็ดของ chain_new_internal


มีประโยชน์ในการเพิ่มประสิทธิภาพจากการใช้ chain.from_iterable(x) หรือไม่ โดยที่ x สามารถทำซ้ำได้ และจุดประสงค์หลักคือเพื่อใช้รายการสิ่งของที่ราบเรียบในที่สุด

เราสามารถลองเปรียบเทียบด้วย:

import random
from itertools import chain
from functools import wraps
from time import time

from tqdm import tqdm

def timing(f):
    @wraps(f)
    def wrap(*args, **kw):
        ts = time()
        result = f(*args, **kw)
        te = time()
        print('func:%r args:[%r, %r] took: %2.4f sec' % (f.__name__, args, kw, te-ts))
        return result
    return wrap

def generate_nm(m, n):
    # Creates m generators of m integers between range 0 to n.
    yield iter(random.sample(range(n), n) for _ in range(m))
    

def chain_star(x):
    # Stores an iterable that will unpack and flatten the list of list.
    chain_x = chain(*x)
    # Consumes the items in the flatten iterable.
    for i in chain_x:
        pass

def chain_from_iterable(x):
    # Stores an iterable that will unpack and flatten the list of list.
    chain_x = chain.from_iterable(x)
    # Consumes the items in the flatten iterable.
    for i in chain_x:
        pass


@timing
def versus(f, n, m):
  f(generate_nm(n, m))

P/S: เกณฑ์มาตรฐานกำลังทำงานอยู่... กำลังรอผลลัพธ์


ผลลัพธ์

chain_star, m=1,000, n=1,000

for _ in range(10):
    versus(chain_star, 1000, 1000)

[ออก]:

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6494 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6603 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6367 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6350 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6296 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6399 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6341 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6381 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6343 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6309 sec

chain_from_iterable, m=1,000, n=1,000

for _ in range(10):
    versus(chain_from_iterable, 1000, 1000)

[ออก]:

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6416 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6315 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6535 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6334 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6327 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6471 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6426 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6287 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6353 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6297 sec

chain_star, m=10,000, n=1,000

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2659 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2966 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2953 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3141 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2802 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2799 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2848 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3299 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2730 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3052 sec

chain_from_iterable, m=10,000, n=1,000

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3129 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3064 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3071 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2660 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2837 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2877 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2756 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2939 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2715 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2877 sec

chain_star, m=100000, n=1,000

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.7874 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.3744 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.5584 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.3745 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.7982 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.4054 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.6769 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.6476 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.7397 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.8980 sec

chain_from_iterable, m=100000, n=1,000

for _ in range(10):
    versus(chain_from_iterable, 100000, 1000)

[ออก]:

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7227 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7717 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7159 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7569 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7906 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.6211 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7294 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.8260 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.8356 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.9738 sec

chain_star, m=500000, n=1,000

for _ in range(3):
    versus(chain_from_iterable, 500000, 1000)

[ออก]:

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 314.5671 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 313.9270 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 313.8992 sec

chain_from_iterable, m=500000, n=1,000

for _ in range(3):
    versus(chain_from_iterable, 500000, 1000)

[ออก]:

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.8301 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.8104 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.9440 sec
person alvas    schedule 22.06.2020

วิธีดูอีกวิธีหนึ่งคือใช้ chain.from_iterable

เมื่อคุณมีการทำซ้ำได้เช่นการวนซ้ำแบบซ้อน (หรือแบบผสม iterbale) และใช้ลูกโซ่สำหรับการวนซ้ำแบบง่าย

person Chyanit Singh    schedule 20.02.2020