ใน Python ฉันจะทราบได้อย่างไรว่าวัตถุนั้นสามารถทำซ้ำได้?


1083

มีวิธีการisiterableอย่างไร ทางออกเดียวที่ฉันได้พบคือการโทร

hasattr(myObj, '__iter__')

แต่ฉันไม่แน่ใจว่านี่เป็นวิธีพิสูจน์คนโง่ได้อย่างไร


18
__getitem__ก็เพียงพอที่จะทำให้วัตถุ iterable
Kos

4
FWIW: iter(myObj)สำเร็จถ้าisinstance(myObj, dict)ดังนั้นถ้าคุณดูที่myObjอาจเป็นลำดับของdicts หรือเดี่ยวdictคุณจะประสบความสำเร็จในทั้งสองกรณี ความละเอียดอ่อนที่มีความสำคัญหากคุณต้องการทราบว่าลำดับคืออะไรและไม่ใช่อะไร (ใน Python 2)
Ben Mosher

7
__getitem__นอกจากนี้ยังเพียงพอที่จะทำให้วัตถุ iterable ... ถ้ามันเริ่มต้นที่ดัชนี zero
Carlos A. Gómez

คำตอบ:


26

ฉันได้ศึกษาปัญหานี้มาไม่นาน จากข้อสรุปของฉันคือทุกวันนี้นี่เป็นวิธีที่ดีที่สุด:

from collections.abc import Iterable   # drop `.abc` with Python 2.7 or lower

def iterable(obj):
    return isinstance(obj, Iterable)

ได้รับการแนะนำข้างต้นก่อนหน้านี้ แต่ฉันทามติทั่วไปว่าการใช้iter()จะดีกว่า:

def iterable(obj):
    try:
        iter(obj)
    except Exception:
        return False
    else:
        return True

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

>>> from faker import Faker
>>> fake = Faker()
>>> iter(fake)    # No exception, must be iterable
<iterator object at 0x7f1c71db58d0>
>>> list(fake)    # Ooops
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/.../site-packages/faker/proxy.py", line 59, in __getitem__
    return self._factory_map[locale.replace('-', '_')]
AttributeError: 'int' object has no attribute 'replace'

หากเราต้องการใช้insinstance()เราจะไม่พิจารณาอินสแตนซ์ของ Faker โดยบังเอิญ (หรือวัตถุอื่นใดที่มีเท่านั้น__getitem__) ให้ทำซ้ำได้:

>>> from collections.abc import Iterable
>>> from faker import Faker
>>> isinstance(Faker(), Iterable)
False

คำตอบก่อนหน้านี้แสดงความคิดเห็นว่าการใช้iter()นั้นปลอดภัยกว่าเพราะวิธีเก่า ๆ ในการใช้การวนซ้ำใน Python นั้นขึ้นอยู่__getitem__กับisinstance()วิธีการที่ตรวจไม่พบ นี่อาจเป็นจริงกับ Python เวอร์ชันเก่า แต่จากการทดสอบที่ละเอียดถี่ถ้วนของฉันก็ใช้isinstance()งานได้ดีในทุกวันนี้ กรณีเดียวที่isinstance()ไม่ได้ทำงาน แต่iter()ทำมาพร้อมกับUserDictเมื่อใช้ Python 2 ถ้ามันเกี่ยวข้องกันคุณสามารถใช้isinstance(item, (Iterable, UserDict))เพื่อให้ครอบคลุมได้


1
นอกจากนี้ยังtyping.Dictมีการพิจารณาโดย iterable iter(Dict)แต่ด้วยข้อผิดพลาดlist(Dict) TypeError: Parameters to generic types must be types. Got 0.ตามที่คาดหวังisinstance(Dict, Iterable)ผลตอบแทนที่ผิดพลาด
Pekka Klärck

1
ฉันมาถึงข้อสรุปเดียวกัน แต่ด้วยเหตุผลที่แตกต่างกัน การใช้iterทำให้บางรหัสของเราที่ใช้ "แคชล่วงหน้า" ช้าลงโดยไม่จำเป็น หาก__iter__รหัสช้าดังนั้นจะโทรiter... เมื่อใดก็ตามที่คุณต้องการดูว่ามีบางอย่างที่ทำซ้ำได้หรือไม่
thorwhalen

842
  1. การตรวจสอบการ__iter__ทำงานในประเภทลำดับ แต่มันจะล้มเหลวในเช่นสตริงในหลาม 2 ฉันต้องการทราบคำตอบที่ถูกต้องเช่นกันจนกระทั่งตอนนี้นี่เป็นความเป็นไปได้หนึ่งอย่าง (ซึ่งจะใช้ได้กับสายอักขระ):

    from __future__ import print_function
    
    try:
        some_object_iterator = iter(some_object)
    except TypeError as te:
        print(some_object, 'is not iterable')

    การiterตรวจสอบบิวด์อินสำหรับ__iter__เมธอดหรือในกรณีของสตริง__getitem__เมธอด

  2. อีกวิธีหนึ่งคือการสมมติว่ามันเป็น pythonic จากนั้นก็ล้มเหลวอย่างสง่างามถ้ามันไม่ทำงานบนวัตถุที่กำหนด คำศัพท์ Python:

    รูปแบบการเขียนโปรแกรม Pythonic ที่กำหนดประเภทของวัตถุโดยการตรวจสอบวิธีการหรือคุณลักษณะของลายเซ็นมากกว่าโดยความสัมพันธ์ที่ชัดเจนกับวัตถุประเภทบางอย่าง ("ถ้ามันดูเหมือนเป็ดและ quacks เหมือนเป็ดมันจะต้องเป็นเป็ด ") โดยเน้นกิจกรรม มากกว่าประเภทเฉพาะรหัสที่ออกแบบมาอย่างดีจะช่วยเพิ่มความยืดหยุ่นโดยอนุญาตให้มีการแทนที่ polymorphic การพิมพ์เป็ดหลีกเลี่ยงการทดสอบโดยใช้ type () หรือ isinstance ()แต่โดยทั่วไปจะใช้รูปแบบการเขียนโปรแกรม EAFP (ง่ายกว่าที่จะขอการให้อภัยมากกว่าการอนุญาต)

    ...

    try:
       _ = (e for e in my_object)
    except TypeError:
       print my_object, 'is not iterable'
  3. collectionsโมดูลมีบางส่วนฐานเรียนนามธรรมซึ่งอนุญาตให้มีการถามในชั้นเรียนหรือกรณีถ้าพวกเขาให้การทำงานโดยเฉพาะอย่างยิ่งตัวอย่างเช่น:

    from collections.abc import Iterable
    
    if isinstance(e, Iterable):
        # e is iterable

    อย่างไรก็ตามสิ่งนี้ไม่ได้ตรวจสอบคลาสที่ทำซ้ำ__getitem__ได้


