ฉันจะรู้ได้อย่างไรว่าตัวกำเนิดนั้นว่างเปล่าตั้งแต่เริ่มต้น?


146

มีวิธีที่ง่ายของการทดสอบถ้าเครื่องกำเนิดไฟฟ้าไม่มีรายการเช่นpeek, hasNext, isEmpty, สิ่งที่ตามเส้นเหล่านั้นหรือไม่


แก้ไขให้ฉันถ้าฉันผิด แต่ถ้าคุณสามารถแก้ปัญหาทั่วไปอย่างแท้จริงกับเครื่องกำเนิดไฟฟ้าใด ๆมันจะเทียบเท่ากับการตั้งค่าเบรกพอยต์ในงบผลตอบแทนและมีความสามารถในการ "ก้าวถอยหลัง" นั่นหมายความว่าการโคลนเฟรมสแต็กตามอัตราผลตอบแทนและการกู้คืนมันใน StopIteration

ฉันเดาเอาคืน StopIteration หรือไม่ แต่อย่างน้อย StopIteration จะบอกคุณว่ามันว่างเปล่า ใช่ฉันต้องนอนหลับ ...

4
ฉันคิดว่าฉันรู้ว่าทำไมเขาต้องการสิ่งนี้ หากคุณกำลังพัฒนาเว็บไซต์ด้วยแม่แบบและส่งค่าตอบแทนกลับไปยังเทมเพลตเช่นเสือชีตาห์หรืออะไรบางอย่างรายการที่ว่างเปล่า[]นั้นเป็นสิ่งที่สะดวกมาก Falsey เพื่อให้คุณสามารถตรวจสอบได้ว่าทำอะไรและทำอะไรเป็นพิเศษ เครื่องกำเนิดไฟฟ้าจะเป็นจริงแม้ว่าจะไม่มีองค์ประกอบก็ตาม
jpsimons

นี่คือกรณีการใช้งานของฉัน ... ฉันใช้glob.iglob("filepattern")รูปแบบไวด์การ์ดที่ผู้ใช้เป็นผู้จัดหาและฉันต้องการเตือนผู้ใช้หากรูปแบบไม่ตรงกับไฟล์ใด ๆ แน่นอนว่าฉันสามารถหลีกเลี่ยงปัญหานี้ได้หลายวิธี แต่มีประโยชน์ที่จะสามารถทดสอบได้อย่างหมดจดว่าตัววนซ้ำว่างเปล่าหรือไม่
LarsH

อาจใช้โซลูชันนี้: stackoverflow.com/a/11467686/463758
balki

คำตอบ:


53

คำตอบที่ง่ายสำหรับคำถามของคุณ: ไม่ไม่มีวิธีง่ายๆ มีจำนวนมากรอบการทำงาน

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

คุณสามารถเขียนฟังก์ชั่น has_next หรืออาจตบมันไปที่เครื่องกำเนิดไฟฟ้าเป็นวิธีที่มีมัณฑนากรแฟนซีหากคุณต้องการ


2
ยุติธรรมมากพอสมควร ฉันรู้ว่าไม่มีทางที่จะค้นหาความยาวของเครื่องกำเนิดไฟฟ้าได้ แต่คิดว่าฉันอาจพลาดวิธีการค้นหาหากเริ่มสร้างสิ่งใด ๆ เลย
Dan

1
โอ้และสำหรับการอ้างอิงฉันลองใช้คำแนะนำ "แฟนซีมัณฑนากร" ของตัวเอง HARD เห็นได้ชัดว่า copy.deepcopy ไม่ทำงานกับเครื่องกำเนิดไฟฟ้า
David Berger

