Hat Python eine unveränderliche Liste?

90

Hat Python unveränderliche Listen?

Angenommen, ich möchte die Funktionalität einer geordneten Sammlung von Elementen haben, aber was ich garantieren möchte, ändert sich nicht. Wie kann dies implementiert werden? Listen werden bestellt, können aber mutiert werden.

cammil
quelle
4
@Marcin: Dies ist eine Frage im FAQ-Stil, die von derselben Person gestellt und beantwortet wird.
RichieHindle
@Marcin: Sie haben offensichtlich nicht bemerkt, dass das OP ihre eigene Frage beantwortet hat .
Sven Marnach
2
Die Hauptmotivation für unveränderliche Typen in Python ist, dass sie als Wörterbuchschlüssel und in Mengen verwendet werden können.
Sven Marnach
15
Entschuldigung, wenn ich hier jemanden beleidigt habe. Ich habe einfach bei Google nach unveränderlichen Listen gesucht und nichts gefunden. Als ich herausfand, dass das, wonach ich suchte, ein Tupel war, nahm ich mir die Mühe, es hier zu veröffentlichen. Nur für den Fall, dass jemand so "dumm" ist wie ich.
Cammil
5
Genau. Im Nachhinein scheint es dumm zu sein, aber aus irgendeinem Grund hatte mich mein dummes Gehirn auf den falschen Weg geführt. Nachdem ich fast ausschließlich Listen verwendet hatte und endlich merkte, dass ich eine unveränderliche brauchte, stellte ich eine natürliche Frage. Obwohl mir klar war, dass es Tupel gab, hatte ich die beiden nicht verbunden. Wenn dies jemand anderem da draußen hilft, denke ich, dass dies kein nutzloser Beitrag ist. Wenn dies jedoch nicht die richtige Antwort auf diese einfache Frage ist, dann ist das eine ganz andere Sache.
Cammil

Antworten:

105

Ja. Es heißt a tuple.

Also, anstatt [1,2]welches ist a listund was mutiert werden kann, (1,2)ist a tupleund kann nicht.


Weitere Informationen:

Ein Element tuplekann nicht durch Schreiben instanziiert werden (1), sondern Sie müssen schreiben (1,). Dies liegt daran, dass der Interpreter verschiedene andere Verwendungszwecke für Klammern hat.

Sie können auch Klammern ganz weglassen: 1,2ist das gleiche wie(1,2)

Beachten Sie, dass ein Tupel nicht gerade eine unveränderliche Liste ist. Klicken Sie hier, um mehr über die Unterschiede zwischen Listen und Tupeln zu erfahren

cammil
quelle
6
Wenn Sie inhärent veränderbare Objektzeiger in das Tupel einfügen (z. B. ([1,2],3)), ist das Tupel nicht mehr wirklich unveränderlich, da das Listenobjekt nur ein Zeiger auf ein veränderbares Objekt ist und das referenzierte Objekt nicht veränderbar ist.
Nisan.H
2
Wenn Sie eine solche grundlegende Frage beantworten, geben Sie zumindest einige weitere Erklärungen, z. B. die Leistungsunterschiede (Tupel etwas schneller) und dass Tupel als Diktiertasten verwendet werden können, während Liste dies nicht kann. Ich bin mir sicher, dass es noch viele andere Unterschiede gibt.
BrtH
3
Eigentlich kann auch ein leeres Tupel geschrieben werden (). Dies ist der einzige Fall, in dem die Klammern erforderlich sind.
RemcoGerlich
1
@ Kane, Ihre Aussage ist sicherlich in typisierten funktionalen Sprachen wahr; speziell (3,4,5)hat einen ganz anderen (int x int x int)Typ [3,4,5]- als der Typ (listof int). Das Tupel von Python scheint jedoch einer unveränderlichen Liste näher zu sein: Insbesondere können sie wiederholt werden, und es scheint, dass sie auch gefiltert und zugeordnet werden können.
John Clements
1
Ein Tupel ist keine Liste, sie haben kein kompatibles Verhalten und können auch nicht polymorph verwendet werden.
Jeremyjjbrown
6

Hier ist eine ImmutableList-Implementierung. Die zugrunde liegende Liste wird in keinem direkten Datenelement angezeigt. Der Zugriff kann jedoch über die Closure- Eigenschaft der Member-Funktion erfolgen. Wenn wir der Konvention folgen, den Inhalt des Abschlusses nicht mit der obigen Eigenschaft zu ändern, wird diese Implementierung den Zweck erfüllen. Die Instanz dieser ImmutableList-Klasse kann überall dort verwendet werden, wo eine normale Python-Liste erwartet wird.

