Seite 1 von 1

Probleme über List umwandeln

Verfasst: Donnerstag 13. November 2008, 09:28
von coffeepig
Hallo zusammen,

Ich habe jetzt eine List wie z.B

Code: Alles auswählen

[a,[b,[c]]]
, wie kann ich dies List leicht um Form

Code: Alles auswählen

[a,b,c]
zu wandlen?

was meint ihr?

Grüßen

Verfasst: Donnerstag 13. November 2008, 10:07
von helduel
Moin,

wie wäre denn Dein Ansatz?

Gruß,
Manuel

Verfasst: Donnerstag 13. November 2008, 11:24
von coffeepig
Ich habe so gemacht:

Code: Alles auswählen

def foo(lst):
   if len(lst) == 1:
      return lst
   else:
      head,tail=lst
      return [head] + foo(tail)
            



if __name__ =='__main__':
    a =['a', ['b', ['c']]]
    print foo(a)
aber ich denke das ist nicht die einfachste weg.

Grüßen

Verfasst: Donnerstag 13. November 2008, 12:05
von helduel
Sieht doch ganz gut aus. Du könntest die Funktion höchstens noch etwas aufmotzen, indem du sie flexibler machst. Schau mal hier vorbei. Da ist eine schöne, flexiblere Lösung zu finden.

Verfasst: Donnerstag 13. November 2008, 12:07
von lunar
Naja, es ist rekursiv, was in Python aufgrund des Rekursionslimits immer ein bisschen blöd ist. Eine iterative Implementierung wäre besser.

Verfasst: Donnerstag 13. November 2008, 13:40
von helduel
lunar hat geschrieben:Naja, es ist rekursiv, was in Python aufgrund des Rekursionslimits immer ein bisschen blöd ist. Eine iterative Implementierung wäre besser.
Gut, aber das würde ich schon von der Aufgabe abhängig machen. Sonst würde ich das ganze so lösen:

Code: Alles auswählen

def flatten(list_):
    for idx, elem in enumerate(list_):
        next_idx = idx + 1
        try:
            next_elem = list_[next_idx]
        except IndexError:
            pass
        else:
            del list_[next_idx]
            list_.extend(next_elem)


if __name__ == '__main__':
    foo = [1, [2, [3, [4]]]]
    flatten(foo)
    print foo
Gruß,
Manuel

Verfasst: Donnerstag 13. November 2008, 13:58
von Y0Gi
"flattening lists" ist das Stichwort.

Die Doku des `itertools`-Moduls hält folgendes Rezept bereit:

Code: Alles auswählen

def flatten(list_of_lists):
    return list(chain.from_iterable(list_of_lists))
`from_iterable` wurde allerdings erst jüngst mit Python 2.6 eingeführt.

In der Doku von Python 2.5.2 sieht das Rezept noch so aus:

Code: Alles auswählen

def flatten(list_of_lists):
    return list(chain(*list_of_lists))
Interessanterweise resultiert das bei mir allerdings in den Fehler "TypeError: chain argument #1 must support iteration". Dagegen funktioniert es, wenn ich es so definiere:

Code: Alles auswählen

def flatten(list_of_lists):
    return list(chain(*[list_of_lists]))
Sollte so ein Bug(?) nachträglich in der älteren Doku-Version korrigiert werden? Hat ja sicher auch noch einige Leser. Bei der Gelegenheit könnte man gleich den (von mir hier bereits umbenannten) unpythonischen Namen `listOfLists` ändern :)


->

Code: Alles auswählen

In [20]: x = [1, [2, [3, [4, 5]]]]

In [21]: flatten(x)
Out[21]: [1, [2, [3, [4, 5]]]]
Edit: Moment, irgendwas stimmt hier nicht ...

Ah, mein Fehler. Mit der eigentlichen 2.5.2-Version klappt dieses:

Code: Alles auswählen

In [25]: flatten([[1, 2, 3], [4], [5, 6]])
Out[25]: [1, 2, 3, 4, 5, 6]
Wie der Name schon sagt, lässt sich das auf Listen von Listen anwenden; `[1, [2, [3, 4]]]` ist aber keine Liste von Listen, zumindest was `1` betrifft.

Verfasst: Donnerstag 13. November 2008, 14:27
von name
Y0Gi hat geschrieben:

Code: Alles auswählen

def flatten(list_of_lists):
    return list(chain(*[list_of_lists]))
Das ist aber sehr suboptimal, da es mehrere verschachtelte listen nicht flattened.

Verfasst: Donnerstag 13. November 2008, 15:58
von derdon
Warum hat noch keiner den Link zum Wiki gepostet?

Verfasst: Donnerstag 13. November 2008, 16:51
von str1442
"*(foo,)" oder "*[foo]" ist aquivalent zu "foo".

Der TypeError kommt vom ersten Element der Liste, das als Integer keine Iterierung unterstützt.