47
ฉันไม่แน่ใจว่าฉันสามารถเห็นด้วยกับ "ไม่ควรมีวิธีง่ายๆ" มี abstractions จำนวนมากในวิทยาการคอมพิวเตอร์ที่ออกแบบมาเพื่อส่งออกลำดับของค่าโดยไม่ต้องถือลำดับในหน่วยความจำ แต่อนุญาตให้โปรแกรมเมอร์ถามว่ามีค่าอื่นโดยไม่ลบออกจาก "คิว" ถ้ามี มีสิ่งเช่นเดียวกับการแอบมองไปข้างหน้าโดยไม่ต้องใช้ "การเดินทางย้อนกลับ" ไม่ได้หมายความว่าการออกแบบตัววนซ้ำต้องให้ฟีเจอร์ดังกล่าว แต่แน่ใจว่ามีประโยชน์ บางทีคุณอาจคัดค้านโดยที่ค่าแรกอาจเปลี่ยนไปหลังจากมอง
LarsH

9
ฉันกำลังคัดค้านเนื่องจากการใช้งานทั่วไปไม่ได้คำนวณค่าจนกว่าจะจำเป็น หนึ่งสามารถบังคับให้อินเทอร์เฟซการทำเช่นนี้ แต่ที่อาจย่อยที่ดีที่สุดสำหรับการใช้งานที่มีน้ำหนักเบา
David Berger

6
@ S.Lott คุณไม่จำเป็นต้องสร้างทั้งลำดับเพื่อทราบว่าลำดับนั้นว่างเปล่าหรือไม่ พื้นที่เก็บข้อมูลหนึ่งองค์ประกอบนั้นเพียงพอ - ดูคำตอบของฉัน
Mark Ransom

99

คำแนะนำ:

def peek(iterable):
    try:
        first = next(iterable)
    except StopIteration:
        return None
    return first, itertools.chain([first], iterable)

การใช้งาน:

res = peek(mysequence)
if res is None:
    # sequence is empty.  Do stuff.
else:
    first, mysequence = res
    # Do something with first, maybe?
    # Then iterate over the sequence:
    for element in mysequence:
        # etc.

2
return first, itertools.chain([first], rest)ฉันไม่ได้ค่อนข้างได้รับจุดกลับองค์ประกอบแรกครั้งที่สองใน
njzk2

6
@ njzk2 ฉันกำลังจะไปที่การดำเนินการ "peek" (ชื่อฟังก์ชั่น) wiki "peek เป็นการดำเนินการที่ส่งคืนค่าสูงสุดของคอลเลกชันโดยไม่ลบค่าออกจากข้อมูล"
John Fouhy

สิ่งนี้จะไม่ทำงานหากเครื่องกำเนิดไฟฟ้าถูกออกแบบมาเพื่อให้ไม่มี def gen(): for pony in range(4): yield None if pony == 2 else pony
พอล

4
@Paul ดูค่าตอบแทนอย่างใกล้ชิด หากเครื่องกำเนิดไฟฟ้าจะทำ - คือไม่กลับมาNoneแต่การเลี้ยงStopIteration- Noneผลของการทำงานคือ มิฉะนั้นจะเป็น tuple Noneซึ่งไม่ได้เป็น
คดีของกองทุนโมนิกา

สิ่งนี้ช่วยฉันได้มากกับโครงการปัจจุบันของฉัน ฉันพบตัวอย่างที่คล้ายกันในรหัสสำหรับโมดูลไลบรารีมาตรฐานของงูใหญ่ 'mailbox.py' This method is for backward compatibility only. def next(self): """Return the next message in a one-time iteration.""" if not hasattr(self, '_onetime_keys'): self._onetime_keys = self.iterkeys() while True: try: return self[next(self._onetime_keys)] except StopIteration: return None except KeyError: continue
เพียร์

29

วิธีง่ายๆคือการใช้พารามิเตอร์ที่เป็นทางเลือกสำหรับnext ()ซึ่งจะใช้หากเครื่องกำเนิดไฟฟ้าหมด (หรือว่างเปล่า) ตัวอย่างเช่น:

iterable = some_generator()

_exhausted = object()

if next(iterable, _exhausted) == _exhausted:
    print('generator is empty')

แก้ไข: แก้ไขปัญหาที่ชี้ให้เห็นในความคิดเห็นของ mehtunguh


1
ไม่ได้สิ่งนี้ไม่ถูกต้องสำหรับตัวสร้างใด ๆ ที่ค่าแรกที่ให้นั้นไม่จริง
mehtunguh