from functools import reduce

__author__ = 'hareesh'


class ImmutableList:
    """
    An unmodifiable List class which uses a closure to wrap the original list.
    Since nothing is truly private in python, even closures can be accessed and
    modified using the __closure__ member of a function. As, long as this is
    not done by the client, this can be considered as an unmodifiable list.

    This is a wrapper around the python list class
    which is passed in the constructor while creating an instance of this class.
    The second optional argument to the constructor 'copy_input_list' specifies
    whether to make a copy of the input list and use it to create the immutable
    list. To make the list truly immutable, this has to be set to True. The
    default value is False, which makes this a mere wrapper around the input
    list. In scenarios where the input list handle is not available to other
    pieces of code, for modification, this approach is fine. (E.g., scenarios
    where the input list is created as a local variable within a function OR
    it is a part of a library for which there is no public API to get a handle
    to the list).

    The instance of this class can be used in almost all scenarios where a
    normal python list can be used. For eg:
    01. It can be used in a for loop
    02. It can be used to access elements by index i.e. immList[i]
    03. It can be clubbed with other python lists and immutable lists. If
        lst is a python list and imm is an immutable list, the following can be
        performed to get a clubbed list:
        ret_list = lst + imm
        ret_list = imm + lst
        ret_list = imm + imm
    04. It can be multiplied by an integer to increase the size
        (imm * 4 or 4 * imm)
    05. It can be used in the slicing operator to extract sub lists (imm[3:4] or
        imm[:3] or imm[4:])
    06. The len method can be used to get the length of the immutable list.
    07. It can be compared with other immutable and python lists using the
        >, <, ==, <=, >= and != operators.
    08. Existence of an element can be checked with 'in' clause as in the case
        of normal python lists. (e.g. '2' in imm)
    09. The copy, count and index methods behave in the same manner as python
        lists.
    10. The str() method can be used to print a string representation of the
        list similar to the python list.
    """

    @staticmethod
    def _list_append(lst, val):
        """
        Private utility method used to append a value to an existing list and
        return the list itself (so that it can be used in funcutils.reduce
        method for chained invocations.

        @param lst: List to which value is to be appended
        @param val: The value to append to the list
        @return: The input list with an extra element added at the end.

        """
        lst.append(val)
        return lst

    @staticmethod
    def _methods_impl(lst, func_id, *args):
        """
        This static private method is where all the delegate methods are
        implemented. This function should be invoked with reference to the
        input list, the function id and other arguments required to
        invoke the function

        @param list: The list that the Immutable list wraps.

        @param func_id: should be the key of one of the functions listed in the
            'functions' dictionary, within the method.
        @param args: Arguments required to execute the function. Can be empty

        @return: The execution result of the function specified by the func_id
        """

        # returns iterator of the wrapped list, so that for loop and other
        # functions relying on the iterable interface can work.
        _il_iter = lambda: lst.__iter__()
        _il_get_item = lambda: lst[args[0]]  # index access method.
        _il_len = lambda: len(lst)  # length of the list
        _il_str = lambda: lst.__str__()  # string function
        # Following represent the >, < , >=, <=, ==, != operators.
        _il_gt = lambda: lst.__gt__(args[0])
        _il_lt = lambda: lst.__lt__(args[0])
        _il_ge = lambda: lst.__ge__(args[0])
        _il_le = lambda: lst.__le__(args[0])
        _il_eq = lambda: lst.__eq__(args[0])
        _il_ne = lambda: lst.__ne__(args[0])
        # The following is to check for existence of an element with the
        # in clause.
        _il_contains = lambda: lst.__contains__(args[0])
        # * operator with an integer to multiply the list size.
        _il_mul = lambda: lst.__mul__(args[0])
        # + operator to merge with another list and return a new merged
        # python list.
        _il_add = lambda: reduce(
            lambda x, y: ImmutableList._list_append(x, y), args[0], list(lst))
        # Reverse + operator, to have python list as the first operand of the
        # + operator.
        _il_radd = lambda: reduce(
            lambda x, y: ImmutableList._list_append(x, y), lst, list(args[0]))
        # Reverse * operator. (same as the * operator)
        _il_rmul = lambda: lst.__mul__(args[0])
        # Copy, count and index methods.
        _il_copy = lambda: lst.copy()
        _il_count = lambda: lst.count(args[0])
        _il_index = lambda: lst.index(
            args[0], args[1], args[2] if args[2] else len(lst))

        functions = {0: _il_iter, 1: _il_get_item, 2: _il_len, 3: _il_str,
                     4: _il_gt, 5: _il_lt, 6: _il_ge, 7: _il_le, 8: _il_eq,
                     9: _il_ne, 10: _il_contains, 11: _il_add, 12: _il_mul,
                     13: _il_radd, 14: _il_rmul, 15: _il_copy, 16: _il_count,
                     17: _il_index}

        return functions[func_id]()

    def __init__(self, input_lst, copy_input_list=False):
        """
        Constructor of the Immutable list. Creates a dynamic function/closure
        that wraps the input list, which can be later passed to the
        _methods_impl static method defined above. This is
        required to avoid maintaining the input list as a data member, to
        prevent the caller from accessing and modifying it.

        @param input_lst: The input list to be wrapped by the Immutable list.
        @param copy_input_list: specifies whether to clone the input list and
            use the clone in the instance. See class documentation for more
            details.
        @return:
        """

        assert(isinstance(input_lst, list))
        lst = list(input_lst) if copy_input_list else input_lst
        self._delegate_fn = lambda func_id, *args: \
            ImmutableList._methods_impl(lst, func_id, *args)

    # All overridden methods.
    def __iter__(self): return self._delegate_fn(0)

    def __getitem__(self, index): return self._delegate_fn(1, index)

    def __len__(self): return self._delegate_fn(2)

    def __str__(self): return self._delegate_fn(3)

    def __gt__(self, other): return self._delegate_fn(4, other)

    def __lt__(self, other): return self._delegate_fn(5, other)

    def __ge__(self, other): return self._delegate_fn(6, other)

    def __le__(self, other): return self._delegate_fn(7, other)

    def __eq__(self, other): return self._delegate_fn(8, other)

    def __ne__(self, other): return self._delegate_fn(9, other)

    def __contains__(self, item): return self._delegate_fn(10, item)

    def __add__(self, other): return self._delegate_fn(11, other)

    def __mul__(self, other): return self._delegate_fn(12, other)

    def __radd__(self, other): return self._delegate_fn(13, other)

    def __rmul__(self, other): return self._delegate_fn(14, other)

    def copy(self): return self._delegate_fn(15)

    def count(self, value): return self._delegate_fn(16, value)

    def index(self, value, start=0, stop=0):
        return self._delegate_fn(17, value, start, stop)