Verfasst: Donnerstag 13. November 2008, 17:53
von helduel
So, das hat mir jetzt keine Ruhe gelassen :? :

Code: Alles auswählen

from collections import deque

def smash_down(list_):
    q = deque(list_)
    result = list()
    while q:
        data = q.popleft()
        if hasattr(data, "__iter__") and not isinstance(data, basestring):
            data = list(reversed(list(data)))
            q.extendleft(data)
        else:
            result.append(data)
    return result


if __name__ == '__main__':
    foo = [[['a', ('foo', 'bar'), 'b'], 'c'], 1, [iter(['x', 'y', 'z']), 99], [2, 'asdf', [3, ([4],)]]]
    print smash_down(foo)
    foo = [1, [2, [3]]]
    print smash_down(foo)
:twisted:

Verfasst: Donnerstag 13. November 2008, 17:59
von lunar
Nicht alles, was iterable ist, hat eine __iter__-Methode.

Verfasst: Donnerstag 13. November 2008, 18:03
von Y0Gi
str1442 hat geschrieben:Der TypeError kommt vom ersten Element der Liste, das als Integer keine Iterierung unterstützt.
Klar; ich hatte übersehen, dass das `flatten`-Rezept nicht für den hier gewünschten Zweck geeignet/gedacht ist.
str1442 hat geschrieben:"*(foo,)" oder "*[foo]" ist aquivalent zu "foo".
Das kann ich so nicht stehen lassen. Mit dem Asterisk wird die folgende Sequenz quasi aufgelöst und ihre Elemente einzeln weitergegeben. Das ist im Zusammenhang mit Callable-Aufrufen sinnvoll, weil man dann eine Liste wie eine Liste von Argumenten für eine Callable verwenden kann. Analog gilt das für dicts und Keyword-Argumente.

Beispiel:

Code: Alles auswählen

def foo(*args, **kwargs):
    print args
    print kwargs

>>> a = [1, 2, 3]
>>> b = {'a': 1, 'b': 2}

>>> foo(a)
([1, 2, 3],)
{}

>>> foo(b)
({'a': 1, 'b': 2},)
{}

>>> foo(*a)
(1, 2, 3)
{}

>>> foo(*b)
('a', 'b')
{}

>>> foo(**b)
()
{'a': 1, 'b': 2}

Verfasst: Donnerstag 13. November 2008, 18:06
von helduel
lunar hat geschrieben:Nicht alles, was iterable ist, hat eine __iter__-Methode.
:? Könnte man das dann so erschlagen?

Code: Alles auswählen

from collections import deque

def smash_down(list_):
    q = deque(list_)
    result = list()
    while q:
        data = q.popleft()
        try:
            iter(data)
            iterable = True
        except TypeError:
            iterable = False
        if iterable and not isinstance(data, basestring):
            data = list(reversed(list(data)))
            q.extendleft(data)
        else:
            result.append(data)
    return result

Verfasst: Donnerstag 13. November 2008, 18:09
von str1442
@Y0Gi

?

Code: Alles auswählen

In [1]: def f(element): print element
   ...: 

In [2]: f(*("Dummy",))
Dummy

In [3]: f(*["Dummy"])
Dummy
*("Dummy",) == "Dummy".

Was die Sternchen machen, weiß ich ;)

Verfasst: Donnerstag 13. November 2008, 18:29
von name
str1442 hat geschrieben:@Y0Gi

?

Code: Alles auswählen

In [1]: def f(element): print element
   ...: 

In [2]: f(*("Dummy",))
Dummy

In [3]: f(*["Dummy"])
Dummy
*("Dummy",) == "Dummy".

Was die Sternchen machen, weiß ich ;)
Aua! Sie geben von der liste jeweils ein element als ein argument zur Funktion.

Verfasst: Donnerstag 13. November 2008, 18:40
von str1442
:roll:

Ja, ich *weiß* was sie tun.

Code: Alles auswählen

    return list(chain(*[list_of_lists]))
Und wo ist das hier bitte sinnvoll angewandt?.

>>> from itertools import chain
>>> list(chain(range(3), range(3, 7)))
[0, 1, 2, 3, 4, 5, 6]
>>> list(chain(*[range(3), range(3, 7)]))
[0, 1, 2, 3, 4, 5, 6]

Verfasst: Donnerstag 13. November 2008, 20:06
von Y0Gi
str1442: Ah, ich glaube mir ist klar, warum du diesen speziellen Fall genannt hast. Die Sternchen-Syntax hatte ich nur fürs allgemeine Verständnis mal ausgeführt.

Fakt ist ja, wie ich schon schrieb, dass das `itertools`-`flatten`-Rezept nicht für diesen Anwendungsfall passt und daher auch nicht mit der Eingabe umgehen kann. Ich hatte mich auch nicht darüber gewundert, wo der "Fehler" liegt (den ich ja "behoben" habe), sondern war eben überrascht, dass `flatten` nicht mit nicht-Listen als Elemente der äußeren Liste umgehen kann.