7
ใช้object()แทนclassการทำให้สั้นลงหนึ่งบรรทัด: _exhausted = object(); if next(iterable, _exhausted) is _exhausted:
Messa

13

next(generator, None) is not None

หรือแทนที่Noneแต่สิ่งที่มีค่าที่คุณรู้ว่ามันไม่ได้อยู่ในเครื่องกำเนิดของคุณ

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

def foo(self):
    if next(self.my_generator(), None) is None:
        raise Exception("Not initiated")

    for x in self.my_generator():
        ...

นั่นคืองานนี้ถ้าคุณกำเนิดมาจากฟังก์ชั่นgenerator()ในขณะที่


4
ทำไมนี่ไม่ใช่คำตอบที่ดีที่สุด? ในกรณีที่กำเนิดกลับNone?
Sait

8
อาจเป็นเพราะสิ่งนี้บังคับให้คุณบริโภคเครื่องกำเนิดไฟฟ้าแทนการทดสอบว่ามันว่างเปล่าหรือเปล่า
bfontaine

3
มันไม่ดีเพราะช่วงเวลาที่คุณโทรหาคนถัดไป (ตัวสร้างไม่มี) คุณจะข้าม 1 รายการถ้ามันใช้ได้
นาธานทำ

ถูกต้องคุณจะพลาดองค์ประกอบที่ 1 ของ gen ของคุณและคุณจะต้องกิน gen ของคุณแทนที่จะทำการทดสอบ
AJ

12

วิธีที่ดีที่สุด IMHO คือหลีกเลี่ยงการทดสอบพิเศษ เวลาส่วนใหญ่การใช้ตัวสร้างคือการทดสอบ:

thing_generated = False

# Nothing is lost here. if nothing is generated, 
# the for block is not executed. Often, that's the only check
# you need to do. This can be done in the course of doing
# the work you wanted to do anyway on the generated output.
for thing in my_generator():
    thing_generated = True
    do_work(thing)

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

if not thing_generated:
    print "Avast, ye scurvy dog!"

3
โซลูชันนี้จะพยายามใช้ตัวสร้างทั้งหมดซึ่งทำให้ไม่สามารถใช้กับเครื่องกำเนิดไฟฟ้าที่ไม่มีที่สิ้นสุด
Viktor Stískala

@ ViktorStískala: ฉันไม่เห็นจุดของคุณ มันคงเป็นเรื่องโง่ที่จะทดสอบว่าตัวสร้างอนันต์สร้างผลลัพธ์ใด ๆ
vezult

ฉันต้องการชี้ให้เห็นว่าโซลูชันของคุณอาจมีการหยุดพักการวนลูปเนื่องจากคุณไม่ได้ประมวลผลผลลัพธ์อื่นและไม่มีประโยชน์ที่จะสร้างขึ้น range(10000000)เป็นตัวกำเนิด จำกัด (Python 3) แต่คุณไม่จำเป็นต้องผ่านรายการทั้งหมดเพื่อดูว่ามันสร้างอะไรขึ้นมาหรือไม่
Viktor Stískala

1
@ ViktorStískala: เข้าใจ อย่างไรก็ตามจุดของฉันคือ: โดยทั่วไปคุณต้องการทำงานกับตัวสร้างเอาต์พุต ในตัวอย่างของฉันหากไม่มีสิ่งใดถูกสร้างขึ้นมาตอนนี้คุณก็รู้แล้ว มิฉะนั้นคุณทำงานกับผลลัพธ์ที่สร้างขึ้นตามที่ต้องการ - "การใช้เครื่องกำเนิดไฟฟ้าคือการทดสอบ" ไม่จำเป็นต้องมีการทดสอบพิเศษหรือใช้งานเอาต์พุตของเครื่องกำเนิดไฟฟ้าอย่างไม่มีจุดหมาย ฉันได้แก้ไขคำตอบของฉันเพื่อชี้แจงเรื่องนี้
vezult

8

