Seite 1 von 2

nach Iterierbarkeit fragen

Verfasst: Mittwoch 8. April 2009, 17:18
von Goswin
Wie frage ich bloß, ob ein Objekt X iterierbar ist oder nicht? Ein eingebautes "isiterable(X)" gibt es anscheinend nicht, oder habe ich etwas übersehen?

Verfasst: Mittwoch 8. April 2009, 17:34
von str1442
Wozu? Der bevorzugte Weg ist, einfach anzunehmen das es iterierbar ist. Sollte das nicht der Fall sein bekommt man eine nette Exception. Ansonsten kannst du auch hasattr(obj, "__iter__") benutzen, aber manche Objekte sind auch iterable, wenn sie __iter__ nicht besitzen - diese Objekte haben eine __len__ + __getitem__ Methode und werden von iter() dann mit den entsprechenden range() Werten gefüttert. IIRC sollte dieses Verhalten aber in Python 3 verschwunden sein (richtig?).

Verfasst: Mittwoch 8. April 2009, 17:49
von Goswin
Oder vielleicht so etwas?

Code: Alles auswählen

def isiterable(X):
  try:
    for x in X: break
    return True
  except TypeError:
    return False
Ich möchte gleich am Anfang eines Funktionsaufrufes testen können, ob die Argumente der Funktion sinnvoll sind oder nicht, und möchte nicht erst lange warten und Daten durcheinanderwürfeln, bevor die Exception kommt.

Verfasst: Mittwoch 8. April 2009, 17:52
von name
Goswin hat geschrieben:Oder vielleicht so etwas?

Code: Alles auswählen

def isiterable(X):
  try:
    for x in X: break
    return True
  except TypeError:
    return False
Ich möchte gleich am Anfang eines Funktionsaufrufes testen können, ob die Argumente der Funktion sinnvoll sind oder nicht, und möchte nicht erst lange warten und Daten durcheinanderwürfeln, bevor die Exception kommt.
Bloederweise verbrauchst du so ein element des Iterators. In Python prueft man die Argumente normalweise nicht, sondern lasst die exception einfach dann raisen wann sie kommt.

Verfasst: Mittwoch 8. April 2009, 17:53
von DasIch
Statt tatsächlich darüber zu iterieren sollte man versuchen mit iter() einen Iterator zu erzeugen.

Verfasst: Mittwoch 8. April 2009, 18:02
von sma
str1442 hat geschrieben:diese Objekte haben eine __len__ + __getitem__ Methode und werden von iter() dann mit den entsprechenden range() Werten gefüttert. IIRC sollte dieses Verhalten aber in Python 3 verschwunden sein (richtig?).
`__len__` ist nicht nötig. Python versucht einfach `__getitem__` mit numerischen Indizes beginnend mit 0 und dann aufsteigend so lange aufzurufen, bis es zu einem IndexError kommt. Dieses Verhalten ist auch noch in Python 3.x dokumentiert.

Stefan

Verfasst: Mittwoch 8. April 2009, 18:10
von Goswin
Schön, dann hätte ich ja schon 2 Alternativen, wofür ich mich vielmals bedanke:

Code: Alles auswählen

def isiterable(X):
  return hasattr(X,'__iter__')

Code: Alles auswählen

def isiterable(X):
  try: iter(X); return True
  except TypeError: return False
Die erste Alternative ist laut str1442 weniger gut für Python_2.x, und laut sma wird das in Python_3.x anscheinend nicht anders sein. Sollen wir die zweite Alternative zum Gewinner erklären?

Verfasst: Mittwoch 8. April 2009, 18:40
von DasIch
Ja, wenn man den SyntaxError behebt und nicht so mit Zeilen geizt.

Verfasst: Mittwoch 8. April 2009, 19:05
von Goswin

Code: Alles auswählen

def isiterable(X):
  try: iter(X); return True
  except TypeError: return False
@Dasich:
Ich habe den Code getestet und er scheint bei Python_2.5 einwandfrei zu funktionieren. Hat er einen Syntaxfehler von Python_3.x?