34
[e for e in my_object]สามารถยกข้อยกเว้นด้วยเหตุผลอื่น ๆ เช่นmy_objectไม่ได้กำหนดหรือข้อบกพร่องที่เป็นไปได้ในmy_objectการดำเนินการ
Nick Dandoulakis

37
สตริงเป็นลำดับ ( isinstance('', Sequence) == True) และเป็นลำดับใด ๆ ก็คือ iterable ( isinstance('', Iterable)) แม้ว่าhasattr('', '__iter__') == Falseและอาจจะสับสน
jfs

82
หากmy_objectมีขนาดใหญ่มาก (เช่นไม่มีที่สิ้นสุดเช่นitertools.count()) ความเข้าใจในรายการของคุณจะใช้เวลา / หน่วยความจำมาก ดีกว่าที่จะสร้างเครื่องกำเนิดไฟฟ้าซึ่งจะไม่พยายามสร้างรายการ (อาจไม่มีที่สิ้นสุด)
คริสลัทซ์

14
จะเกิดอะไรขึ้นถ้าsome_objectพ่น TypeError ที่เกิดจากเหตุผลอื่น (บั๊ก ฯลฯ ) ด้วย? เราจะบอกได้อย่างไรจาก "Noterable TypeError"
Shaung

54
โปรดทราบว่าใน Python 3: hasattr(u"hello", '__iter__')ส่งคืนTrue
Carlos

572

พิมพ์เป็ด

try:
    iterator = iter(theElement)
except TypeError:
    # not iterable
else:
    # iterable

# for obj in iterator:
#     pass

การตรวจสอบประเภท

ใช้การเรียนบทคัดย่อฐาน พวกเขาต้องการอย่างน้อย Python 2.6 และทำงานสำหรับคลาสสไตล์ใหม่เท่านั้น

from collections.abc import Iterable   # import directly from collections for Python < 3.3

if isinstance(theElement, Iterable):
    # iterable
else:
    # not iterable

อย่างไรก็ตามiter()มีความน่าเชื่อถือมากกว่านี้เล็กน้อยตามที่อธิบายไว้ในเอกสาร :

การตรวจสอบตรวจisinstance(obj, Iterable)พบคลาสที่ลงทะเบียนเป็น Iterable หรือมี__iter__()เมธอด แต่ไม่พบคลาสที่วนซ้ำกับ__getitem__() เมธอด วิธีที่เชื่อถือได้เท่านั้นที่จะตรวจสอบว่าวัตถุ iterable iter(obj)คือการโทร


18
จาก "Fluent Python" โดย Luciano Ramalho: ตั้งแต่ Python 3.4 วิธีที่แม่นยำที่สุดในการตรวจสอบว่าอ็อบเจกต์ x นั้นสามารถทำซ้ำได้คือการเรียก iter (x) และจัดการกับข้อยกเว้น TypeError หากไม่ใช่ สิ่งนี้มีความแม่นยำมากกว่าการใช้ isinstance (x, abc.Iterable) เนื่องจาก iter (x) พิจารณาวิธีgetitemดั้งเดิมด้วยในขณะที่ ABC แบบ Iterable ไม่
RdB

ในกรณีที่คุณกำลังความคิด "โอ้ฉันจะเป็นเพียงแค่isinstance(x, (collections.Iterable, collections.Sequence))แทนiter(x)" ทราบว่าเรื่องนี้จะยังคงตรวจไม่พบวัตถุ iterable ว่าการดำเนินการเพียงแต่ไม่__getitem__ __len__ใช้iter(x)และตรวจจับข้อยกเว้น
Dale

คำตอบที่สองของคุณไม่ทำงาน ใน PyUNO ถ้าฉันทำiter(slide1)ทุกอย่างไป แต่พ่นisinstance(slide1, Iterable) TypeError: issubclass() arg 1 must be a class
Hi-Angel

@ Hi-แองเจิลเสียงเหมือนข้อผิดพลาดในPyUNOสังเกตว่าข้อผิดพลาดของคุณบอกว่าแทนissubclass() isinstance()
Georg Schölly

2
การเรียกใช้ iter () เหนือวัตถุอาจเป็นการดำเนินการที่มีราคาแพง (ดู DataLoader ใน Pytorch ซึ่ง forks / วางไข่กระบวนการหลายกระบวนการใน iter ())
szali

126

ฉันต้องการที่จะหลั่งน้อยบิตแสงเพิ่มเติมเกี่ยวกับอิทธิพลซึ่งกันและกันของiter, __iter__และ__getitem__และสิ่งที่เกิดขึ้นหลังม่าน ด้วยความรู้นั้นคุณจะสามารถเข้าใจได้ว่าทำไมสิ่งที่ดีที่สุดที่คุณสามารถทำได้คือ

try:
    iter(maybe_iterable)
    print('iteration will probably work')
except TypeError:
    print('not iterable')

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

