Bagaimana cara saya mendapatkan jumlah elemen dalam daftar?

1938

Pertimbangkan yang berikut ini:

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

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

Bagaimana cara saya mendapatkan jumlah elemen dalam daftar items?

y2k
sumber
23
Anda jelas meminta jumlah elemen dalam daftar. Jika seorang pencari datang ke sini mencari ukuran objek dalam memori, ini adalah pertanyaan & jawaban aktual yang mereka cari: Bagaimana cara menentukan ukuran objek dalam Python?
Aaron Hall

Jawaban:

2640

The len()fungsi dapat digunakan dengan beberapa jenis yang berbeda dengan Python - baik built-in tipe dan jenis perpustakaan. Sebagai contoh:

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

Dokumentasi 2.x resmi ada di sini: Dokumentasi resmi 3.x ada di sini:len()
len()

Agak
sumber
239

Bagaimana cara mendapatkan ukuran daftar?

Untuk menemukan ukuran daftar, menggunakan fungsi builtin, len:

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

Dan sekarang:

len(items)

mengembalikan 3.

Penjelasan

Segala sesuatu di Python adalah objek, termasuk daftar. Semua objek memiliki semacam header dalam implementasi C.

Daftar dan objek builtin serupa lainnya dengan "ukuran" di Python, khususnya, memiliki atribut yang disebut ob_size, di mana jumlah elemen dalam objek di-cache. Jadi memeriksa jumlah objek dalam daftar sangat cepat.

Tetapi jika Anda memeriksa apakah ukuran daftar nol atau tidak, jangan gunakan len- sebagai gantinya, masukkan daftar dalam konteks boolean - itu diperlakukan sebagai False jika kosong, Benar sebaliknya .

Dari dokumen

len(s)

Mengembalikan panjang (jumlah item) suatu objek. Argumennya bisa berupa urutan (seperti string, byte, tuple, daftar, atau rentang) atau koleksi (seperti kamus, set, atau set beku).

lendiimplementasikan dengan __len__, dari dokumen model data :

object.__len__(self)

Dipanggil untuk mengimplementasikan fungsi bawaan len(). Harus mengembalikan panjang objek, bilangan bulat> = 0. Juga, objek yang tidak mendefinisikan metode __nonzero__()[dengan Python 2 atau __bool__()Python 3] dan __len__()metode yang mengembalikan nol dianggap salah dalam konteks Boolean.

Dan kita juga bisa melihat itu __len__adalah metode daftar:

items.__len__()

mengembalikan 3.

Jenis builtin Anda bisa mendapatkan len(panjang) dari

Dan sebenarnya kita melihat kita bisa mendapatkan informasi ini untuk semua jenis yang dijelaskan:

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

Jangan gunakan lenuntuk menguji daftar kosong atau kosong

Untuk menguji panjang tertentu, tentu saja, cukup menguji kesetaraan:

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

Tetapi ada kasus khusus untuk pengujian untuk daftar panjang nol atau kebalikannya. Dalam hal itu, jangan menguji kesetaraan.

Juga, jangan lakukan:

if len(items): 
    ...

Sebaliknya, lakukan saja:

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

atau

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

Saya jelaskan mengapa di sini tetapi singkatnya, if itemsatau if not itemskeduanya lebih mudah dibaca dan lebih berkinerja.

Aaron Hall
sumber
75

Walaupun ini mungkin tidak berguna karena fakta bahwa itu akan jauh lebih masuk akal sebagai fungsi "di luar kotak", peretasan yang cukup sederhana adalah membangun kelas dengan lengthproperti:

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

Anda dapat menggunakannya seperti ini:

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

Pada dasarnya, ini persis identik dengan objek daftar, dengan manfaat tambahan dari memiliki lengthproperti ramah-OOP .

Seperti biasa, jarak tempuh Anda mungkin bervariasi.

Naftuli Kay
sumber
19
asal Anda tahu, Anda bisa melakukan length = property(len)dan melewatkan fungsi pembungkus satu baris dan menyimpan dokumentasi / introspeksi lendengan properti Anda.
Tadhg McDonald-Jensen
17

Selain itu lenAnda juga dapat menggunakan operator.length_hint(membutuhkan Python 3.4+). Untuk yang normal listkeduanya sama, tetapi length_hintmemungkinkan untuk mendapatkan panjang daftar-iterator, yang bisa berguna dalam keadaan tertentu:

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

Tapi length_hintmenurut definisi hanya "petunjuk", jadi sebagian besar waktu lenlebih baik.

Saya telah melihat beberapa jawaban yang menyarankan mengakses __len__. Ini baik-baik saja ketika berhadapan dengan kelas bawaan seperti list, tetapi bisa menimbulkan masalah dengan kelas khusus, karena len(dan length_hint) menerapkan beberapa pemeriksaan keamanan. Misalnya, keduanya tidak memungkinkan panjang negatif atau panjang yang melebihi nilai tertentu ( sys.maxsizenilai). Jadi selalu lebih aman menggunakan lenfungsi daripada __len__metode!