ฉันเกลียดที่จะเสนอทางออกที่สองโดยเฉพาะอย่างยิ่งที่ฉันจะไม่ใช้ตัวเอง แต่ถ้าคุณต้องทำอย่างนี้และไม่กินเครื่องกำเนิดไฟฟ้าเช่นเดียวกับคำตอบอื่น ๆ :

def do_something_with_item(item):
    print item

empty_marker = object()

try:
     first_item = my_generator.next()     
except StopIteration:
     print 'The generator was empty'
     first_item = empty_marker

if first_item is not empty_marker:
    do_something_with_item(first_item)
    for item in my_generator:
        do_something_with_item(item)

ตอนนี้ฉันไม่ชอบวิธีนี้เพราะฉันเชื่อว่านี่ไม่ใช่วิธีการใช้เครื่องกำเนิดไฟฟ้า


4

ฉันตระหนักว่าโพสต์นี้มีอายุ 5 ปีแล้ว แต่ฉันพบว่าขณะมองหาวิธีการทำสิ่งนี้ที่เป็นสำนวนและไม่เห็นวิธีแก้ปัญหาของฉันที่โพสต์ ดังนั้นสำหรับลูกหลาน:

import itertools

def get_generator():
    """
    Returns (bool, generator) where bool is true iff the generator is not empty.
    """
    gen = (i for i in [0, 1, 2, 3, 4])
    a, b = itertools.tee(gen)
    try:
        a.next()
    except StopIteration:
        return (False, b)
    return (True, b)

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


1
การดำเนินการนี้จะเรียกตัวgenสร้างเพียงครั้งเดียวสำหรับแต่ละรายการดังนั้นผลข้างเคียงจึงไม่เป็นปัญหา แต่มันจะเก็บสำเนาของทุกสิ่งที่ถูกดึงออกมาจากเครื่องกำเนิดไฟฟ้าผ่านbแต่ไม่ใช่ผ่านaดังนั้นความหมายของหน่วยความจำจะคล้ายกับที่เพิ่งทำงานlist(gen)และตรวจสอบว่า
Matthias Fripp

มันมีสองประเด็น 1. itertool นี้อาจต้องการที่เก็บข้อมูลสำรองที่สำคัญ (ขึ้นอยู่กับจำนวนข้อมูลชั่วคราวที่ต้องจัดเก็บ) โดยทั่วไปหากตัววนซ้ำหนึ่งตัวใช้ข้อมูลส่วนใหญ่หรือทั้งหมดก่อนที่ตัววนซ้ำตัวอื่นจะเริ่มทำงานจะเร็วกว่าที่จะใช้ list () แทน tee () 2. ตัววนซ้ำทีไม่ใช่ threadsafe RuntimeError อาจเพิ่มขึ้นเมื่อใช้ตัววนซ้ำพร้อมกันที่ส่งคืนโดยการเรียก tee () เดียวกันแม้ว่าการทำซ้ำแบบดั้งเดิมนั้นจะเป็น threadsafe ก็ตาม
AJ

3

ขออภัยสำหรับวิธีการที่ชัดเจน แต่วิธีที่ดีที่สุดควรทำ:

for item in my_generator:
     print item

ตอนนี้คุณตรวจพบแล้วว่าตัวกำเนิดนั้นว่างเปล่าในขณะที่คุณกำลังใช้งาน แน่นอนว่ารายการจะไม่ปรากฏหากตัวกำเนิดนั้นว่างเปล่า

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


หรือ ... ผู้ถามอาจให้คำใบ้ว่าทำไมคนเราถึงพยายามตรวจหาเครื่องกำเนิดไฟฟ้าที่ว่างเปล่า?
S.Lott

คุณหมายถึง "ไม่มีอะไรจะแสดงเนื่องจากตัวกำเนิดว่างเปล่า"
SilentGhost

S.Lott ฉันเห็นด้วย. ฉันไม่เห็นสาเหตุ แต่ฉันคิดว่าแม้ว่าจะมีเหตุผลปัญหาอาจจะดีกว่าหันไปใช้แต่ละรายการแทน
Ali Afshar

1
สิ่งนี้ไม่ได้บอกโปรแกรมว่าตัวกำเนิดนั้นว่างเปล่า
Ethan Furman