def main():
    lst1 = ['a', 'b', 'c']
    lst2 = ['p', 'q', 'r', 's']

    imm1 = ImmutableList(lst1)
    imm2 = ImmutableList(lst2)

    print('Imm1 = ' + str(imm1))
    print('Imm2 = ' + str(imm2))

    add_lst1 = lst1 + imm1
    print('Liist + Immutable List: ' + str(add_lst1))
    add_lst2 = imm1 + lst2
    print('Immutable List + List: ' + str(add_lst2))
    add_lst3 = imm1 + imm2
    print('Immutable Liist + Immutable List: ' + str(add_lst3))

    is_in_list = 'a' in lst1
    print("Is 'a' in lst1 ? " + str(is_in_list))

    slice1 = imm1[2:]
    slice2 = imm2[2:4]
    slice3 = imm2[:3]
    print('Slice 1: ' + str(slice1))
    print('Slice 2: ' + str(slice2))
    print('Slice 3: ' + str(slice3))

    imm1_times_3 = imm1 * 3
    print('Imm1 Times 3 = ' + str(imm1_times_3))
    three_times_imm2 = 3 * imm2
    print('3 Times Imm2 = ' + str(three_times_imm2))

    # For loop
    print('Imm1 in For Loop: ', end=' ')
    for x in imm1:
        print(x, end=' ')
    print()

    print("3rd Element in Imm1: '" + imm1[2] + "'")

    # Compare lst1 and imm1
    lst1_eq_imm1 = lst1 == imm1
    print("Are lst1 and imm1 equal? " + str(lst1_eq_imm1))

    imm2_eq_lst1 = imm2 == lst1
    print("Are imm2 and lst1 equal? " + str(imm2_eq_lst1))

    imm2_not_eq_lst1 = imm2 != lst1
    print("Are imm2 and lst1 different? " + str(imm2_not_eq_lst1))

    # Finally print the immutable lists again.
    print("Imm1 = " + str(imm1))
    print("Imm2 = " + str(imm2))

    # The following statemetns will give errors.
    # imm1[3] = 'h'
    # print(imm1)
    # imm1.append('d')
    # print(imm1)

if __name__ == '__main__':
    main()
Hareesh
quelle
5