ข้อเท็จจริง

  1. คุณสามารถรับตัววนซ้ำจากวัตถุใด ๆoโดยการเรียกiter(o)ถ้าอย่างน้อยหนึ่งในเงื่อนไขต่อไปนี้ถือเป็นจริง:

    a) oมี__iter__วิธีการที่ส่งกลับวัตถุตัววนซ้ำ ตัววนซ้ำเป็นวัตถุใด ๆ ที่มี__iter__และ__next__(Python 2:) nextวิธีการ

    b) oมี__getitem__วิธีการ

  2. การตรวจสอบอินสแตนซ์ของIterableหรือSequenceหรือการตรวจสอบแอตทริบิวต์__iter__ไม่เพียงพอ

  3. หากวัตถุoดำเนินการเพียง__getitem__แต่ไม่ได้__iter__, iter(o)จะสร้าง iterator ที่พยายามที่จะดึงข้อมูลรายการจากoดัชนีจำนวนเต็มเริ่มต้นที่ดัชนี 0 iterator จะจับใด ๆIndexError( แต่ไม่มีข้อผิดพลาดอื่น ๆ ) ที่ถูกยกขึ้นแล้วยกStopIterationตัวเอง

  4. โดยทั่วไปแล้วไม่มีทางที่จะตรวจสอบว่าตัววนซ้ำที่ส่งคืนมาiterนั้นมีเหตุผลอย่างอื่นนอกเหนือจากการลองใช้หรือไม่

  5. หากวัตถุoดำเนิน__iter__การiterฟังก์ชั่นจะทำให้แน่ใจว่าวัตถุที่ส่งกลับโดย__iter__เป็นตัววนซ้ำ __getitem__ไม่มีการตรวจสอบสุขภาพจิตดีคือถ้าวัตถุเพียงการดำเนินการ

  6. __iter__ชนะ หากวัตถุoการดำเนินการทั้งใน__iter__และ__getitem__, จะเรียกiter(o)__iter__

  7. หากคุณต้องการทำให้วัตถุของคุณเป็นตัวกำหนดให้ใช้__iter__วิธีการนี้เสมอ

for ลูป

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

เมื่อคุณใช้for item in oสำหรับวัตถุที่วนซ้ำบางตัวoหลามจะเรียกiter(o)และคาดว่าวัตถุตัววนซ้ำเป็นค่าที่ส่งคืน ตัววนซ้ำเป็นวัตถุใด ๆ ที่ใช้เมธอด__next__(หรือnextใน Python 2) และ__iter__เมธอด

ตามแบบแผน__iter__วิธีการวนซ้ำควรส่งคืนวัตถุเอง (เช่นreturn self) Python จะเรียกnextใช้ตัววนซ้ำจนกว่าStopIterationจะเพิ่มขึ้น ทั้งหมดนี้เกิดขึ้นโดยปริยาย แต่การสาธิตต่อไปนี้ทำให้มองเห็นได้:

import random

class DemoIterable(object):
    def __iter__(self):
        print('__iter__ called')
        return DemoIterator()

class DemoIterator(object):
    def __iter__(self):
        return self

    def __next__(self):
        print('__next__ called')
        r = random.randint(1, 10)
        if r == 5:
            print('raising StopIteration')
            raise StopIteration
        return r

ย้ำกว่าDemoIterable:

>>> di = DemoIterable()
>>> for x in di:
...     print(x)
...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration

การอภิปรายและภาพประกอบ

ในวันที่ 1 และ 2: รับ iterator และการตรวจสอบที่ไม่น่าเชื่อถือ

พิจารณาคลาสต่อไปนี้:

class BasicIterable(object):
    def __getitem__(self, item):
        if item == 3:
            raise IndexError
        return item

โทรiterกับอินสแตนซ์ของBasicIterableจะกลับ iterator โดยไม่มีปัญหาใด ๆ เพราะการดำเนินการBasicIterable__getitem__

>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>

อย่างไรก็ตามสิ่งสำคัญคือต้องทราบว่าbไม่มี__iter__แอตทริบิวต์และไม่ถือว่าเป็นตัวอย่างของIterableหรือSequence:

>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False

นี่คือเหตุผลที่Fluent Pythonโดย Luciano Ramalho แนะนำให้โทรiterและจัดการศักยภาพTypeErrorเป็นวิธีที่แม่นยำที่สุดในการตรวจสอบว่าวัตถุนั้นสามารถใช้การได้หรือไม่ การอ้างอิงโดยตรงจากหนังสือ:

ตั้งแต่ Python 3.4 วิธีที่แม่นยำที่สุดในการตรวจสอบว่าวัตถุxนั้นสามารถทำซ้ำได้คือการเรียกiter(x)และจัดการTypeErrorข้อยกเว้นหากไม่มี วิธีนี้มีความแม่นยำมากกว่าการใช้isinstance(x, abc.Iterable)เพราะiter(x)ยังคำนึงถึง__getitem__วิธีการดั้งเดิมในขณะที่IterableABC ไม่ทำเช่นนั้น

ในจุดที่ 3: วนซ้ำวัตถุที่จัดเตรียมไว้ให้เท่านั้น__getitem__แต่ไม่ใช่__iter__

วนซ้ำของอินสแตนซ์ของBasicIterableงานตามที่คาดไว้: Python สร้างตัววนซ้ำที่พยายามดึงรายการตามดัชนีเริ่มต้นที่ศูนย์จนกระทั่งมีการIndexErrorยกขึ้น การสาธิตของวัตถุ__getitem__วิธีการเพียงแค่ส่งกลับitemซึ่งได้รับการจัดเป็นอาร์กิวเมนต์__getitem__(self, item)โดย iterator iterกลับโดย

>>> b = BasicIterable()
>>> it = iter(b)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

