Jak uzyskać liczbę elementów na liście?

1937

Rozważ następujące:

items = []
items.append("apple")
items.append("orange")
items.append("banana")

# FAKE METHOD:
items.amount()  # Should return 3

Jak uzyskać liczbę elementów na liście items?

y2k
źródło
23
Oczywiście pytasz o liczbę elementów na liście. Jeśli przychodzi tu poszukiwacz wielkości obiektu w pamięci, to właśnie tego pytania i odpowiedzi szukają: Jak określić rozmiar obiektu w Pythonie?
Aaron Hall

Odpowiedzi:

2640

Tej len()funkcji można używać z kilkoma różnymi typami w Pythonie - zarówno typami wbudowanymi, jak i typami bibliotek. Na przykład:

>>> len([1,2,3])
3

Oficjalna dokumentacja 2.x jest tutaj: Oficjalna dokumentacja 3.x jest tutaj:len()
len()

gnud
źródło
239

Jak uzyskać rozmiar listy?

Aby znaleźć rozmiar listy, użyj wbudowanej funkcji len:

items = []
items.append("apple")
items.append("orange")
items.append("banana")

I teraz:

len(items)

zwraca 3.

Wyjaśnienie

Wszystko w Pythonie jest obiektem, w tym listami. Wszystkie obiekty mają jakiś nagłówek w implementacji C.

W szczególności listy i inne podobne wbudowane obiekty o „rozmiarze” w Pythonie mają wywołany atrybut ob_size, w którym buforowana jest liczba elementów w obiekcie. Sprawdzanie liczby obiektów na liście jest więc bardzo szybkie.

Ale jeśli sprawdzasz, czy rozmiar listy wynosi zero, czy nie, nie używaj len- zamiast tego umieść listę w kontekście boolowskim - traktowana jest ona jako False, jeśli jest pusta, w przeciwnym razie True .

Z dokumentów

len(s)

Zwraca długość (liczbę elementów) obiektu. Argumentem może być sekwencja (taka jak ciąg, bajty, krotka, lista lub zakres) lub kolekcja (taka jak słownik, zestaw lub zestaw zamrożony).

lenjest implementowany z __len__, z dokumentów modelu danych :

object.__len__(self)

Wywoływany w celu wdrożenia wbudowanej funkcji len(). Powinna zwracać długość obiektu, liczba całkowita> = 0. Również obiekt, który nie definiuje metody __nonzero__()[w Python 2 lub __bool__()Python 3] i którego __len__()metoda zwraca zero, jest uważany za fałszywy w kontekście logicznym.

Widzimy też, że __len__jest to metoda list:

items.__len__()

zwraca 3.

Wbudowane typy, które możesz uzyskać len(długość)

W rzeczywistości widzimy, że możemy uzyskać te informacje dla wszystkich opisanych typów:

>>> all(hasattr(cls, '__len__') for cls in (str, bytes, tuple, list, 
                                            xrange, dict, set, frozenset))
True

Nie należy używać lendo testowania pustej lub niepustej listy

Aby przetestować określoną długość, po prostu przetestuj równość:

if len(items) == required_length:
    ...

Ale jest specjalny przypadek do testowania listy zerowej lub odwrotnej. W takim przypadku nie testuj równości.

Nie rób też:

if len(items): 
    ...

Zamiast tego po prostu wykonaj:

if items:     # Then we have some items, not empty!
    ...

lub

if not items: # Then we have an empty list!
    ...

I tu wyjaśnienie dlaczego , ale w skrócie, if itemsczy if not itemsjest zarówno bardziej czytelne i bardziej wydajnych.

Aaron Hall
źródło
75

Chociaż może to nie być przydatne ze względu na fakt, że miałoby o wiele większy sens jako funkcjonalność „po wyjęciu z pudełka”, dość prostym hackem byłoby zbudowanie klasy z lengthwłaściwością:

class slist(list):
    @property
    def length(self):
        return len(self)

Możesz go używać w następujący sposób:

>>> l = slist(range(10))
>>> l.length
10
>>> print l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Zasadniczo jest dokładnie identyczny z obiektem listy, z dodatkową zaletą posiadania lengthwłaściwości przyjaznej dla OOP .

Jak zawsze przebieg może się różnić.

Naftuli Kay
źródło
19
tylko dlatego, że wiesz, możesz po prostu length = property(len)pominąć funkcję owijania jednego wiersza i zachować dokumentację / introspekcję lenswojej właściwości.
Tadhg McDonald-Jensen
17

Poza tym lenmożesz także używać operator.length_hint(wymaga Python 3.4+). Dla normalnego listoba są równoważne, ale length_hintumożliwia uzyskanie długości iteratora listy, co może być przydatne w pewnych okolicznościach:

>>> from operator import length_hint
>>> l = ["apple", "orange", "banana"]
>>> len(l)
3
>>> length_hint(l)
3

>>> list_iterator = iter(l)
>>> len(list_iterator)
TypeError: object of type 'list_iterator' has no len()
>>> length_hint(list_iterator)
3

Ale length_hintz definicji jest tylko „wskazówką”, więc przez większość czasu lenjest lepiej.

Widziałem kilka odpowiedzi sugerujących dostęp __len__. Jest to w porządku, gdy mamy do czynienia z wbudowanymi klasami takimi jak list, ale może to prowadzić do problemów z klasami niestandardowymi, ponieważ len(i length_hint) wdrażają pewne kontrole bezpieczeństwa. Na przykład oba nie zezwalają na ujemne długości lub długości, które przekraczają określoną wartość ( sys.maxsizewartość). Dlatego zawsze bezpieczniej jest używać lenfunkcji zamiast __len__metody!

MSeifert
źródło
8

Odpowiadając na twoje pytanie jako przykłady podane wcześniej:

items = []
items.append("apple")
items.append("orange")
items.append("banana")

print items.__len__()
Shai Alon
źródło
15
W Pythonie nazwy rozpoczynające się od podkreślenia są semantycznie niepublicznymi metodami i nie powinny być używane przez użytkowników.
Aaron Hall
2
1 __foo__.: to tylko konwencja, sposób, w jaki system Python może używać nazw, które nie będą powodować konfliktów z nazwami użytkowników. 2 _foo.: to tylko konwencja, sposób dla programisty na wskazanie, że zmienna jest prywatna (cokolwiek to znaczy w Pythonie). 3 __foo.: to ma prawdziwe znaczenie: interpreter zastępuje tę nazwę _classname__foojako sposób, aby nazwa nie nakładała się na podobną nazwę w innej klasie. * Żadna inna forma podkreślenia nie ma znaczenia w świecie Python. * W tych konwencjach nie ma różnicy między klasą, zmienną, globalną itp.
Shai Alon,
4
To pytanie i wyjaśnienie, dlaczego nie powinieneś używać specjalnych metod bezpośrednio jako użytkownik: stackoverflow.com/q/40272161/541136
Aaron Hall
@AaronHall, ale dla funkcji len jest prawie taki sam. Może być szybszy dla bardzo dużych zmiennych. Rozumiem jednak twój punkt widzenia i powinniśmy używać len (obj), a nie obj .__ len __ ().
Shai Alon
7

A dla kompletności (przede wszystkim edukacyjnej) jest to możliwe bez użycia len()funkcji. Nie przyjąłbym tego za dobrą opcję. NIE PROGRAMUJ JAK TEGO W PYTHONIE , ale służy to celowi uczenia się algorytmów.

def count(list):
    item_count = 0
    for item in list[:]:
        item_count += 1
    return item_count

count([1,2,3,4,5])

(Dwukropek w list[:] jest ukryty i dlatego jest również opcjonalny.)