3

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

นี่คือคลาสของ wrapper ที่สามารถเพิ่มลงในตัววนซ้ำที่มีอยู่เพื่อเพิ่มการ__nonzero__ทดสอบดังนั้นคุณสามารถดูว่าตัวกำเนิดนั้นว่างเปล่าด้วยifวิ มันอาจกลายเป็นมัณฑนากรได้

class GenWrapper:
    def __init__(self, iter):
        self.source = iter
        self.stored = False

    def __iter__(self):
        return self

    def __nonzero__(self):
        if self.stored:
            return True
        try:
            self.value = next(self.source)
            self.stored = True
        except StopIteration:
            return False
        return True

    def __next__(self):  # use "next" (without underscores) for Python 2.x
        if self.stored:
            self.stored = False
            return self.value
        return next(self.source)

นี่คือวิธีที่คุณจะใช้:

with open(filename, 'r') as f:
    f = GenWrapper(f)
    if f:
        print 'Not empty'
    else:
        print 'Empty'

โปรดทราบว่าคุณสามารถตรวจสอบความว่างเปล่าได้ตลอดเวลาไม่เพียงแค่เริ่มการทำซ้ำ


นี่คือทิศทางที่ถูกต้อง ควรแก้ไขเพื่อให้มองไปข้างหน้าเท่าที่คุณต้องการเก็บผลลัพธ์ได้มากเท่าที่ต้องการ เป็นการดีที่มันจะช่วยให้การผลักดันรายการโดยพลการบนหัวของกระแส pusher-iterator เป็นนามธรรมที่มีประโยชน์มากที่ฉันมักจะใช้
sfkleach

@sfkleach ฉันไม่เห็นความจำเป็นที่จะต้องทำให้ซับซ้อนสำหรับการมองไปข้างหน้าหลายครั้งมันค่อนข้างมีประโยชน์เหมือนที่เป็นอยู่และตอบคำถาม แม้ว่านี่จะเป็นคำถามเก่า แต่ก็ยังได้รับการมองเป็นครั้งคราวดังนั้นหากคุณต้องการออกจากคำตอบของคุณเองบางคนอาจพบว่ามีประโยชน์
Mark Ransom

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

2

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

class Pushable:

    def __init__(self, iter):
        self.source = iter
        self.stored = []

    def __iter__(self):
        return self

    def __bool__(self):
        if self.stored:
            return True
        try:
            self.stored.append(next(self.source))
        except StopIteration:
            return False
        return True

    def push(self, value):
        self.stored.append(value)

    def peek(self):
        if self.stored:
            return self.stored[-1]
        value = next(self.source)
        self.stored.append(value)
        return value

    def __next__(self):
        if self.stored:
            return self.stored.pop()
        return next(self.source)

2

เพิ่งตกไปที่หัวข้อนี้และรู้ว่าคำตอบที่ง่ายและอ่านง่ายหายไป:

def is_empty(generator):
    for item in generator:
        return False
    return True

หากเราไม่ควรบริโภครายการใด ๆ เราจำเป็นต้องฉีดรายการแรกเข้าไปในเครื่องกำเนิดไฟฟ้า:

def is_empty_no_side_effects(generator):
    try:
        item = next(generator)
        def my_generator():
            yield item
            yield from generator
        return my_generator(), False
    except StopIteration:
        return (_ for _ in []), True

ตัวอย่าง:

>>> g=(i for i in [])
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
True
>>> g=(i for i in range(10))
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
False
>>> list(g)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

1
>>> gen = (i for i in [])
>>> next(gen)
Traceback (most recent call last):
  File "<pyshell#43>", line 1, in <module>
    next(gen)
StopIteration

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

สิ่งอื่นที่คุณสามารถทำได้คือ:

>>> gen = (i for i in [])
>>> if not list(gen):
    print('empty generator')

2
ซึ่งจริง ๆ แล้วใช้ตัวกำเนิดทั้งหมด น่าเศร้าที่มันไม่ชัดเจนจากคำถามว่านี่เป็นพฤติกรรมที่พึงประสงค์หรือไม่พึงปรารถนา
S.Lott