โปรดทราบว่าตัววนซ้ำจะยกขึ้นStopIterationเมื่อไม่สามารถส่งคืนรายการถัดไปและสิ่งIndexErrorที่ถูกยกขึ้นสำหรับitem == 3ได้รับการจัดการภายใน นี่คือเหตุผลที่การวนซ้ำBasicIterableกับ a forloop ทำงานอย่างที่คาดไว้:

>>> for x in b:
...     print(x)
...
0
1
2

นี่คืออีกตัวอย่างหนึ่งในการผลักดันแนวคิดของตัววนซ้ำที่ส่งคืนโดยiterพยายามเข้าถึงไอเท็มตามดัชนี WrappedDictไม่สืบทอดdictซึ่งหมายความว่าอินสแตนซ์จะไม่มี__iter__วิธีการ

class WrappedDict(object): # note: no inheritance from dict!
    def __init__(self, dic):
        self._dict = dic

    def __getitem__(self, item):
        try:
            return self._dict[item] # delegate to dict.__getitem__
        except KeyError:
            raise IndexError

โปรดทราบว่าการเรียกไป__getitem__ยังได้รับการมอบหมายให้dict.__getitem__ทำเครื่องหมายรูปสี่เหลี่ยมจัตุรัสเป็นเพียงการจดชวเลข

>>> w = WrappedDict({-1: 'not printed',
...                   0: 'hi', 1: 'StackOverflow', 2: '!',
...                   4: 'not printed', 
...                   'x': 'not printed'})
>>> for x in w:
...     print(x)
... 
hi
StackOverflow
!

ที่จุด 4 และ 5: iterตรวจสอบตัววนซ้ำเมื่อมีการเรียก__iter__ :

เมื่อiter(o)เรียกว่าสำหรับวัตถุo, iterจะให้แน่ใจว่าค่าตอบแทนของ__iter__ถ้าวิธีการที่เป็นปัจจุบันเป็น iterator ซึ่งหมายความว่าวัตถุที่ส่งกลับมาจะต้องดำเนินการ__next__(หรือnextในหลาม 2) __iter__และ iterไม่สามารถทำการตรวจสอบความถูกต้องของวัตถุที่มีให้__getitem__ได้เพราะไม่มีวิธีการตรวจสอบว่ารายการของวัตถุนั้นสามารถเข้าถึงได้โดยดัชนีจำนวนเต็มหรือไม่

class FailIterIterable(object):
    def __iter__(self):
        return object() # not an iterator

class FailGetitemIterable(object):
    def __getitem__(self, item):
        raise Exception

โปรดทราบว่าการสร้าง iterator จากFailIterIterableกรณีล้มเหลวทันทีในขณะที่การสร้าง iterator จากFailGetItemIterableประสบความสำเร็จ __next__แต่จะโยนข้อยกเว้นในสายแรกที่

>>> fii = FailIterIterable()
>>> iter(fii)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'object'
>>>
>>> fgi = FailGetitemIterable()
>>> it = iter(fgi)
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/iterdemo.py", line 42, in __getitem__
    raise Exception
Exception

เมื่อวันที่ 6: __iter__ชนะ

อันนี้ตรงไปตรงมา หากมีการดำเนินการวัตถุ__iter__และ__getitem__, จะเรียกiter __iter__พิจารณาคลาสต่อไปนี้

class IterWinsDemo(object):
    def __iter__(self):
        return iter(['__iter__', 'wins'])

    def __getitem__(self, item):
        return ['__getitem__', 'wins'][item]

และเอาต์พุตเมื่อวนลูปมากกว่าอินสแตนซ์:

>>> iwd = IterWinsDemo()
>>> for x in iwd:
...     print(x)
...
__iter__
wins

ในวันที่ 7: คลาส iterable ของคุณควรใช้ __iter__

คุณอาจถามตัวเองว่าทำไมลำดับในตัวส่วนใหญ่เช่นlistใช้__iter__วิธีการเมื่อ__getitem__มีเพียงพอ

class WrappedList(object): # note: no inheritance from list!
    def __init__(self, lst):
        self._list = lst

    def __getitem__(self, item):
        return self._list[item]

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

>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
...     print(x)
... 
A
B
C

สาเหตุที่กำหนดเอง iterables ของคุณควรใช้__iter__มีดังนี้:

  1. หากคุณใช้__iter__อินสแตนซ์จะ iterables พิจารณาและจะกลับมาisinstance(o, collections.abc.Iterable)True
  2. หากวัตถุที่ส่งกลับโดย__iter__ไม่ได้เป็น iterator, จะล้มเหลวทันทีและเพิ่มiterTypeError
  3. มีการจัดการพิเศษ__getitem__สำหรับเหตุผลด้านความเข้ากันได้ย้อนหลัง การอ้างอิงอีกครั้งจาก Fluent Python:

นั่นคือเหตุผลใด ๆ ลำดับงูหลามเป็น iterable: __getitem__พวกเขาทั้งหมดใช้ ในความเป็นจริงแล้วลำดับมาตรฐานก็ใช้__iter__เช่นกันและคุณก็ควรใช้เช่นกันเนื่องจากมีการจัดการพิเศษ__getitem__สำหรับเหตุผลด้านความเข้ากันได้แบบย้อนหลังและอาจหายไปในอนาคต (แม้ว่ามันจะไม่คัดค้านในขณะที่ฉันเขียน)


ดังนั้นจึงปลอดภัยในการกำหนดเพรดิเคตis_iterableโดยกลับมาTrueในtryบล็อกและFalseในexcept TypeErrorบล็อก?
alancalvitti

นี่คือคำตอบที่ดี ฉันคิดว่ามันเน้นถึงลักษณะที่ไม่ได้ใช้งานและโชคร้ายของโปรโตคอล getitem มันไม่ควรถูกเพิ่มเข้ามา
Neil G

31

ไม่เพียงพอ: วัตถุที่ส่งคืนโดย__iter__ต้องใช้โปรโตคอลการทำซ้ำ (เช่นnextวิธีการ) ดูส่วนที่เกี่ยวข้องในเอกสาร

