Python'un değişmez bir listesi var mı?


93

Python'un değişmez listeleri var mı?

Diyelim ki sıralı bir öğe koleksiyonunun işlevselliğine sahip olmak istiyorum, ancak bunun değişmeyeceğini garanti etmek istiyorum, bu nasıl uygulanabilir? Listeler sıralanır ancak değiştirilebilirler.


4
@Marcin: Bu aynı kişi tarafından sorulan ve cevaplanan SSS tarzı bir sorudur.
RichieHindle

@Marcin: OP'nin kendi sorusunu cevapladığını fark etmediniz .
Sven Marnach

2
Python'daki değişmez türlerin ana motivasyonu, sözlük anahtarları ve setler olarak kullanılabilir olmalarıdır.
Sven Marnach

16
Burada birini kırdıysam özür dilerim. Sadece Google'da değişmez listeleri aradım ve hiçbir şey bulamadım. Aradığım şeyin bir demet olduğunu anladığımda, burada yayınlama zahmetine girdim. Birinin benim kadar "aptal" olması durumunda.
cammil

5
Katılıyorum. Geriye dönüp baktığımda aptalca görünüyor, ama her ne sebeple olursa olsun, aptal beynim beni yanlış yola yönlendirmişti. Neredeyse sadece listeleri kullandıktan ve sonunda değişmez bir listeye ihtiyacım olduğunu anladıktan sonra doğal bir soru sordum. Tuple'ların var olduğunun farkında olmama rağmen, ikisini birbirine bağlamamıştım. Bu orada başka birine yardımcı oluyorsa, bunun işe yaramaz bir gönderi olmadığını hissediyorum. Bununla birlikte, bu basit sorunun doğru cevabı bu değilse, o zaman bu tamamen başka bir konudur.
cammil

Yanıtlar:


108

Evet. Adı a tuple.

Yani, yerine [1,2]hangi bir olduğunu listve mutasyona uğramış edilebilen, (1,2)bir olduğunu tupleve olamaz.


Daha fazla bilgi:

Bir tek eleman tupleyazarak somutlaştırılamaz (1), bunun yerine yazmanız gerekir (1,). Bunun nedeni, yorumlayıcının parantez için başka çeşitli kullanımlara sahip olmasıdır.

Ayrıca tamamen parantez ile uzak yapabilirsiniz: 1,2aynıdır(1,2)

Bir demetin tam olarak değişmez bir liste olmadığını unutmayın . Listeler ve demetler arasındaki farklar hakkında daha fazla bilgi edinmek için burayı tıklayın


6
Ayrıca, demet içine doğal olarak değiştirilebilir nesne işaretçileri yerleştirirseniz (örneğin ([1,2],3)), başlık artık gerçekten değişmez değildir, çünkü liste nesnesi yalnızca değiştirilebilir bir nesneye göstericidir ve işaretçi değişmezken, başvurulan nesne değildir.
Nisan H

2
ayrıca, bu kadar basit bir soruyu yanıtladığınızda, en azından performans farklılıkları (biraz daha hızlı tuple) ve tuplelar dikte tuşları olarak kullanılabilir, oysa liste gibi daha fazla açıklama sağlayın. Eminim başka birçok farklılık da vardır.
BrtH

3
Aslında boş bir demet de yazılabilir (). Parantezlerin gerekli olduğu tek durum budur.
RemcoGerlich

1
@Kane, ifadeniz yazılan işlevsel dillerde kesinlikle doğrudur; özellikle, (3,4,5)çok farklı bir (int x int x int)türe [3,4,5]sahiptir - - türüne göre (listof int). Ancak, python en tanımlama grubu gerçekten daha yakın bir değişmez listeye görünüyor: Özellikle, bunlar olabilir onlar da süzülür ve eşlenebilir görünür iterated ve edilecek.
John Clements

1
Bir demet bir Liste değildir, uyumlu davranışları yoktur ve bunları polimorfik olarak kullanamazsınız.
jeremyjjbrown

7

İşte bir ImmutableList uygulaması. Altta yatan liste herhangi bir doğrudan veri üyesinde açığa çıkmaz. Yine de, üye işlevinin kapatma özelliği kullanılarak erişilebilir . Yukarıdaki özelliği kullanarak kapatmanın içeriğini değiştirmeme kuralını izlersek, bu uygulama amaca hizmet edecektir. Bu ImmutableList sınıfının örneği, normal bir python listesinin beklendiği her yerde kullanılabilir.

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()