MSeifert
sumber
8

Menjawab pertanyaan Anda sebagai contoh juga diberikan sebelumnya:

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

print items.__len__()
Shai Alon
sumber
15
Dalam Python, nama yang dimulai dengan garis bawah adalah metode non-publik semantik dan tidak boleh digunakan oleh pengguna.
Aaron Hall
2
1 __foo__.: ini hanya sebuah konvensi, cara bagi sistem Python untuk menggunakan nama yang tidak akan bertentangan dengan nama pengguna. 2 _foo.: ini hanya sebuah konvensi, cara bagi pemrogram untuk menunjukkan bahwa variabel bersifat pribadi (apa pun artinya dengan Python). 3 __foo.: ini memiliki arti nyata: penerjemah menggantikan nama ini dengan _classname__foocara untuk memastikan bahwa nama tersebut tidak akan tumpang tindih dengan nama yang serupa di kelas lain. * Tidak ada bentuk garis bawah yang memiliki makna di dunia Python. * Tidak ada perbedaan antara kelas, variabel, global, dll dalam konvensi ini.
Shai Alon
4
T&J ini menjelaskan mengapa Anda tidak boleh menggunakan metode khusus secara langsung sebagai pengguna: stackoverflow.com/q/40272161/541136
Aaron Hall
@ AaronHall tapi untuk fungsi len hampir sama. Mungkin lebih cepat untuk variabel yang sangat besar. Namun, saya mengerti maksud Anda dan kami harus menggunakan len (obj) dan bukan obj .__ len __ ().
Shai Alon
7

Dan untuk kelengkapan (terutama pendidikan), dimungkinkan tanpa menggunakan len()fungsi. Saya tidak akan memaafkan ini sebagai opsi yang baik JANGAN PROGRAM SEPERTI INI DI PYTHON , tetapi ini melayani tujuan untuk mempelajari algoritma.

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

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

(Usus besar dalam list[:]adalah implisit dan karena itu juga opsional.)

Pelajaran di sini untuk programmer baru adalah: Anda tidak bisa mendapatkan jumlah item dalam daftar tanpa menghitungnya di beberapa titik. Pertanyaannya menjadi: kapan waktu yang tepat untuk menghitungnya? Misalnya, kode berperforma tinggi seperti sambungkan sistem panggilan untuk soket (ditulis dalam C) connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);, tidak menghitung panjang elemen (memberikan tanggung jawab itu pada kode panggilan). Perhatikan bahwa panjang alamat dilewatkan untuk menyimpan langkah penghitungan panjang terlebih dahulu? Pilihan lain: secara komputasi, mungkin masuk akal untuk melacak jumlah item saat Anda menambahkannya dalam objek yang Anda lewati. Pikiran bahwa ini membutuhkan lebih banyak ruang dalam memori. Lihat jawaban Naftuli Kay .

Contoh melacak panjang untuk meningkatkan kinerja sambil mengambil lebih banyak ruang di memori. Perhatikan bahwa saya tidak pernah menggunakan fungsi len () karena panjangnya dilacak:

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
sumber
Mengapa for item in list[:]:? Mengapa tidak for item in list:? Juga, saya gunakan += 1untuk menambah.
Nenek Mencapai
@GrannyAching Saya secara eksplisit menyebutkan titik dua opsional (range specifier). Saya meninggalkan range specifier di sana untuk tujuan pendidikan - ada baiknya mengetahui bahwa itu tersirat. Tipe daftar [] juga disimpulkan, seperti yang Anda sarankan-setara dengan kode saya. Operator kenaikan juga setara dengan menambahkan 1 ke variabel yang ada, namun lebih pendek dalam semua kasus. Jadi saya setuju bahwa itu harus digunakan jika itu alasan Anda. Kode ini seharusnya tidak dimasukkan ke dalam produksi di mana saja, kecuali (kecuali ketika belajar pemrograman).
Jonathan Komar
0

Dalam hal bagaimana len()sebenarnya bekerja, ini adalah implementasi C-nya :

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_tadalah panjang maksimum yang dapat dimiliki objek. PyObject_Size()adalah fungsi yang mengembalikan ukuran suatu objek. Jika tidak dapat menentukan ukuran suatu objek, ia mengembalikan -1. Dalam hal ini, blok kode ini akan dieksekusi:

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

Dan sebagai hasilnya muncul pengecualian. Jika tidak, blok kode ini akan dieksekusi:

return PyLong_FromSsize_t(res);

resyang merupakan Cbilangan bulat, diubah menjadi python longdan dikembalikan. Semua bilangan bulat python disimpan seperti longssejak Python 3.

Alec Alameddine
sumber
3
Mengapa mengetahui, atau mengetahui tentang masalah implementasi C?
cs95
Karena pertanyaan ini tidak spesifik untuk CPython, jawaban ini mungkin menyesatkan. PyPy, IronPython, ... dapat dan mengimplementasikannya secara berbeda.
MSeifert