ใน Python วิธีปฏิบัติที่ดีคือ "ลองดู" แทนที่จะ "ตรวจสอบ"


9
"พิมพ์เป็ด" ฉันเชื่อไหม :)
วิลเล็ม

9
@willem: หรือ "ไม่ได้ขออนุญาต แต่สำหรับการให้อภัย" ;-)
jldupont

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

22

ใน Python <= 2.5 คุณทำไม่ได้และไม่ควรทำอย่างนั้นมันเป็นอินเทอร์เฟซ "ไม่เป็นทางการ"

แต่เนื่องจาก Python 2.6 และ 3.0 คุณสามารถใช้โครงสร้างพื้นฐาน ABC (ระดับฐานนามธรรม) ใหม่พร้อมกับ ABCs ในตัวบางตัวที่มีอยู่ในโมดูลคอลเล็กชัน:

from collections import Iterable

class MyObject(object):
    pass

mo = MyObject()
print isinstance(mo, Iterable)
Iterable.register(MyObject)
print isinstance(mo, Iterable)

print isinstance("abc", Iterable)

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

ในทางกลับกันหากวัตถุของคุณไม่เป็นไปตามอินเตอร์เฟสที่คุณต้องการคุณจะทำอย่างไร นำตัวอย่างต่อไปนี้:

from collections import Iterable
from traceback import print_exc

def check_and_raise(x):
    if not isinstance(x, Iterable):
        raise TypeError, "%s is not iterable" % x
    else:
        for i in x:
            print i

def just_iter(x):
    for i in x:
        print i


class NotIterable(object):
    pass

if __name__ == "__main__":
    try:
        check_and_raise(5)
    except:
        print_exc()
        print

    try:
        just_iter(5)
    except:
        print_exc()
        print

    try:
        Iterable.register(NotIterable)
        ni = NotIterable()
        check_and_raise(ni)
    except:
        print_exc()
        print

หากวัตถุไม่เป็นไปตามที่คุณคาดหวังคุณเพียงแค่โยน TypeError แต่หากลงทะเบียน ABC ที่เหมาะสมแล้วเช็คของคุณก็จะไม่ได้ผล ในทางตรงกันข้ามถ้า__iter__วิธีการที่มีอยู่ Python จะรับรู้วัตถุของคลาสนั้นโดยอัตโนมัติว่าเป็น Iterable

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


13
อย่าใช้รหัสเปลือยexcept:ในตัวอย่างสำหรับผู้เริ่มต้น มันส่งเสริมการปฏิบัติที่ไม่ดี
jfs

JFS: ฉันจะไม่ทำ แต่ฉันต้องผ่านการเพิ่มรหัสข้อยกเว้นหลายครั้งและฉันไม่ต้องการที่จะตรวจสอบข้อยกเว้นที่เฉพาะเจาะจง ... ฉันคิดว่าวัตถุประสงค์ของรหัสนี้ค่อนข้างชัดเจน
Alan Franzoni

21
try:
  #treat object as iterable
except TypeError, e:
  #object is not actually iterable

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


3
ในทางเทคนิคในระหว่างการคำนวณซ้ำการคำนวณของคุณอาจโยนTypeErrorและโยนคุณออกจากที่นี่ แต่โดยทั่วไปแล้วใช่
คริสลัทซ์

6
@willem: โปรดใช้ timeit เพื่อทำการวัดประสิทธิภาพ ข้อยกเว้น Python มักจะเร็วกว่าคำสั่ง if พวกเขาสามารถใช้เส้นทางที่สั้นกว่าเล็กน้อยผ่านล่าม
S.Lott

2
@willem: IronPython มีข้อยกเว้นช้า (เทียบกับ CPython)
jfs

2
ความพยายามในการทำงาน: ข้อความอย่างรวดเร็วจริงๆ ดังนั้นหากคุณมีข้อยกเว้นเล็กน้อยให้ลองใช้การยกเว้นนั้นเร็ว หากคุณคาดหวังว่ามีข้อยกเว้นมากมาย "ถ้า" สามารถทำได้เร็วกว่า
Arne Babenhauserheide

2
ไม่ควรจับวัตถุข้อยกเว้นด้วยการเพิ่ม " as e" หลังจากTypeErrorแทนที่จะเพิ่ม " , e" หรือไม่
HelloGoodbye

21

ตั้งแต่Python 3.5คุณสามารถใช้โมดูลการพิมพ์จากไลบรารี่มาตรฐานสำหรับสิ่งที่เกี่ยวข้องกับประเภท:

from typing import Iterable

...

if isinstance(my_item, Iterable):
    print(True)

18

ทางออกที่ดีที่สุดที่ฉันพบ:

hasattr(obj, '__contains__')

ซึ่งโดยทั่วไปจะตรวจสอบว่าวัตถุใช้ตัวinดำเนินการหรือไม่

ข้อดี (ไม่มีโซลูชันอื่นใดที่มีทั้งหมดสามข้อ):

  • มันเป็นนิพจน์ (ทำงานเป็นแลมบ์ดาซึ่งตรงข้ามกับลอง ... ยกเว้นตัวแปร)
  • มันเป็น (ควรจะ) นำมาใช้โดย iterables ทั้งหมดรวมถึงสตริง (ตรงข้ามกับ__iter__)
  • ใช้งานได้กับ Python> = 2.5

หมายเหตุ:

  • ปรัชญา Python ของ "ขอการอภัยการไม่ได้รับอนุญาต" ทำงานได้ไม่ดีเช่นในรายการที่คุณมีทั้ง iterables และ non-iterables และคุณต้องปฏิบัติต่อแต่ละองค์ประกอบต่างกันตามประเภทของมัน iterables บนยกเว้นจะทำงานได้ แต่มันจะดูน่าเกลียดและทำให้เข้าใจผิด)
  • วิธีแก้ปัญหานี้ซึ่งพยายามที่จะวนซ้ำวัตถุ (เช่น [x สำหรับ x ใน obj]) เพื่อตรวจสอบว่าการทำซ้ำอาจทำให้เกิดการลงโทษอย่างมีนัยสำคัญสำหรับ iterables ขนาดใหญ่ (โดยเฉพาะถ้าคุณต้องการองค์ประกอบสองสามอย่างแรกของ iterable สำหรับ ตัวอย่าง) และควรหลีกเลี่ยง