เช่นเดียวกับวิธีอื่นในการกำเนิด "แตะ" ฉันคิดว่า
SilentGhost

ฉันรู้นี้เป็นรุ่นเก่า แต่ใช้ 'รายการ ()' ไม่สามารถเป็นวิธีที่ดีที่สุดถ้ารายการที่สร้างไม่ว่างเปล่า แต่ในความเป็นจริงที่มีขนาดใหญ่แล้วนี่คือสิ้นเปลืองไม่มีความจำเป็น
Chris_Rands

1

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

was_empty = True

for some_item in some_generator:
    was_empty = False
    do_something_with(some_item)

if was_empty:
    handle_already_empty_generator_case()

1

เพียงแค่สร้างเครื่องกำเนิดไฟฟ้าด้วยitertools.chainใส่สิ่งที่จะแสดงจุดสิ้นสุดของ iterable เป็น iterable ตัวที่สองจากนั้นเพียงตรวจสอบสิ่งนั้น

Ex:

import itertools

g = some_iterable
eog = object()
wrap_g = itertools.chain(g, [eog])

ตอนนี้สิ่งที่เหลืออยู่คือการตรวจสอบค่าที่เราผนวกเข้ากับส่วนท้ายของ iterable เมื่อคุณอ่านแล้วมันจะหมายถึงจุดสิ้นสุด

for value in wrap_g:
    if value == eog: # DING DING! We just found the last element of the iterable
        pass # Do something

ใช้eog = object()แทนการสมมติว่าfloat('-inf')จะไม่เกิดขึ้นใน iterable
bfontaine

@bfontaine ความคิดที่ดี
smac89

1

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

ความหมาย:

def has_items(iterable):
    try:
        return True, itertools.chain([next(iterable)], iterable)
    except StopIteration:
        return False, []

การใช้งาน:

def filter_empty(iterables):
    for iterable in iterables:
        itr_has_items, iterable = has_items(iterable)
        if itr_has_items:
            yield iterable


def merge_iterables(iterables):
    populated_iterables = filter_empty(iterables)
    for items in zip(*populated_iterables):
        # Use items for each "slice"

ปัญหาเฉพาะของฉันมีคุณสมบัติที่ iterables ว่างเปล่าหรือมีจำนวนรายการเท่ากันทุกประการ


1

ฉันพบโซลูชันนี้เฉพาะสำหรับการทำซ้ำที่ว่างเปล่าเช่นกัน

def is_generator_empty(generator):
    a, b = itertools.tee(generator)
    try:
        next(a)
    except StopIteration:
        return True, b
    return False, b

is_empty, generator = is_generator_empty(generator)

หรือถ้าคุณไม่ต้องการใช้ข้อยกเว้นสำหรับการลองใช้งานนี้

def is_generator_empty(generator):
    a, b = itertools.tee(generator)
    for item in a:
        return False, b
    return True, b

is_empty, generator = is_generator_empty(generator)

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

def get_empty_generator():
    while False:
        yield None 

generator = get_empty_generator()


0