Sie können eine unveränderliche, einfach verknüpfte Liste im Lisp-Stil mithilfe von Tupeln mit zwei Elementen simulieren (Hinweis: Dies unterscheidet sich von der Tupelantwort für beliebige Elemente , wodurch ein Tupel erstellt wird, das viel weniger flexibel ist):

nil = ()
cons = lambda ele, l: (ele, l)

zB für die Liste hätten [1, 2, 3]Sie folgendes:

l = cons(1, cons(2, cons(3, nil))) # (1, (2, (3, ())))

Ihr Standard carund Ihre cdrFunktionen sind unkompliziert:

car = lambda l: l[0]
cdr = lambda l: l[1]

Da diese Liste einzeln verknüpft ist, ist das Anhängen an die Vorderseite O (1). Da diese Liste unveränderlich ist, können Sie jede Unterliste, die in einer anderen Liste wiederverwendet werden soll, sicher freigeben, wenn die zugrunde liegenden Elemente in der Liste ebenfalls unveränderlich sind.

Kevinji
quelle
4

Wenn es jedoch ein Tupel von Arrays und Tupeln gibt, kann das Array in einem Tupel geändert werden.

>>> a
([1, 2, 3], (4, 5, 6))

>>> a[0][0] = 'one'

>>> a
(['one', 2, 3], (4, 5, 6))
Gopal
quelle
9
Es kann nicht wirklich eine Sammlung geben, die ihren Inhalt unveränderlich macht, weil Sie eine Möglichkeit benötigen würden, eine unveränderliche Kopie beliebiger Objekte zu erstellen. Dazu müssten Sie die Klassen kopieren, zu denen diese Objekte gehören, und sogar die integrierten Klassen, auf die sie verweisen. Und dennoch könnten sich die Objekte auf das Dateisystem oder auf das Netzwerk oder auf etwas anderes beziehen, das einfach immer veränderlich ist. Da wir also ein beliebiges Objekt nicht unveränderlich machen können, müssen wir uns mit unveränderlichen Sammlungen veränderlicher Objekte zufrieden geben.
Jack O'Connor
1
@ JackO'Connor Nicht ganz einverstanden. Es hängt alles davon ab, wie Sie die Welt modellieren: Externe Veränderbarkeit kann immer als sich zeitlich entwickelnde Zustände modelliert werden, und anstatt einen einzelnen veränderlichen Zustand beizubehalten, kann ich mich immer auf s_t beziehen, das unveränderlich ist. "Unveränderliche Sammlung unveränderlicher Objekte" <- Schauen Sie sich Huskell, Scala und andere funktionale Programmiersprachen an. Bevor ich anfing, Python zu lernen, glaubte ich, dass Python die Unveränderlichkeit und fp von dem, was ich von anderen gehört habe, voll unterstützt, aber es stellt sich heraus, dass es nicht wahr ist.
Kane
Ich hätte sagen sollen, dass es so etwas in Python nicht wirklich geben kann. Die Unveränderlichkeit von Python beruht darauf, dass der Programmierer Konventionen (wie _private_variables) einhält und nicht die Durchsetzung durch den Interpreter.
Jack O'Connor
1
Eine Sprache wie Haskell bietet viel mehr Garantien. Wenn der Programmierer jedoch wirklich böse sein wollte, konnte er dennoch in /proc/#/memunsichere Bibliotheken oder was auch immer schreiben oder gegen diese verlinken, um das Modell zu brechen.
Jack O'Connor
1

List und Tuple unterscheiden sich in ihrem Arbeitsstil.

In LIST können wir nach der Erstellung Änderungen vornehmen. Wenn Sie jedoch eine geordnete Reihenfolge wünschen, in der in Zukunft keine Änderungen mehr vorgenommen werden können, können Sie TUPLE verwenden.

weitere Informationen::

 1) the LIST is mutable that means you can make changes in it after its creation
 2) In Tuple, we can not make changes once it created
 3) the List syntax is
           abcd=[1,'avn',3,2.0]
 4) the syntax for Tuple is 
           abcd=(1,'avn',3,2.0) 
      or   abcd= 1,'avn',3,2.0 it is also correct
Avnish Kumar
quelle
-1

Anstelle von Tupel können Sie auch Frozenset verwenden. frozenset schafft ein unveränderliches Set. Sie können list als Mitglied von frozenset verwenden und mit single for loop auf jedes Element der Liste in frozenset zugreifen.

Vishal Mopari
quelle
3
Für frozenset müssen die festgelegten Mitglieder hashbar sein, was bei einer Liste nicht der Fall ist.
Matias Elgart