3
ดี แต่ทำไมไม่ใช้โมดูลคอลเล็กชันตามที่เสนอในstackoverflow.com/questions/1952464/… ? ดูเหมือนจะแสดงออกให้ฉันมากขึ้น
Dave Abrahams

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

46
เพียงเพราะบางสิ่งบางอย่างสามารถมีบางสิ่งบางอย่างไม่ได้หมายความว่ามันเป็นเรื่องง่าย ตัวอย่างเช่นผู้ใช้สามารถตรวจสอบว่าจุดอยู่ในคิวบ์ 3 มิติ แต่คุณจะวนซ้ำผ่านวัตถุนี้ได้อย่างไร
Casey Kuball

13
สิ่งนี้ไม่ถูกต้อง iterable ตัวเองไม่ได้รับการสนับสนุนที่มีอย่างน้อยกับงูหลาม 3.4
Peter Shinners

15

คุณสามารถลองสิ่งนี้:

def iterable(a):
    try:
        (x for x in a)
        return True
    except TypeError:
        return False

หากเราสามารถสร้างเครื่องกำเนิดไฟฟ้าที่วนซ้ำ (แต่ไม่เคยใช้เครื่องกำเนิดไฟฟ้าเพื่อไม่ให้เปลืองเนื้อที่) ก็สามารถทำได้ ดูเหมือนว่าเป็นสิ่งที่ "duh" ทำไมคุณต้องพิจารณาว่าตัวแปรสามารถทำซ้ำได้ตั้งแต่แรก?


เกี่ยวกับiterable(itertools.repeat(0))อะไร :)
badp

5
@badp เป็น(x for x in a)เพียงแค่สร้างตัวสร้างมันไม่ได้ทำซ้ำใด ๆ ใน
catchmeifyoutry

5
การพยายาม(x for x in a)เทียบเท่ากับการลองอย่างแม่นยำiterator = iter(a)หรือไม่ หรือมีบางกรณีที่ทั้งสองแตกต่างกันอย่างไร
สูงสุด

for _ in a: breakตรงไปตรงมาไม่ใช่เหรอ มันช้ากว่านี้ไหม?
Mr_and_Mrs_D

2
@Mr_and_Mrs_D มันไม่ดีถ้าวัตถุที่ทดสอบนั้นเป็นตัววนซ้ำแล้ววนซ้ำหลังจากนั้น (มันจะสั้นกว่า 1 รายการเนื่องจากไม่สามารถรีเซ็ตตำแหน่งได้) การสร้างเครื่องกำเนิดขยะจะไม่ซ้ำไปตามวัตถุเนื่องจากไม่ได้วนซ้ำ แม้ว่าฉันจะไม่แน่ใจว่ามันจะเพิ่ม TypeError 100% หากไม่สามารถทำซ้ำได้
Tcll


10

ตามPython 2 อภิธานศัพท์ iterables คือ

ทุกประเภทลำดับ (เช่นlist, strและtuple) และบางชนิดที่ไม่เหมือนลำดับdictและfileและวัตถุของการเรียนใด ๆ ที่คุณกำหนดด้วยการ__iter__()หรือ__getitem__()วิธีการ Iterables สามารถใช้ใน for for loop และในสถานที่อื่น ๆ ที่ต้องการลำดับ (zip (), map (), ... ) เมื่อวัตถุ iterable ถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังฟังก์ชั่น iter () ในตัวมันจะส่งกลับ iterator สำหรับวัตถุ

แน่นอนว่าด้วยรูปแบบการเขียนโค้ดทั่วไปสำหรับ Python ตามความจริงที่ว่า "ง่ายกว่าที่จะขอการให้อภัยมากกว่าการอนุญาต" การคาดหวังทั่วไปคือการใช้

try:
    for i in object_in_question:
        do_something
except TypeError:
    do_something_for_non_iterable

hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")แต่ถ้าคุณต้องการที่จะตรวจสอบอย่างชัดเจนคุณสามารถทดสอบโดย iterable คุณต้องตรวจสอบทั้งสองอย่างเนื่องจากstrไม่มี__iter__วิธีการ (อย่างน้อยไม่ได้อยู่ใน Python 2 ใน Python 3 พวกเขาทำ) และเนื่องจากgeneratorวัตถุไม่มี__getitem__วิธีการ


8

ฉันมักพบว่าสะดวกในสคริปต์เพื่อกำหนดiterableฟังก์ชั่น (ตอนนี้รวมการทำให้เข้าใจง่ายที่แนะนำของ Alfe):

import collections

def iterable(obj):
    return isinstance(obj, collections.Iterable):

เพื่อให้คุณสามารถทดสอบว่าวัตถุใด ๆ ที่ทำซ้ำได้ในรูปแบบที่อ่านได้มาก

if iterable(obj):
    # act on iterable
else:
    # not iterable

อย่างที่คุณจะทำกับcallableฟังก์ชั่น

แก้ไข: หากคุณติดตั้ง numpy คุณสามารถทำได้ง่ายๆ: จากnumpy import iterableซึ่งเป็นเรื่องง่าย ๆ

def iterable(obj):
    try: iter(obj)
    except: return False
    return True

หากคุณไม่ได้มี numpy คุณสามารถใช้รหัสนี้หรืออย่างใดอย่างหนึ่งข้างต้น