Lekcja dla nowych programistów brzmi: nie można uzyskać liczby pozycji na liście, nie licząc ich w pewnym momencie. Powstaje pytanie: kiedy należy je policzyć? Na przykład kod o wysokiej wydajności, taki jak wywołanie systemowe Connect dla gniazd (napisane w C) connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);, nie oblicza długości elementów (przypisując odpowiedzialność za kod wywołujący). Zauważ, że długość adresu jest przekazywana, aby zapisać krok liczenia długości jako pierwszy? Inna opcja: obliczeniowo sensowne może być śledzenie liczby elementów podczas dodawania ich w przekazywanym obiekcie. Pamiętaj, że zajmuje to więcej miejsca w pamięci. Zobacz odpowiedź Naftuli Kay .

Przykład śledzenia długości w celu poprawy wydajności przy jednoczesnym zajmowaniu większej ilości miejsca w pamięci. Zauważ, że nigdy nie używam funkcji len (), ponieważ długość jest śledzona:

class MyList(object):
    def __init__(self):
        self._data = []
        self.length = 0 # length tracker that takes up memory but makes length op O(1) time


        # the implicit iterator in a list class
    def __iter__(self):
        for elem in self._data:
            yield elem

    def add(self, elem):
        self._data.append(elem)
        self.length += 1

    def remove(self, elem):
        self._data.remove(elem)
        self.length -= 1

mylist = MyList()
mylist.add(1)
mylist.add(2)
mylist.add(3)
print(mylist.length) # 3
mylist.remove(3)
print(mylist.length) # 2
Jonathan Komar
źródło
Dlaczego for item in list[:]:? Dlaczego nie for item in list:? Chciałbym += 1również zwiększyć.
Babcia Boli
@GrannyAching wyraźnie wspomniałem o opcjonalnym dwukropku (specyfikator zakresu). Zostawiłem tam specyfikator zasięgu do celów edukacyjnych - warto wiedzieć, że jest to implikowane. Typ listy [] jest również wywnioskowany, jak sugerujesz, jako odpowiednik mojego kodu. Operator inkrementacji jest również równoważny z dodaniem 1 do istniejącej zmiennej, ale we wszystkich przypadkach jest krótszy. Zgadzam się więc, że należy tego użyć, jeśli takie jest twoje rozumowanie. Zresztą ten kod nie powinien być nigdzie produkowany (z wyjątkiem uczenia się programowania).
Jonathan Komar
0

Pod względem tego, jak len()faktycznie działa, jest to jego implementacja C :

static PyObject *
builtin_len(PyObject *module, PyObject *obj)
/*[clinic end generated code: output=fa7a270d314dfb6c input=bc55598da9e9c9b5]*/
{
    Py_ssize_t res;

    res = PyObject_Size(obj);
    if (res < 0) {
        assert(PyErr_Occurred());
        return NULL;
    }
    return PyLong_FromSsize_t(res);
}

Py_ssize_tto maksymalna długość, jaką może mieć obiekt. PyObject_Size()to funkcja zwracająca rozmiar obiektu. Jeśli nie może określić rozmiaru obiektu, zwraca -1. W takim przypadku ten blok kodu zostanie wykonany:

if (res < 0) {
        assert(PyErr_Occurred());
        return NULL;
    }

W rezultacie powstaje wyjątek. W przeciwnym razie ten blok kodu zostanie wykonany:

return PyLong_FromSsize_t(res);

resktóra jest Cliczbą całkowitą, jest konwertowana na python longi zwracana. Wszystkie liczby całkowite w Pythonie są przechowywane od longsczasu wydania Python 3.

Alec Alameddine
źródło
3
Dlaczego znajomość implementacji C ma znaczenie?
cs95
Ponieważ to pytanie nie jest specyficzne dla CPython, ta odpowiedź może wprowadzać w błąd. PyPy, IronPython, ... mogą i robią to inaczej.
MSeifert,