จะตรวจสอบว่าตัวแปรเป็นพจนานุกรมใน Python ได้อย่างไร?


214

คุณจะตรวจสอบว่าตัวแปรเป็นพจนานุกรมในไพ ธ อนได้อย่างไร?

ตัวอย่างเช่นฉันต้องการให้วนลูปผ่านค่าในพจนานุกรมจนกว่าจะพบพจนานุกรม จากนั้นวนซ้ำที่พบ:

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)

นอกจากนี้stackoverflow.com/questions/378927/… (ซึ่งทำเครื่องหมายว่าซ้ำกับด้านบน)
NPE


40
ไม่มันไม่ใช่คำถามเดียวกัน คำตอบสำหรับคำถามนี้และคำถามอื่น ๆ ที่แสดงไว้ที่นี่ล้วนมีข้อมูลที่เหมือนกัน แต่คำตอบของ "วิธีตรวจสอบว่าตัวแปรเป็นพจนานุกรมในภาษาไพ ธ อน" คือ "Use type () หรือ isinstance ()" ซึ่งจะนำไปสู่คำถามใหม่ซึ่งเป็นความแตกต่างระหว่างประเภท () และ isinstance () คืออะไร . แต่คนที่ถามคำถามแรกไม่สามารถรู้ได้จนกว่าจะตอบคำถามแรก ดังนั้นคำถามที่แตกต่างซึ่งสำคัญเมื่อคุณกำลังมองหาคำถามของคุณในเว็บไซต์

13
ฉันเห็นด้วยกับ @mbakeranalecta ฉันมาที่นี่เพื่อค้นหาคำตอบของคำถาม "วิธีตรวจสอบว่าตัวแปรเป็นพจนานุกรมใน Python หรือไม่" และฉันก็ไม่เคยคิดที่จะค้นหาคำตอบของฉันใน "ความแตกต่างระหว่าง isinstance () และ type () ใน python"
lodebari

5
isinstance(v, collections.abc.Mapping)สำหรับการตรวจสอบถ้าตัวแปรเป็นพจนานุกรมโดยเฉพาะอย่างยิ่งคุณอาจจะใช้ กล่าวอีกนัยหนึ่งนี่ไม่ใช่การซ้ำซ้อนของ "ความแตกต่างระหว่าง isinstance () และ type ()"
Josh Kelley

คำตอบ:


279

คุณสามารถใช้if type(ele) is dictหรือใช้isinstance(ele, dict)งานได้หากคุณมีคลาสย่อย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)

70
ฉันลงคะแนนคำตอบนี้เพราะคำตอบที่ถูกต้องสำหรับคำถามทั่วไปคือ: isinstance(ele, collections.Mapping). การทำงานสำหรับdict(), และcollections.OrderedDict() collections.UserDict()ตัวอย่างในคำถามนั้นเฉพาะเจาะจงมากพอสำหรับคำตอบของ Padriac ในการทำงาน แต่มันก็ไม่ดีพอสำหรับกรณีทั่วไป
Alexander Ryzhov

3
ฉันลงคะแนนคำตอบนี้เพราะถ้าคุณจะเรียกele.items()ว่าทำไมคุณกำลังตรวจสอบประเภท? EAFP / เป็ดพิมพ์ทำงานที่นี่เพียงห่อในfor k,v in ele.items() try...except (AttributeError, TypeError)หากยกเว้นจะยกคุณรู้ว่าeleมีใครitemsที่อัตราผลตอบแทนถัว iterable ...
cowbert

@Padraic Cunningham กรณีขอบสมมุติของคุณต้องการคลาสที่กำหนดเอง "100% ไม่ใช่ dict" ของคุณ 1. มีitems()วิธีที่ 2 ซึ่งเพิ่งเกิดขึ้นเพื่อให้ได้ iterable และ 3 ซึ่งแต่ละองค์ประกอบของ iterable นั้นสามารถแสดงเป็น 2 -tuple ณ จุดนี้วัตถุที่กำหนดเองของคุณได้ติดตั้ง dict ครึ่งหนึ่งแล้ว สำหรับวัตถุประสงค์ของรหัสที่แสดงรายการเราใส่ใจเฉพาะการขยายองค์ประกอบที่ซ้อนกันแบบมีเงื่อนไขและวัตถุที่กำหนดเองของคุณใช้สิ่งนั้นอยู่แล้ว (เป็นบิตแดกดันว่าคุณวิพากษ์วิจารณ์ความคิดเห็น @Alexander Ryzhov สำหรับเป็นกว้างเกินไป แต่ตอนนี้เพิ่มเป็นกรณีทั่วไป)
cowbert