นี่คือวิธีการง่ายๆของฉันที่ฉันใช้เพื่อส่งคืนตัววนซ้ำในขณะที่ตรวจสอบว่ามีบางสิ่งที่ให้ผลฉันเพียงตรวจสอบว่าลูปรันหรือไม่:

        n = 0
        for key, value in iterator:
            n+=1
            yield key, value
        if n == 0:
            print ("nothing found in iterator)
            break

0

นี่คือมัณฑนากรง่ายๆที่ล้อมรอบตัวสร้างดังนั้นจึงส่งคืน None ถ้าไม่มี สิ่งนี้จะมีประโยชน์หากรหัสของคุณจำเป็นต้องรู้ว่าตัวสร้างจะสร้างอะไรก่อนที่จะวนซ้ำมันหรือไม่

def generator_or_none(func):
    """Wrap a generator function, returning None if it's empty. """

    def inner(*args, **kwargs):
        # peek at the first item; return None if it doesn't exist
        try:
            next(func(*args, **kwargs))
        except StopIteration:
            return None

        # return original generator otherwise first item will be missing
        return func(*args, **kwargs)

    return inner

การใช้งาน:

import random

@generator_or_none
def random_length_generator():
    for i in range(random.randint(0, 10)):
        yield i

gen = random_length_generator()
if gen is None:
    print('Generator is empty')

ตัวอย่างหนึ่งที่สิ่งนี้มีประโยชน์คือในรหัสการสร้างเทมเพลต - เช่น jinja2

{% if content_generator %}
  <section>
    <h4>Section title</h4>
    {% for item in content_generator %}
      {{ item }}
    {% endfor %
  </section>
{% endif %}

สิ่งนี้เรียกฟังก์ชันตัวสร้างขึ้นสองครั้งดังนั้นจะต้องเสียค่าใช้จ่ายเริ่มต้นของตัวสร้างสองครั้ง นั่นอาจเป็นสิ่งสำคัญหากตัวอย่างเช่นฟังก์ชันตัวสร้างเป็นแบบสอบถามฐานข้อมูล
เอียนโกลด์บาย

0

ใช้ islice คุณต้องตรวจสอบซ้ำครั้งแรกเพื่อดูว่าว่างหรือไม่

จาก itertools นำเข้า islice

def isempty (iterable):
    รายการส่งคืน (islice (iterable, 1)) == []


ขออภัยนี่คือการอ่านที่สิ้นเปลือง ... ต้องลอง / จับด้วย StopIteration
Quin

0

แล้วการใช้ใด ๆ () ฉันใช้กับเครื่องกำเนิดไฟฟ้าและมันก็ใช้ได้ดีที่นี่มีคนอธิบายเล็ก ๆ น้อย ๆ เกี่ยวกับเรื่องนี้


2
เราไม่สามารถใช้ "any ()" สำหรับเครื่องกำเนิดไฟฟ้าทุกอย่าง เพิ่งลองใช้กับเครื่องกำเนิดไฟฟ้าที่มีหลายดาต้าไฟล์ ฉันได้รับข้อความนี้ "ค่าความจริงของ DataFrame นั้นคลุมเครือ" ในทุก ๆ (my_generator_of_df)
probitaille

any(generator)ทำงานเมื่อคุณรู้ว่าตัวกำเนิดจะสร้างค่าที่สามารถส่งไปยังbool- ประเภทข้อมูลพื้นฐาน (เช่น int, สตริง) งาน any(generator)จะเป็นเท็จเมื่อเครื่องกำเนิดไฟฟ้าว่างเปล่าหรือเมื่อเครื่องกำเนิดไฟฟ้ามีค่าเท็จเท่านั้นตัวอย่างเช่นหากเครื่องกำเนิดไฟฟ้าจะสร้าง 0, '' (สตริงว่าง) และเท็จแล้วมันจะยังคงเป็นเท็จ นี้อาจหรือไม่อาจจะเป็นพฤติกรรมตั้งใจเพียงเพื่อให้นานที่สุดเท่าที่คุณจะตระหนักถึงมัน :)
แดเนียล

0

ใช้แอบฟังก์ชั่นใน cytoolz

from cytoolz import peek
from typing import Tuple, Iterable

def is_empty_iterator(g: Iterable) -> Tuple[Iterable, bool]:
    try:
        _, g = peek(g)
        return g, False
    except StopIteration:
        return g, True

ตัววนซ้ำที่ส่งคืนโดยฟังก์ชันนี้จะเทียบเท่ากับต้นฉบับที่ส่งผ่านเป็นอาร์กิวเมนต์


-2

ฉันแก้ไขมันโดยใช้ฟังก์ชั่นผลรวม ดูตัวอย่างด้านล่างที่ฉันใช้กับ glob.iglob (ซึ่งส่งคืนตัวกำเนิด)

def isEmpty():
    files = glob.iglob(search)
    if sum(1 for _ in files):
        return True
    return False

* สิ่งนี้อาจไม่ทำงานสำหรับเครื่องกำเนิดไฟฟ้าขนาดใหญ่ แต่ควรทำงานได้ดีกับรายการขนาดเล็ก

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