区分字典和字典列表的Pythonic方式是什么?

5

所以,我试图成为一名优秀的 Python 程序员并尽可能地使用鸭子类型,但我的输入是一个字典或字典的列表。

我不能区分它们是否可迭代,因为它们都是。

我的下一个想法就是简单地调用 list(x) 并希望返回我的列表完整且给我一个字典作为列表中唯一的项;可惜,它只给我了字典键的列表。

现在,我已经正式没有了想法(除了调用isinstance,众所周知,这不是很 Python)。我只想最终得到一个字典列表,即使我的输入只是一个单独的字典。


1
这个输入是从哪里来的?如果你能使它保持一致,那真的是你最好的选择。如果由于某些原因你不能这样做,那么把它尽可能靠近边缘处理是你唯一的选择... - abarnert
1
isinstance 不太符合 Python 风格?如果是这样,我将打破这个规则。此外,Python cookbook 中有一些使用它的示例。如果你们说 isinstance 不够 Pythonic,我会开始怀疑自己加入了什么样的修道院。 - erewok
它是一致的...它来自JSON。显然,isinstance对于几乎所有输入都有效。这实际上只是一个哲学问题...我之所以问,是因为我相信有一些聪明、简洁、Pythonic的方法我还没有想到,这会让我感觉更好。 - Morgan Harris
@erewok 我的意思是 isinstance 必然会阻止鸭子类型,这在我看来不符合 Python 的风格。 - Morgan Harris
为什么不用 x.items() 而是用 list(x)x.items() 会给你一个键值对列表。 - Akavall
显示剩余4条评论
5个回答

6
实际上,没有明显的Pythonic方法来做到这一点,因为这是一种不合理的输入格式,而明显的Pythonic方法是修复输入...
但如果你不能这样做,那么是的,你需要编写一个适配器(尽可能靠近输入边缘)。最好的方法取决于实际数据。如果它确实是一个字典或字典列表,而其他任何东西都不可能(例如,您正在对某些糟糕编写的服务返回的对象或对象数组调用json.loads),那么使用isinstance没有问题。
如果您想使其更加通用,可以使用适当的ABCs。例如:
if isinstance(dict_or_list, collections.abc.Mapping):
    return [dict_or_list]
else:
    return dict_or_list

除非你有某些需要这种普遍性的好理由,否则你只是在掩盖那些hacky的解决方法,而你最好尽可能让它可见。例如,如果它是从一些远程服务器的json.loads中出来的,处理一个不是dict的Mapping是没有用的,对吧?(如果你使用的是一些第三方客户端库,它只返回“类似于字典的东西”或“包含类似于字典的东西的列表”,那么使用ABCs。或者,如果该库甚至不支持适当的ABCs,你可以编写代码尝试特定的方法,如keys。但如果这是个问题,你会知道你要解决的具体细节,并且可以编写和记录适当的代码。)

是的。我更多地是在一般意义上想知道。我没有意识到甚至像dictlist这样的东西也继承自collections ABCs,不过,知道这点很酷。 - Morgan Harris
@MorganHarris:与Java接口相比,ABC的酷炫之处在于类可以向其中注册而无需继承它。因此,即使list实际上是一个内置类型,不继承任何东西,isinstance([], Sequence)仍然为True。 - abarnert
...什么?你怎么做到的?虽然我永远想象不到实际需要它,但知道这个还是很酷的。 - Morgan Harris
@MorganHarris:文档中已经解释了,但是看一下源代码可能更容易理解。看到MutableSequence.register(list)的调用了吗?这就足以告诉Python,list实现了MutableSequence(因此也实现了它的祖先SequenceSizedIterableContainer)。 - abarnert

3

使用非int键访问dict会得到一个项,或者是一个KeyError。使用一个list会得到一个TypeError。因此,您可以使用异常处理:

def list_dicts(dict_or_list):
    try:
        dict_or_list[None]
        return [dict_or_list]  # no error, we have a dict
    except TypeError:
        return dict_or_list    # wrong index type, we have a list
    except Exception:
        return [dict_or_list]  # probably KeyError but catch anything to be safe

该函数将给出一个包含字典的列表,无论它是否得到了列表或字典。(如果它得到一个字典,它会将其转换为一个项目的列表。)这应该是相当安全的类型,也很容易实现;其他类似于字典或列表的对象,如果它们没有类似的行为,可能会被认为是有问题的。

1
你可以检查是否存在一个items属性。 dict有这个属性而list没有。
>>> hasattr({}, 'items')
True

>>> hasattr([], 'items')
False

这里是Python 3.3.2中dictlist属性名称之间的差异完整列表。 list上的属性但不在dict上:
>>> print('\n'.join(sorted(list(set(dir([])) - set(dir({}))))))
__add__
__iadd__
__imul__
__mul__
__reversed__
__rmul__
append
count
extend
index
insert
remove
reverse
sort

dict上的属性,但不在list上:

>>> print('\n'.join(sorted(list(set(dir({})) - set(dir([]))))))
fromkeys
get
items
keys
popitem
setdefault
update
values

1
也许我太天真了,不过像这样的东西怎么样?
try:
    data.keys()
    print "Probs just a dictionary"    
except AttributeError:
    print "List o' dictionaries!"

你能否直接使用数据进行操作,并在出现问题时决定它是字典还是列表?


-1

不要使用 types 模块:

import types

d = {}
print type(d) is types.DictType

l = [{},{}]
print type(l) is types.ListType and len(l) and type(l[0]) is types.DictType

首先,比较类型的相等性是一个不好的想法。如果你得到了一个OrderedDictisinstance(dict)isinstance(Mapping)将会是真的,但是type is dict则不会。PEP 8正是因为这个原因推荐使用isinstance - abarnert
其次,除非你正在编写Python 1.x代码或其他类似的代码,否则为什么要使用types.DictTypetypes.ListType?在3.x中,这些冗余类型甚至不存在;在2.x中,它们存在,但文档明确表示内置类型是“访问类型的首选方式,而不是使用types模块”。 - abarnert
最后,引用PEP 8的话:“对于序列(字符串、列表、元组),使用空序列为假的事实。”而不应该做的例子正是你所做的:使用len(l)来检查一个空序列。 - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接