1
@PadraicCunningham ฉันไม่ควรสอนคนที่ไม่คุ้นเคยมากเกินไปกับรูปแบบการต่อต้าน Python เริ่มต้นด้วย มีกรณีการใช้งานน้อยมากใน Python ที่ต้องการการพิมพ์แบบชัดแจ้งซึ่งส่วนใหญ่มาจากการสืบทอดการใช้งานที่ไม่ดีในการเริ่มต้นด้วย ('วัตถุของพระเจ้า, การสร้างมาตรฐานภาษา / ไลบรารี / ภาษาอื่น ๆ ) คำถามเดิมเป็นปัญหา XY ทำไม OP ต้องตรวจสอบประเภท? เพราะตามรหัสของพวกเขาสิ่งที่พวกเขาต้องการทำจริงๆคือการตรวจสอบว่ารายการในคอลเลกชันของพวกเขามีพฤติกรรมเหมือนคอลเลกชันหรือitems()ไม่
cowbert

4
@AlexanderRyzhov ทำไมไม่โพสต์วิธีการที่เป็นคำตอบ? BTW ตามที่ Josh Kelley แสดงความคิดเห็นไว้แล้วแนะนำcollections.abc.Mappingอาจมีความเหมาะสมที่จะเหมือนกัน แต่collections.abcไม่สามารถใช้ได้ก่อน Python 3.3 collections.Mappingนามแฝงยังคงมีอยู่ใน Python 3.6 แต่ไม่มีเอกสารดังนั้นจึงควรเลือกcollections.abc.Mapingใช้
Yushin Washio

5

คุณจะตรวจสอบว่าตัวแปรเป็นพจนานุกรมใน Python หรือไม่?

นี่เป็นคำถามที่ดี แต่มันเป็นโชคร้ายที่ upvoted type(obj) is dictที่สุดนำไปสู่คำตอบที่มีคำแนะนำที่น่าสงสาร

(โปรดทราบว่าคุณไม่ควรใช้dictเป็นชื่อตัวแปร - เป็นชื่อของวัตถุบิวด์อิน)

หากคุณกำลังเขียนโค้ดที่ผู้อื่นจะนำเข้าและใช้งานอย่าสันนิษฐานว่าพวกเขาจะใช้ dict ในตัวโดยตรงทำให้ข้อสันนิษฐานนั้นทำให้โค้ดของคุณยืดหยุ่นมากขึ้นและในกรณีนี้ให้สร้างข้อบกพร่องที่ซ่อนได้ง่ายซึ่งไม่ผิดพลาดโปรแกรม .

ฉันขอแนะนำอย่างยิ่งสำหรับวัตถุประสงค์ของความถูกต้องความสามารถในการบำรุงรักษาและความยืดหยุ่นสำหรับผู้ใช้ในอนาคตไม่ควรมีความยืดหยุ่นน้อยกว่าและไม่มีการแสดงออกในรหัสของคุณเมื่อมีความยืดหยุ่นมากกว่า

isเป็นการทดสอบเอกลักษณ์ของวัตถุตัวตนของวัตถุมันไม่สนับสนุนการสืบทอดมันไม่สนับสนุนสิ่งที่เป็นนามธรรมและไม่สนับสนุนอินเทอร์เฟซ

ดังนั้นฉันจะให้ตัวเลือกมากมายที่ทำ

สนับสนุนการสืบทอด:

นี่คือคำแนะนำแรกที่ผมจะทำเพราะจะช่วยให้ผู้ใช้สามารถจัดหา subclass ของตัวเองของ Dict หรือOrderedDict, defaultdictหรือCounterจากคอลเลกชัน Module:

if isinstance(any_object, dict):

แต่มีตัวเลือกที่ยืดหยุ่นมากกว่า

รองรับ abstractions:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

วิธีนี้ช่วยให้ผู้ใช้รหัสของคุณสามารถใช้การแมปแบบนามธรรมที่กำหนดเองซึ่งรวมถึงคลาสย่อยของ dictและยังคงได้รับพฤติกรรมที่ถูกต้อง

ใช้ส่วนต่อประสาน

คุณมักได้ยินคำแนะนำ OOP "โปรแกรมไปยังส่วนต่อประสาน"

กลยุทธ์นี้ใช้ประโยชน์จากความหลากหลายของงูหลามหรือการพิมพ์เป็ด

ดังนั้นเพียงแค่พยายามที่จะเข้าถึงอินเตอร์เฟซจับข้อผิดพลาดเฉพาะที่คาดหวัง ( AttributeErrorในกรณีที่ไม่มี.itemsและTypeErrorในกรณีที่itemsไม่ได้เป็น callable) มีทางเลือกที่เหมาะสม - และตอนนี้ระดับใด ๆ ที่ดำเนินการที่อินเตอร์เฟซที่จะทำให้คุณรายการของมัน (หมายเหตุ.iteritems()จะหายไปในหลาม 3):

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

บางทีคุณอาจคิดว่าการใช้การพิมพ์เป็ดแบบนี้ไปไกลเกินกว่าจะยอมให้มีผลบวกปลอมมากเกินไปและอาจขึ้นอยู่กับวัตถุประสงค์ของรหัสนี้

ข้อสรุป

อย่าใช้isเพื่อตรวจสอบประเภทของโฟลว์การควบคุมมาตรฐาน ใช้isinstanceพิจารณา abstractions เช่นMappingหรือMutableMappingและพิจารณาหลีกเลี่ยงการตรวจสอบชนิดโดยใช้อินเตอร์เฟสโดยตรง


3

OP ไม่ได้ยกเว้นตัวแปรเริ่มต้นดังนั้นเพื่อความสมบูรณ์ในที่นี้คือวิธีจัดการกรณีทั่วไปของการประมวลผลพจนานุกรมที่คาดคะเนซึ่งอาจรวมถึงรายการเป็นพจนานุกรม

ทำตามวิธีการที่แนะนำโดย Python บริสุทธิ์ (3.8) เพื่อทดสอบพจนานุกรมในความคิดเห็นด้านบน

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)

dict.iteritems()ไม่มีอยู่ใน Python 3 คุณควรใช้dict.items()แทน
Arkelis

@Arkelis อ๊ะเพิ่งคัดลอก / วางส่วนนั้นขอบคุณที่ชี้ให้เห็น - แก้ไขแล้วตอนนี้
CodeMantle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.