9 Stimmen

Python, faule Liste

Ist es möglich, dass eine Liste in Python auf faule Weise ausgewertet wird?

Zum Beispiel

a = 1
list = [a]
print list
#[1]
a = 2
print list
#[1]

Wenn die Liste faul ausgewertet wäre, würde die letzte Zeile [2] sein

11voto

Alex Martelli Punkte 805329

Das Konzept der "faulen" Auswertung kommt normalerweise in funktionalen Sprachen vor - aber in denen könnte man nicht zwei verschiedene Werte derselben Bezeichnung neu zuweisen, daher könnte auch dort Ihr Beispiel nicht reproduziert werden.

Es geht überhaupt nicht um Faulheit - vielmehr ist es garantiert, dass die Verwendung einer Bezeichnung identisch damit ist, eine Referenz auf denselben Wert zu erhalten, auf den diese Bezeichnung verweist, und eine Bezeichnung neu zuzuweisen, also einem nackten Namen, um einen anderen Wert zu machen, garantiert, dass die Bezeichnung ab diesem Zeitpunkt auf einen anderen Wert verweist. Die Referenz auf den ersten Wert (Objekt) geht nicht verloren.

Betrachten Sie ein ähnliches Beispiel, bei dem keine Neuzuweisung an einen nackten Namen stattfindet, sondern jede andere Art von Mutation (für ein veränderliches Objekt, natürlich - Zahlen und Strings sind unveränderlich), einschließlich einer Zuweisung an etwas anderes als einen nackten Namen:

>>> a = [1]
>>> liste = [a]
>>> print liste
[[1]]
>>> a[:] = [2]
>>> print liste
[[2]]

Da es kein a - ... gibt, das den nackten Namen a neu zuweist, sondern ein a[:] = ..., das den Inhalt von a neu zuweist, ist es trivial, Python so "faul" zu machen, wie Sie möchten (und tatsächlich wäre es sogar etwas Aufwand, es "gierig" zu machen!;-)... wenn Faulheit gegenüber Gier in einem dieser Fälle eine Rolle spielen würde (was nicht der Fall ist;-).

Seien Sie sich einfach der vollkommen einfachen Semantik des "Zuweisens an einen nackten Namen" bewusst (im Gegensatz zum Zuweisen an irgendetwas anderes, was durch Verwendung Ihrer eigenen Typen entsprechend angepasst und gesteuert werden kann), und die optische Illusion von "faul vs eifrig" wird hoffentlich verschwinden;-)

11voto

Jamie Cockburn Punkte 6989

Bin auf diesen Beitrag gestoßen, als ich nach einer echten Implementierung einer faulen Liste gesucht habe, aber es klang wie etwas Lustiges, was man ausprobieren könnte.

Die folgende Implementierung macht grundsätzlich das, was ursprünglich gefragt wurde:

from collections import Sequence

class LazyClosureSequence(Sequence):
    def __init__(self, get_items):
        self._get_items = get_items

    def __getitem__(self, i):
        return self._get_items()[i]

    def __len__(self):
        return len(self._get_items())

    def __repr__(self):
        return repr(self._get_items())

Man verwendet es so:

>>> a = 1
>>> l = LazyClosureSequence(lambda: [a])
>>> print l
[1]
>>> a = 2
>>> print l
[2]

Das ist offensichtlich schrecklich.

8voto

Amber Punkte 473552

Python ist im Allgemeinen nicht wirklich sehr faul.

Sie können Generatoren verwenden, um träge Datenstrukturen zu emulieren (wie unendliche Listen usw.), aber was die Verwendung von normaler Listen-Syntax betrifft, usw., werden Sie keine Trägheit haben.

0voto

ankostis Punkte 7409

Dies ist eine schreibgeschützte Lazy-Liste, für die nur eine vordefinierte Länge und eine Cache-Update-Funktion erforderlich sind:

importieren Sie copy
importieren Sie Operationen
von Sammlungen.abc import Sequenz
von functools import partialmethod
von Eingabe importieren Dict, Union

def _cmp_list(a: list, b: list, op, if_eq: bool, if_long_a: bool) -> bool:
    """Hilfsfunktion zum Implementieren der Klassenoperatoren gt|ge|lt|le"""
    wenn a ist b:
        zurück, wenn_eq
    for ia, ib in zip(a, b):
        wenn ia == ib:
            fortfahren
        zurück op(ia, ib)

    la, lb = len(a), len(b)
    wenn la == lb:
        zurück, wenn_eq
    wenn la > lb:
        zurück, wenn_long_a
    zurück nicht, wenn_long_a

Klasse LazyListView(Sequenz):
    def __init__(self, Länge):
        self._range = Bereich(Länge)
        self._cache: Dict[int, Wert] = {}

    def __len__(self) -> int:
        zurück len(self._range)

    def __getitem__(self, ix: Union[int, slice]) -> Wert:
        Länge = len(self)

        wenn isinstance(ix, slice):
            klon = copy.copy(self)
            klon._range = self._range[slice(*ix.indices(length))]  # schneiden
            zurück klon
        sonst:
            wenn ix < 0:
                ix += len(self)  # negative Indizes zählen vom Ende
            wenn nicht (0 <= ix < Länge):
                raise IndexError(f"Listeindex {ix} außerhalb des Bereichs [0, {length})")
            wenn ix nicht in self._cache:
                ...  # Cache aktualisieren
            zurück self._cache[ix]

    def __iter__(self) -> dict:
        für i, _row_ix in enumerate(self._range):
            yield self[i]

    __eq__ = _eq_list
    __gt__ = partialmethod(_cmp_list, op=operator.gt, if_eq=False, if_long_a=True)
    __ge__ = partialmethod(_cmp_list, op=operator.ge, if_eq=True, if_long_a=True)
    __le__ = partialmethod(_cmp_list, op=operator.le, if_eq=True, if_long_a=False)
    __lt__ = partialmethod(_cmp_list, op=operator.lt, if_eq=False, if_long_a=False)

    def __add__(self, other):
        """BRICHT die LALISZITÄT und gibt eine einfache Liste zurück"""
        return list(self) + other

    def __mul__(self, Faktor):
        """BRICHT die LALISZITÄT und gibt eine einfache Liste zurück"""
        return list(self) * Faktor

    __radd__ = __add__
    __rmul__ = __mul__

Beachten Sie, dass diese Klasse auch in diesem SO diskutiert wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X