6

Lisp tarzı değişmez tek bağlantılı bir listeyi iki öğeli tuple kullanarak simüle edebilirsiniz (not: bu, çok daha az esnek olan bir demet oluşturan herhangi bir öğe tuple yanıtından farklıdır ):

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

Örneğin liste [1, 2, 3]için aşağıdakilere sahip olacaksınız:

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

Standartınız carve cdrişlevleriniz basittir:

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

Bu liste tek başına bağlantılı olduğundan, öne eklenen O (1) 'dir. Bu liste değişmez olduğundan, listedeki temel öğeler de değişmez ise, başka bir listede yeniden kullanılmak üzere herhangi bir alt listeyi güvenle paylaşabilirsiniz.


Bu uygulama nasıl yerel demetten (a, b, c) daha esnektir?
Değişmez

@Literal Dondurulmuş olan normal bir demetten farklı olarak, tekil bağlantılı bir listenin başına ekleyebilirsiniz. Bu, onları çok daha çok yönlü ve işlevsel programlama dillerinde bir temel yapan şeydir.
kevinji

Cevabınız için teşekkürler. Hâlâ bu uygulamanın faydasını anlamaya çalışıyorum, çünkü yeni bir tuple örneği oluşturarak da bir öğeyi başa ekleyebilirim: (z,) + (a, b, c). Bu bir performans meselesi mi?
Değişmez

4

Ancak bir dizi ve tuple varsa, bir demet içindeki dizi değiştirilebilir.

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

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

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

9
İçeriğini değişmez kılan bir koleksiyon diye bir şey gerçekten olamaz, çünkü rastgele nesnelerin değişmez bir kopyasını yapmanın bir yolunu bulmanız gerekir. Bunu yapmak için, bu nesnelerin ait olduğu sınıfları ve hatta referans verdikleri yerleşik sınıfları kopyalamanız gerekir. Ve yine de, nesneler dosya sistemine veya ağa veya her zaman değiştirilebilir olacak başka bir şeye başvurabilir. Dolayısıyla, keyfi bir nesneyi değişmez yapamayacağımız için, değişmez değişken nesnelerin koleksiyonlarından memnun olmalıyız.
Jack O'Connor

1
@ JackO'Connor Tamamen aynı fikirde değil. Her şey dünyayı nasıl modellediğinize bağlıdır: dışsal değişkenlik her zaman zaman içinde gelişen durumlar olarak modellenebilir ve tek bir değişken durumu sürdürmek yerine, her zaman değişmez olan s_t'ye başvurmayı seçebilirim. "Değişmez nesnelerin değişmez koleksiyonu" <- Huskell, Scala ve diğer işlevsel programlama dillerine göz atın. Python öğrenmeye başlamadan önce, Python'un diğerlerinden duyduklarımdan tam olarak değişmezlik ve fp desteğine sahip olduğuna inanıyordum, ama bu doğru değil.
Kane

Python'da gerçekten böyle bir şey olamaz demeliydim. Python'un değişmezliği _private_variables, yorumlayıcının herhangi bir yaptırımı yerine programcının kurallara (gibi ) saygı duymasına dayanır .
Jack O'Connor

1
Haskell gibi bir dil çok daha fazla garanti sağlar, ancak programcı gerçekten kötü olmak isterse, yine de /proc/#/memgüvenli olmayan kitaplıklara yazabilir veya bunlara bağlanabilir veya modeli bozabilecek her şeyi yapabilir.
Jack O'Connor

1

List ve Tuple çalışma tarzlarında farklılık gösterir.

LIST'de oluşturulduktan sonra değişiklikler yapabiliriz, ancak ileride hiçbir değişikliğin uygulanamayacağı sıralı bir sıra istiyorsanız TUPLE'ı kullanabilirsiniz.

daha fazla bilgi::

 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

-1

Tuple yerine frozenset'i kullanabilirsiniz. frozenset, değişmez bir set oluşturur. listeyi frozenset'in bir üyesi olarak kullanabilir ve tek for döngüsünü kullanarak frozenset içindeki listenin her öğesine erişebilirsiniz.


3
frozenset, ayarlanan üyelerinin hashable olmasını gerektirir, ancak bir liste değildir.
matias elgart
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.