3
เมื่อใดก็ตามที่คุณทำเช่นฏif x: return True else: return False(กับxการเป็นแบบบูล) return xคุณสามารถเขียนนี้เป็น ในกรณีของคุณโดยไม่ต้องมีreturn isinstance(…) if
Alfe

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

2
คุณควรจับ "TypeError" ในบรรทัด `ยกเว้น: return false` การจับทุกอย่างเป็นลวดลายที่ไม่ดี
Mariusz Jamro

รู้ว่า. ฉันแปลโค้ดนั้นจากไลบรารี NumPy ซึ่งใช้ข้อยกเว้นทั่วไป
fmonegaglia

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

5

มีฟังก์ชันในตัวดังนี้:

from pandas.util.testing import isiterable

นี่เป็นเพียงแค่ดูว่ามี__iter__และไม่สนใจจริง ๆ เกี่ยวกับลำดับและคล้ายกัน
อี๊ด

4

มันทำให้ฉันหลงทางอยู่เสมอว่าทำไมหลามถึงมีcallable(obj) -> boolแต่ไม่ใช่iterable(obj) -> bool...
แน่นอนว่ามันง่ายกว่าที่จะทำhasattr(obj,'__call__')แม้ว่ามันจะช้ากว่าก็ตาม

เนื่องจากคำตอบอื่น ๆ ทุกข้อแนะนำให้ใช้try/ except TypeErrorโดยที่การทดสอบสำหรับข้อยกเว้นนั้นถือว่าเป็นการปฏิบัติที่ไม่ดีในภาษาใด ๆ นี่คือการดำเนินการของiterable(obj) -> boolฉันที่ฉันชอบและใช้บ่อยขึ้น:

เพื่อประโยชน์ของ python 2 ฉันจะใช้แลมบ์ดาเพียงเพื่อเพิ่มประสิทธิภาพพิเศษนั้น ...
(ใน python 3 ไม่สำคัญว่าคุณจะใช้เพื่อกำหนดฟังก์ชั่นใดdefมีความเร็วเท่ากันโดยประมาณlambda)

iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__')

โปรดทราบว่าฟังก์ชั่นนี้ดำเนินการได้เร็วขึ้นสำหรับวัตถุที่มีเพราะมันไม่ได้ทดสอบ__iter____getitem__

วัตถุที่สามารถทำซ้ำได้ส่วนใหญ่ควรขึ้นอยู่กับ__iter__ว่าวัตถุที่เป็นกรณีพิเศษถอยกลับไป__getitem__แม้ว่าวัตถุนั้นจะต้องทำซ้ำก็ตาม
(และเนื่องจากนี่เป็นมาตรฐานจึงมีผลกับวัตถุ C เช่นกัน)


เขาไม่ได้ให้รหัสทำงาน แต่อย่างเดียวพูดคุยเกี่ยวกับประสิทธิภาพของงูหลาม ... แม้ว่าคำตอบนี้เป็นเพียงเพื่อความสะดวกสบายเหมือนที่ฉันเคยเห็นมาหลายครั้งแล้ว
Tcll

3
def is_iterable(x):
    try:
        0 in x
    except TypeError:
        return False
    else:
        return True

นี้จะบอกว่าใช่ลักษณะของวัตถุ iterable แต่มันจะบอกว่าไม่มีการสตริงในหลาม 2 (นั่นคือสิ่งที่ฉันต้องการตัวอย่างเช่นเมื่อฟังก์ชัน recursive สามารถใช้สตริงหรือคอนเทนเนอร์ของสตริงในสถานการณ์นั้นการขอการให้อภัยอาจนำไปสู่ ​​obfuscode และดีกว่าที่จะขออนุญาตก่อน)

import numpy

class Yes:
    def __iter__(self):
        yield 1;
        yield 2;
        yield 3;

class No:
    pass

class Nope:
    def __iter__(self):
        return 'nonsense'

assert is_iterable(Yes())
assert is_iterable(range(3))
assert is_iterable((1,2,3))   # tuple
assert is_iterable([1,2,3])   # list
assert is_iterable({1,2,3})   # set
assert is_iterable({1:'one', 2:'two', 3:'three'})   # dictionary
assert is_iterable(numpy.array([1,2,3]))
assert is_iterable(bytearray("not really a string", 'utf-8'))

assert not is_iterable(No())
assert not is_iterable(Nope())
assert not is_iterable("string")
assert not is_iterable(42)
assert not is_iterable(True)
assert not is_iterable(None)

กลยุทธ์อื่น ๆ ที่นี่จะบอกว่าใช่กับสตริง ใช้พวกเขาหากนั่นคือสิ่งที่คุณต้องการ

import collections
import numpy

assert isinstance("string", collections.Iterable)
assert isinstance("string", collections.Sequence)
assert numpy.iterable("string")
assert iter("string")
assert hasattr("string", '__getitem__')

หมายเหตุ: is_iterable () จะบอกว่าใช่สตริงประเภทและbytesbytearray

  • bytesวัตถุใน Python 3 สามารถทำซ้ำTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))ได้ไม่มีประเภทดังกล่าวใน Python 2
  • bytearray วัตถุใน Python 2 และ 3 สามารถทำซ้ำได้ True == is_iterable(bytearray(b"abc"))

สหกรณ์hasattr(x, '__iter__')วิธีการจะบอกว่าใช่สตริงในหลาม 3 และไม่มีการในหลาม 2 (ไม่ว่า''หรือb''หรือu'') ขอขอบคุณที่ @LuisMasuelli __iter__สำหรับสังเกตเห็นมันยังจะช่วยให้คุณลงบนรถม้าชนิดเล็ก


2

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

class A(object):
    def __getitem__(self, item):
        return something

class B(object):
    def __iter__(self):
        # Return a compliant iterator. Just an example
        return iter([])

class C(object):
    def __iter__(self):
        # Return crap
        return 1

class D(object): pass

def iterable(obj):
    try:
        iter(obj)
        return True
    except:
        return False