Verfasst: Mittwoch 8. April 2009, 19:06
von name
Schrecklich aussehen tut er trotzdem.

Verfasst: Mittwoch 8. April 2009, 19:07
von derdon
Als Parameter würde ich obj statt X nehmen.

Verfasst: Mittwoch 8. April 2009, 19:09
von DasIch
Hab dass mit dem Semikolon falsch interpretiert. Trotzdem ist 3 Zeilen in eine zu pressen kein Zeichen von sonderlich schönem Code. Wenn man sowas wie try except, if elif else o.ä. hat macht dies den Code deutlich schwerer zu lesen.

Verfasst: Mittwoch 8. April 2009, 19:12
von sma
Goswin hat geschrieben:Hat er einen Syntaxfehler von Python_3.x?
Nein.

Verfasst: Mittwoch 8. April 2009, 19:20
von mzh
Python > 3.0 hat definitiv kein hasattr() mehr, dort läufts über getattr(obj, '__name__').

Verfasst: Mittwoch 8. April 2009, 19:22
von DasIch
mzh hat geschrieben:Python > 3.0 hat definitiv kein hasattr() mehr, dort läufts über getattr(obj, '__name__').

Code: Alles auswählen

Python 3.0.1 (r301:69556, Feb 22 2009, 14:12:04)
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> hasattr
<built-in function hasattr>
Da muss ich wohl ein anderes Python haben.

Verfasst: Mittwoch 8. April 2009, 19:24
von mzh
Hm, keine Ahnung, wie ich darauf gekommen bin..
Ah, ich meinte callable.

Verfasst: Mittwoch 8. April 2009, 19:54
von BlackJack
@Goswin: Die zweite Variante ist genauso unzuverlässig wie die erste, denn ein Objekt das eine `__getitem__()`-Methode besitzt, aber nicht "iterable" ist, hat kein Problem mit dem `iter()`-Aufruf. Auch da "kracht" es erst, wenn man versucht von dem Iterator das erste Element zu holen.

Was letztendlich bedeutet, dass man nicht wirklich auf "iterable" prüfen kann, ohne das Objekt einfach so zu verwenden.

Verfasst: Mittwoch 8. April 2009, 20:56
von helduel
Ab Python 2.6 geht das auch so:

Code: Alles auswählen

from collections import Iterable

class Foo(object):
    def __iter__(self):
        pass

isinstance(list(), Iterable)
# True
isinstance(dict(), Iterable)
# True
isinstance(Foo(), Iterable)
# True
Gruß,
Manuel

Verfasst: Mittwoch 8. April 2009, 21:56
von BlackJack
@helduel: Aber auch bei 2.6 erkennt man so nicht alle "iterables":

Code: Alles auswählen

>>> class A(object):
...   def __getitem__(self, i):
...     if i > 5:
...       raise StopIteration()
...     else:
...       return i
...
>>> a = A()
>>> isinstance(a, Iterable)
False
>>> [x for x in a]
[0, 1, 2, 3, 4, 5]
Man muss es halt einfach probieren.

Verfasst: Donnerstag 9. April 2009, 21:03
von Goswin
name hat geschrieben:Schrecklich aussehen tut er [dein Code] trotzdem.
DasIch hat geschrieben:3 Zeilen in eine zu pressen [ist] kein Zeichen von sonderlich schönem Code. Wenn man sowas wie try except, if elif else o.ä. hat macht dies den Code deutlich schwerer zu lesen.
Am Anfang war Python noch script und stur,
da schuf BDFL das Semikolon und sprach:

"Zieh hinaus in alle Module und vermehre dich,
und sammelt um euch die Kleinen unter den Befehlen,
auf dass sie eng zusammenstehen
und ich sie gut übersehen kann"
Und da zogen Semikola ein in Klassen und Methoden,
und sammelten um sich die breaks und die returns,
und alle verwaisten x+=1 und list.sort(),
ein jegliches in seine vorangehende Codezeile.

Und BDFL sah, dass das Semikolon gut war,
und es wurde aus Alfa und Beta die Version t+1.

:wink: