Frage

Ich mag einige Muster auf Listen in Python passend zu tun. Zum Beispiel in Haskell, kann ich so etwas wie die folgenden tun:

fun (head : rest) = ...

Also, wenn ich in einer Liste übergeben, head wird das erste Element sein, und rest wird die hinteren Elemente sein.

Auch in Python, ich kann automatisch Tupel auszupacken:

(var1, var2) = func_that_returns_a_tuple()

Ich möchte etwas ähnliches mit Listen in Python tun. Gerade jetzt, ich habe eine Funktion, die eine Liste zurückgibt, und ein Stück Code, macht folgendes:

ls = my_func()
(head, rest) = (ls[0], ls[1:])

fragte ich mich, wenn ich könnte irgendwie zu tun, dass in einer Zeile in Python, statt zwei.

War es hilfreich?

Lösung

So weit ich weiß, gibt es keine Möglichkeit, es einen Einzeiler in aktueller Python zu machen, ohne eine andere Funktion einzuführen, z.

split_list = lambda lst: (lst[0], lst[1:])
head, rest = split_list(my_func())

Doch in Python 3.0 die spezielle Syntax für variadische Argument Signaturen und Argument auspacken verwendet wird für diese Art der allgemeinen Sequenz als auch das Auspacken verfügbar werden, so in 3.0 können Sie schreiben:

head, *rest = my_func()

Siehe PEP 3132 .

Andere Tipps

Zunächst einmal, bitte beachten Sie, dass die „Pattern Matching“ von funktionalen Sprachen und die Zuordnung zu Tupeln Sie erwähnen, ist nicht wirklich so ähnlich. In funktionalen Sprachen sind die Muster zu geben, Teil-Definitionen einer Funktion verwendet. So f (x : s) = e bedeuten nehmen nicht den Kopf und Schwanz des Arguments von f und zurück e sie verwenden, aber es bedeutet, dass , wenn das Argument von f ist von der Form x : s (für einige x und s), und f (x : s) gleich e.

Die Zuordnung von Python eher wie eine Mehrfachzuordnung ist (ich vermute, das war seine ursprüngliche Absicht). So schreiben Sie zum Beispiel auf x, y = y, x die Werte in x und y zu tauschen, ohne eine temporäre Variable zu benötigen (wie bei einer einfachen Zuweisung). Dies hat wenig mit Mustervergleich zu tun, wie es im Grunde eine Abkürzung für die „simultane“ und Ausführung von x = y y = x ist. Obwohl Python statt durch Komma getrennte Listen beliebige Sequenzen erlaubt, würde ich nicht empfehlen, diese Musterabgleich aufrufen. Mit Musterabgleich Sie überprüfen, ob etwas ein Muster übereinstimmt; in der Python-Zuordnung sollten Sie sicherstellen, dass die Sequenzen auf beiden Seiten gleich sind.

Zu tun, was Sie scheinen zu wollen, würden Sie in der Regel (auch in funktionalen Sprachen) entweder eine Hilfsfunktion verwenden (wie von anderen erwähnt) oder etwas ähnlichem let oder where Konstrukte (die Sie als mit einem anonymen Funktionen betrachten können). Zum Beispiel:

(head, tail) = (x[0], x[1:]) where x = my_func()

Oder in tatsächlichem Python:

(head, tail) = (lambda x: (x[0], x[1:]))(my_func())

Beachten Sie, dass dies im Wesentlichen die gleichen wie die von anderen mit einer Hilfsfunktion gegeben Lösungen außer, dass dies die Einzeiler Sie wollten. Es ist jedoch nicht unbedingt besser als eine separate Funktion.

(Sorry, wenn meine Antwort ein wenig übertrieben ist. Ich denke einfach, ist es wichtig, den Unterschied deutlich zu machen.)

Das ist ein sehr viel ein "rein funktionaler Ansatz und ist somit eine sinnvolle Idiom in Haskell, aber es ist wahrscheinlich nicht so geeignet Python. Python hat nur ein sehr begrenztes Konzept von Mustern auf diese Weise - und ich vermute, Sie könnten braucht ein etwas steifes Typ-System, um diese Art von Konstrukt zu implementieren ( erlang Fans eingeladen hier andere Meinung zu sein).

Was Sie haben, ist wahrscheinlich so nah wie Sie zu diesem Idiom bekommen würde, aber Sie sind wahrscheinlich besser dran, eine Liste Verständnis oder aus zwingenden Ansatz anstatt rekursiv eine Funktion mit dem Ende der Liste aufrufen.

Wie bereits angegeben ein paar Mal a href = < "http://blog.ianbicking.org/ Funktions-python.html“rel = "nofollow noreferrer"> vor , ist Python nicht wirklich eine funktionale Sprache. Es leiht sich nur Ideen aus der FP Welt. Es ist nicht von Natur aus Schwanz rekursive in der Art und Weise würden erwarten, dass Sie in der Architektur eines eingebettet zu sehen funktionale Sprache, so dass Sie einige Schwierigkeiten zu tun, diese Art von rekursiven Operation auf einem großen Datensatz haben würden, ohne viele Stack-Speicher.

erweitert Auspacken in 3.0 eingeführt wurde http://www.python.org/dev/peps/pep-3132/

Im Gegensatz zu Haskell oder ML, ist Python nicht hat eingebautes Pattern-Matching von Strukturen. Die Pythonic Art und Weise Pattern-Matching zu tun, ist mit einem try-except-Block:

def recursive_sum(x):
    try:
        head, tail = x[0], x[1:]
        return head + recursive-sum(tail)
    except IndexError:  # empty list: [][0] raises IndexError
        return 0

Beachten Sie, dass dies nur bei Objekten mit Slice Indizierung funktioniert. Auch wenn die Funktion kompliziert wird, etwas im Körper nach die head, tail Linie könnte erhöhen Indexerror, die auf subtile Bugs führen wird. Dies schließt jedoch ermöglicht es Ihnen, die Dinge zu tun, wie:

for frob in eggs.frob_list:
    try:
        frob.spam += 1
    except AttributeError:
        eggs.no_spam_count += 1

In Python wird Endrekursion besser im Allgemeinen als eine Schleife mit einem Speicher implementiert ist, d.h.

def iterative_sum(x):
    ret_val = 0
    for i in x:
        ret_val += i
    return ret_val

Dies ist ein offensichtlicher, richtiger Weg, es 99% der Zeit zu tun. Es ist nicht nur klarer zu lesen, es ist schneller und es wird auf andere Dinge als Listen arbeiten (Sets, zum Beispiel). Wenn es eine Ausnahme wartet dort geschehen, wird die Funktion glücklich scheitern und es die Kette auszuliefern.

Ich arbeite an pyfpm , eine Bibliothek für den Mustervergleich in Python mit einer Scala-ähnlichen Syntax . Sie können es verwenden, um Objekte wie folgt auspacken:

from pyfpm import Unpacker

unpacker = Unpacker()

unpacker('head :: tail') << (1, 2, 3)

unpacker.head # 1
unpacker.tail # (2, 3)

oder in einer Funktion arglist:

from pyfpm import match_args

@match_args('head :: tail')
def f(head, tail):
    return (head, tail)

f(1)          # (1, ())
f(1, 2, 3, 4) # (1, (2, 3, 4))

Nun, warum wollen Sie es in 1-line in erster Linie?

Wenn Sie wirklich wollen, können Sie immer einen Trick tun, wie folgt:

def x(func):
  y = func()
  return y[0], y[1:]

# then, instead of calling my_func() call x(my_func)
(head, rest) = x(my_func) # that's one line :)

Zusätzlich zu den anderen Antworten zu beachten, dass die äquivalente Kopf / Schwanz Operation in Python, einschließlich python3 der Erweiterung der * Syntax allgemein als Haskells Mustervergleich zu weniger effizient sein wird.

Python-Listen werden als Vektoren implementiert, so dass der Schwanz zu erhalten, müssen Sie eine Kopie der Liste nehmen. Dies ist O (n) wrt die Größe der Liste, während ein implementaion verkettete Listen wie Haskell Verwendung lediglich die Endzeiger verwenden kann, eine O (1) Betrieb.

Die einzige Ausnahme kann Iterator basierte Ansätze, wo die Liste nicht tatsächlich zurückgegeben wird, aber ein Iterator ist. Dies kann jedoch nicht anwendbar sein, alle Orte, an denen eine Liste gewünscht wird (z. B. mehrmals iteriert).

Zum Beispiel Cipher der Ansatz, modifiziert, wenn das zurück Iterator anstatt es zu einem Tupel Umwandlung wird dieses Verhalten hat. Alternativ kann eine einfache 2-Punkt einzige Methode nicht auf dem Bytecode unter Berufung wäre:

def head_tail(lst):
    it = iter(list)
    yield it.next()
    yield it

>>> a, tail = head_tail([1,2,3,4,5])
>>> b, tail = head_tail(tail)
>>> a,b,tail
(1, 2, <listiterator object at 0x2b1c810>)
>>> list(tail)
[3, 4]

Offensichtlich wenn Sie noch in einer Nutzenfunktion zu wickeln haben, anstatt es schön syntaktischer Zucker zu sein für sie.

Es war ein reciepe in dem Python-Kochbuch, dies zu tun. Ich kann nicht scheinen, um es jetzt zu finden, aber hier ist der Code (i es leicht modifizierte)


def peel(iterable,result=tuple):
    '''Removes the requested items from the iterable and stores the remaining in a tuple
    >>> x,y,z=peel('test')
    >>> print repr(x),repr(y),z
    't' 'e' ('s', 't')
    '''
    def how_many_unpacked():
        import inspect,opcode
        f = inspect.currentframe().f_back.f_back
        if ord(f.f_code.co_code[f.f_lasti])==opcode.opmap['UNPACK_SEQUENCE']:
            return ord(f.f_code.co_code[f.f_lasti+1])
        raise ValueError("Must be a generator on RHS of a multiple assignment!!")
    iterator=iter(iterable)
    hasItems=True
    amountToUnpack=how_many_unpacked()-1
    next=None
    for num in xrange(amountToUnpack):
        if hasItems:        
            try:
                next = iterator.next()
            except StopIteration:
                next = None
                hasItems = False
        yield next
    if hasItems:
        yield result(iterator)
    else:
        yield None

Sie sollten jedoch, dass die nur funktioniert, beachten Sie, wenn aufgrund der Art und Weise einer Zuordnung auspacken mit dem vorherigen Frame inespects ... noch sein ganz nützlich.

Für Ihren spezifischen Anwendungsfall - Haskells fun (head : rest) = ... emuliert, sicher. Funktionsdefinitionen haben Parameter unterstützt seit geraumer Zeit Auspacken:

def my_method(head, *rest):
    # ...

Ab Python 3.0, wie @bpowah erwähnt, Python unterstützt im Auftrag Auspacken auch:

my_list = ['alpha', 'bravo', 'charlie', 'delta', 'echo']
head, *rest = my_list
assert head == 'alpha'
assert rest == ['bravo', 'charlie', 'delta', 'echo']
Hinweis

, dass der Stern (der „Klecks“) bedeutet „den Rest des iterable“, nicht „bis zum Ende“. Die folgenden Werke fein:

first, *middle, last = my_list
assert first == 'alpha'
assert last == 'echo'
assert middle == ['bravo', 'charlie', 'delta']

first, *middle, last = ['alpha', 'bravo']
assert first == 'alpha'
assert last == 'bravo'
assert middle == []
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top