assert iterable(A())
assert iterable(B())
assert iterable(C())
assert not iterable(D())

หมายเหตุ :

  1. มันไม่เกี่ยวข้องกับความแตกต่างไม่ว่าจะเป็นวัตถุที่ไม่สามารถทำซ้ำได้หรือมีการใช้บั๊กกี้__iter__ถ้าประเภทของข้อยกเว้นเหมือนกัน: อย่างไรก็ตามคุณจะไม่สามารถย้ำวัตถุได้
  2. ฉันคิดว่าฉันเข้าใจข้อกังวลของคุณ: จะcallableมีวิธีการตรวจสอบอย่างไรถ้าฉันสามารถพึ่งพาการพิมพ์เป็ดเพื่อเพิ่มAttributeErrorถ้า__call__ไม่ได้กำหนดไว้สำหรับวัตถุของฉัน แต่นั่นไม่ใช่กรณีสำหรับการตรวจสอบซ้ำได้?

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


1

isiterablefunc ที่ผลตอบแทนรหัสต่อไปนี้Trueถ้าวัตถุเป็น iterable ถ้ามันไม่กลับ iterableFalse

def isiterable(object_):
    return hasattr(type(object_), "__iter__")

ตัวอย่าง

fruits = ("apple", "banana", "peach")
isiterable(fruits) # returns True

num = 345
isiterable(num) # returns False

isiterable(str) # returns False because str type is type class and it's not iterable.

hello = "hello dude !"
isiterable(hello) # returns True because as you know string objects are iterable

2
คำตอบโดยละเอียดจำนวนมากข้างบนพร้อม upvotes มากมายและคุณตอบด้วยคำตอบที่ไม่ได้อธิบาย ... meh
Nrzonline

กรุณาอย่าโพสต์รหัสเปลือย รวมถึงคำอธิบายเกี่ยวกับสิ่งที่กำลังทำอยู่
Jonathan Mee

1

แทนที่จะตรวจสอบ__iter__คุณลักษณะคุณสามารถตรวจสอบ__len__แอตทริบิวต์ซึ่งใช้งานโดยทุก ๆ ไพ ธ อนที่สร้างขึ้นใน iterable รวมถึงสตริง

>>> hasattr(1, "__len__")
False
>>> hasattr(1.3, "__len__")
False
>>> hasattr("a", "__len__")
True
>>> hasattr([1,2,3], "__len__")
True
>>> hasattr({1,2}, "__len__")
True
>>> hasattr({"a":1}, "__len__")
True
>>> hasattr(("a", 1), "__len__")
True

วัตถุที่ไม่สามารถทำซ้ำได้จะไม่ใช้สิ่งนี้ด้วยเหตุผลที่ชัดเจน อย่างไรก็ตามมันไม่ได้ตรวจจับ iterables ที่ผู้ใช้กำหนดเองซึ่งไม่ได้ใช้มันหรือการแสดงออกของ generator ซึ่งiterสามารถจัดการได้ อย่างไรก็ตามสามารถทำได้ในบรรทัดและการเพิ่มorการตรวจสอบนิพจน์อย่างง่ายสำหรับเครื่องกำเนิดไฟฟ้าจะแก้ไขปัญหานี้ได้ (โปรดทราบว่าการเขียนtype(my_generator_expression) == generatorจะส่งข้อความNameErrorอ้างอิงคำตอบนี้แทน)

คุณสามารถใช้ GeneratorType จากประเภท:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True

--- คำตอบที่ยอมรับโดย utdemir

(สิ่งนี้ทำให้มีประโยชน์สำหรับการตรวจสอบว่าคุณสามารถโทรหาlenวัตถุได้หรือไม่)


โชคไม่ดีที่วัตถุที่ใช้ซ้ำได้ทั้งหมดใช้__len__ในกรณีนี้มันมักจะเป็นการคำนวณระยะทางระหว่างวัตถุที่ไม่เหมาะสม ที่obj.dist()สามารถนำมาใช้แทนได้อย่างง่ายดาย
Tcll

ใช่. ผู้ใช้ส่วนใหญ่กำหนด iterables ใช้ iter และ getitem แต่ไม่ใช่ len อย่างไรก็ตามประเภทในตัวทำและถ้าคุณต้องการตรวจสอบว่าคุณสามารถเรียกใช้ฟังก์ชั่น len ที่มันการตรวจสอบสำหรับ len มีความปลอดภัยมากขึ้น แต่คุณพูดถูก
DarthCadeus

0

ไม่ใช่"ถูกต้อง"จริง ๆแต่สามารถทำหน้าที่ตรวจสอบประเภททั่วไปอย่างรวดเร็วเช่นสตริงสิ่งอันดับทุ่นลอย ฯลฯ ...

>>> '__iter__' in dir('sds')
True
>>> '__iter__' in dir(56)
False
>>> '__iter__' in dir([5,6,9,8])
True
>>> '__iter__' in dir({'jh':'ff'})
True
>>> '__iter__' in dir({'jh'})
True
>>> '__iter__' in dir(56.9865)
False

0

ค่อนข้างจะสายไปงานเลี้ยง แต่ฉันถามตัวเองคำถามนี้และเห็นสิ่งนี้แล้วคิดว่าคำตอบ ฉันไม่รู้ว่ามีใครโพสต์นี้หรือไม่ แต่ที่จริงแล้วฉันสังเกตเห็นแล้วว่าประเภทที่สามารถทำให้เข้าใจได้ทั้งหมดนั้นมี__getitem __ ()ใน dict ของพวกเขา นี่คือวิธีที่คุณจะตรวจสอบว่าวัตถุนั้นทำซ้ำได้หรือไม่โดยไม่ต้องลอง (ปุนตั้งใจ)

def is_attr(arg):
    return '__getitem__' in dir(arg)

น่าเสียดายที่นี่ไม่น่าเชื่อถือ ตัวอย่าง
timgeb

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