Bagaimana cara memeriksa apakah variabel adalah kamus dengan Python?

214

Bagaimana Anda memeriksa apakah variabel adalah kamus dengan python?

Sebagai contoh, saya ingin untuk mengulang nilai-nilai dalam kamus sampai menemukan kamus. Kemudian, lewati yang ditemukannya:

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}
for k, v in dict.iteritems():
    if ###check if v is a dictionary:
        for k, v in v.iteritems():
            print(k, ' ', v)
    else:
        print(k, ' ', v)
Riley
sumber
Juga stackoverflow.com/questions/378927/… (yang ditandai sebagai duplikat dari yang di atas).
NPE
40
Tidak, itu bukan pertanyaan yang sama. Jawaban untuk pertanyaan ini dan untuk pertanyaan-pertanyaan lain yang tercantum di sini semuanya mengandung informasi yang sama. Tetapi jawaban untuk "Bagaimana memeriksa apakah variabel adalah kamus dengan python" adalah "Gunakan tipe () atau isinstance ()" yang kemudian mengarah ke pertanyaan baru, yang merupakan perbedaan antara tipe () dan isinstance () . Tetapi orang yang mengajukan pertanyaan pertama tidak mungkin mengetahuinya sampai pertanyaan pertama dijawab. Ergo, pertanyaan berbeda, yang penting ketika Anda mencari pertanyaan Anda di situs.
13
Saya setuju dengan @mbakeranalecta saya datang ke sini mencari jawaban untuk pertanyaan "Bagaimana cara memeriksa apakah variabel adalah kamus dengan Python?" dan saya tidak akan pernah berpikir untuk melihat jawaban saya menjadi "Perbedaan antara isinstance () dan ketik () dengan python".
lodebari
5
Untuk memeriksa apakah suatu variabel adalah kamus khususnya, Anda sebaiknya menggunakan isinstance(v, collections.abc.Mapping). Dengan kata lain, ini bukan duplikat tepat dari "Perbedaan antara isinstance () dan type ()."
Josh Kelley

Jawaban:

279

Anda dapat menggunakan if type(ele) is dictatau menggunakan isinstance(ele, dict)yang akan berfungsi jika Anda telah mensubklasifikasikan dict:

d = {'abc':'abc','def':{'ghi':'ghi','jkl':'jkl'}}
for ele in d.values():
    if isinstance(ele,dict):
       for k, v in ele.items():
           print(k,' ',v)
Padraic Cunningham
sumber
70
Aku turun-sebagai jawaban ini karena jawaban yang tepat untuk pertanyaan umum adalah: isinstance(ele, collections.Mapping). Ia bekerja untuk dict(), collections.OrderedDict(), dan collections.UserDict(). Contoh dalam pertanyaan ini cukup spesifik untuk jawaban Padriac untuk bekerja, tetapi itu tidak cukup baik untuk kasus umum.
Alexander Ryzhov
4
Saya menurunkan jawaban ini karena jika Anda akan memohon ele.items()mengapa Anda memeriksa tipe? EAFP / bebek-mengetik bekerja di sini, hanya membungkus for k,v in ele.items()di try...except (AttributeError, TypeError). Jika pengecualian dinaikkan, Anda tahu eletidak ada itemsyang menghasilkan iterable ...
cowbert
@ Padraic Cunningham Kasing tepi hipotetis Anda mengharuskan kelas khusus "100% bukan dict" Anda memiliki items()metode 2. yang kebetulan menghasilkan iterable dan 3. di mana setiap elemen iterable dapat direpresentasikan sebagai 2 -Tuple. Pada titik ini objek kustom Anda sudah menerapkan setengah dari dikt. Untuk keperluan kode yang tercantum, kami hanya peduli tentang perluasan bersyarat elemen bersarang, dan objek kustom Anda sudah mengimplementasikannya. (Agak ironis bahwa Anda mengkritik komentar @Alexander Ryzhov karena terlalu umum tetapi sekarang mengajukan kasus umum)
cowbert
1
@ PadraicCunningham Saya lebih suka tidak mengajarkan orang-orang-yang-tidak-terlalu-akrab-dengan-pola Python untuk memulai. Ada sangat sedikit use-case dalam Python yang memerlukan pengetikan ketik secara eksplisit - sebagian besar berasal dari mewarisi implementasi yang buruk untuk memulai dengan ('objek dewa, mengesampingkan pustaka / konstruksi bahasa standar, dll.) Pertanyaan asli itu sendiri merupakan masalah XY. Mengapa OP perlu memeriksa tipe? Karena menurut kode mereka apa yang benar-benar ingin mereka lakukan adalah memeriksa apakah suatu item dalam koleksi mereka berperilaku seperti koleksi (mengimplementasikan items()yang menghasilkan iterable bersarang).
cowbert
4
@AlexanderRyzhov Mengapa tidak memposting pendekatan itu sebagai jawaban? BTW sebagai Josh Kelley telah berkomentar di atas menyarankan collections.abc.Mapping, catatan mungkin sesuai bahwa mereka sama, tetapi collections.abctidak tersedia sebelum Python 3.3. collections.Mappingalias masih tersedia di Python 3.6, tetapi tidak didokumentasikan, jadi mungkin orang harus memilih collections.abc.Maping.
Yushin Washio
5

Bagaimana Anda memeriksa apakah variabel adalah kamus dengan Python?

Ini adalah pertanyaan yang sangat bagus, tetapi sangat disayangkan bahwa jawaban yang paling dipilih mengarah dengan rekomendasi yang buruk type(obj) is dict,.

(Perhatikan bahwa Anda juga tidak boleh menggunakan dictsebagai nama variabel - itu adalah nama objek bawaan.)

Jika Anda menulis kode yang akan diimpor dan digunakan oleh orang lain, jangan menganggap bahwa mereka akan menggunakan dict builtin secara langsung - membuat anggapan itu membuat kode Anda lebih tidak fleksibel dan dalam hal ini, buat bug yang mudah disembunyikan yang tidak akan membuat kesalahan program. .

Saya sangat menyarankan, untuk keperluan koreksi, pemeliharaan, dan fleksibilitas untuk pengguna di masa depan, jangan pernah memiliki ekspresi yang kurang fleksibel dan tidak otomatis dalam kode Anda ketika ada lebih banyak, ekspresi idiomatik yang lebih fleksibel.

isadalah tes untuk identitas objek . Itu tidak mendukung warisan, tidak mendukung abstraksi apa pun, dan tidak mendukung antarmuka.

Jadi saya akan memberikan beberapa opsi yang bisa dilakukan.

Mendukung warisan:

Ini adalah rekomendasi pertama saya akan membuat, karena memungkinkan pengguna untuk memasok subclass mereka sendiri dict, atau OrderedDict, defaultdictatau Counterdari koleksi modul:

if isinstance(any_object, dict):

Tetapi ada opsi yang bahkan lebih fleksibel.

Abstraksi pendukung:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

Ini memungkinkan pengguna kode Anda untuk menggunakan implementasi kustom mereka sendiri dari Pemetaan abstrak, yang juga mencakup subkelas dari dict, dan masih mendapatkan perilaku yang benar.

Gunakan antarmuka

Anda biasanya mendengar saran OOP, "program ke antarmuka".

Strategi ini mengambil keuntungan dari polimorfisme Python atau mengetik bebek.

Jadi coba saja untuk mengakses antarmuka, menangkap kesalahan yang diharapkan spesifik ( AttributeErrorjika tidak ada .itemsdan TypeErrordalam kasus itemstidak dapat dipanggil) dengan mundur yang masuk akal - dan sekarang setiap kelas yang mengimplementasikan antarmuka itu akan memberi Anda barang-barangnya (catatan .iteritems()hilang dengan Python 3):

try:
    items = any_object.items()
except (AttributeError, TypeError):
    non_items_behavior(any_object)
else: # no exception raised
    for item in items: ...

Mungkin Anda mungkin berpikir menggunakan itik mengetik seperti ini terlalu jauh memungkinkan terlalu banyak kesalahan positif, dan mungkin saja, tergantung pada tujuan Anda untuk kode ini.

Kesimpulan

Jangan gunakan isuntuk memeriksa jenis untuk aliran kontrol standar. Gunakan isinstance, pertimbangkan abstraksi seperti Mappingatau MutableMapping, dan pertimbangkan untuk menghindari pengecekan tipe, menggunakan antarmuka secara langsung.

Aaron Hall
sumber
3

OP tidak mengecualikan variabel awal, jadi untuk kelengkapan di sini adalah bagaimana menangani kasus umum pemrosesan kamus yang mungkin menyertakan item sebagai kamus.

Juga mengikuti cara Python murni (3.8) yang direkomendasikan untuk menguji kamus dalam komentar di atas.

from collections.abc import Mapping

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}

def parse_dict(in_dict): 
    if isinstance(in_dict, Mapping):
        for k, v in in_dict.items():
            if isinstance(v, Mapping):
                for k, v in v.items():
                    print(k, v)
            else:
                print(k, v)

parse_dict(dict)
Mantel kode
sumber
dict.iteritems()tidak ada di Python 3, Anda harus menggunakannya dict.items().
Arkelis
@Arkelis Ups, baru saja menyalin / menempel bagian itu, terima kasih telah menunjukkannya - dikoreksi sekarang.
CodeMantle