一种在不进行类型条件判断的情况下遍历列表或字典值的方法

编程语言 2026-07-12

我在遍历一个来自第三方的JSON数据结构,代码对它所知甚少,但需要对其进行统计等信息的收集。我正在读取该数据结构中的一个元素,它可能属于多种类型中的任意一种,并试图尽量用同一份代码来处理,以降低代码的复杂性与重复性。

从这个JSON中取出的变量可能是一个列表或一个字典,我希望能够调用一个单一的函数,对两者中的所有值执行某些操作。

因此:

def do_stuff_to_values(obj):
    for v in obj.values():
        # do stuff with v

但如果按这种方式实现,它就只对字典起作用,因为列表没有 values() 方法。

我可以使用 isinstance 来检查它是否是字典:

def do_stuff_to_values(obj):
    for v in (obj.values() if isinstance(obj, Mapping) else obj):
        # do stuff with v

有没有办法在Python中表达让字典和列表得到相同的处理(其实字符串、元组、数组、集合也一样)?

似乎所有内置的序列类型都应该有一个values的迭代器方法,为什么它们没有呢?还是我遗漏了什么?

这是我一直在测试的代码:

mydict = {'k1': 'dv1', 'k2': 'dv2'}

myarray = ['av1', 'av2']

def get_values(obj):
    return [i for i in (obj.values() if isinstance(obj, Mapping) else obj)]

print(get_values(mydict))
print(get_values(myarray))

Python-Fiddle

解决方案

结合从他人及自身学到的东西来回答我自己的问题。

首先,字典在Python中应被理解为映射(Mappings),与序列(Sequences)区分开来。尽管现在它们是有序的;从概念上讲,它们没有values(),相反它们表示的是一个从 "from" 和 "to" 部分之间的映射。这就是它们没有values() 属性的原因。

对Mappings和 Sequences采用相同处理的代码应谨慎使用,通常最好让函数对被操作的类型有明确的预期。

因为它们在概念上是分离的,所以没有内置的方式在不显式区分它们的情况下处理它们。

如果出于设计原因需要这样做,那么通常更倾向于使用显式条件,例如isinstance。

就我而言,我的代码正在对一个数据结构进行分析并收集信息,结果大概是这样的:

from collections.abc import Iterable, Mapping, Sequence

    def walk_datastructure(obj, depth = 0):
        nonlocal max_depth
        max_depth = depth if depth > max_depth else max_depth

        types_found_set.add(type(obj).__name__)

        if not isinstance(obj, Iterable) or isinstance(obj, str):
            process_value(obj)
        else:
            for ref, value in obj.items() if isinstance(obj, Mapping) else enumerate(obj):
                process_object(obj, ref)
                walk_datastructure(value, depth + 1)

确实可以创建让它们以相同方式对待的代码,但在有限的场景下才有意义。

from collections.abc import Iterable, Mapping, Sequence
from functools import singledispatch

@singledispatch
def object_itervalues(arg):
    assert isinstance(arg, Iterable)
    return arg

@object_itervalues.register
def _(arg: Mapping):
    return arg.values()

def test(obj):
    for i in object_itervalues(obj):
        print(i)

test(object_itervalues([1,3,2]))  # list argument
test(object_itervalues((1,3,2)))  # tuple argument
test(object_itervalues({"A":1,"B":3,"C":2}))  # dict argument

或者使用内置的条件表达式:

from collections.abc import Mapping

def object_itervalues(obj):
    if isinstance(obj, Mapping):
        return obj.values()
    return obj

感谢所有回复者,特别是Daweo、dROOOze、deceze、Paulo Oliveira。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章