Python中的字典是一个对象吗?

19

我有一个像这样的 dict

>>> my_dict = {u'2008': 6.57, u'2009': 4.89, u'2011': 7.74,
...            u'2010': 7.44, u'2012': 7.44}

使用has_key输出:

>>> my_dict.has_key(unicode(2012))
True

使用hasattr的输出:

>>> hasattr(my_dict, unicode(2012))
False

我不理解为什么这样会有所不同。我搜索了一下发现是因为dict和对象不同。

但是,我还是无法完全理解它们的区别。

(顺便说一句:我正在使用 Python 2.7)


3
顺便提一下:has_key已经被Python3废弃并移除了。现在可以使用the_key in the_dictionary代替。请注意不要改变原意,使句子更易懂。 - Bakuriu
5个回答

31

dict 实例也是对象。但是它们的键只是不作为属性公开。

将键作为属性(或者说代替项目访问)公开会导致命名空间污染;例如,您将永远无法使用 has_key 键。 has_key 已经是字典上的属性:

>>> hasattr({}, 'has_key')
True
>>> {}.has_key
<built-in method has_key of dict object at 0x7fa2a8461940>

对象属性和字典内容是两个不同的东西,这种分离是有意的。

您可以始终子类化dict,并使用__getattr__() 钩子方法添加属性访问:

class AttributeDict(dict):
    def __getattr__(self, name):
        if name in self:
            return self[name]
        raise AttributeError(name)

示例:

>>> demo = AttributeDict({'foo': 'bar'})
>>> demo.keys()
['foo']
>>> demo.foo
'bar'

现有的dict类属性具有优先级:

>>> demo['has_key'] = 'monty'
>>> demo.has_key
<built-in method has_key of AttributeDict object at 0x7fa2a8464130>

你太快了,让我有点跟不上。我原本想写一个类似的答案,但是突然看到了“3个新回答”... - Martin Thoma
5
在周末,提问者与回答者之间的平衡似乎稍微向回答者倾斜了一点.. :-) - Martijn Pieters
谢谢@martijn-pieters,非常好的解释,我不明白名称空间污染是什么意思,你可以向我解释一下吗? - John Prawyn
1
@JohnPrawyn:对象上的属性是一个命名空间;dct.keys()dct.items()等。如果字典内容也是属性,它们会污染该命名空间;你开始将额外的名称混合到其中。 - Martijn Pieters
@MartijnPieters 真的很酷,但是当我们子类化词典并使用 getattr() 钩子方法添加属性访问时,我们难道不是在污染命名空间吗? - John Prawyn
显示剩余2条评论

3

has_key用于检查字典中是否存在某个键(在创建字典时定义的键)。hasattr用于检查对象是否具有某个属性。

字典是对象,它们具有特定的属性。 hasattr用于检查这些属性是否存在。

>>> hasattr(dict, 'has_key')
True
>>> hasattr(dict, 'items')
True
>>> newDict = {'a': 1, 'b':2}
>>> newDict.has_key('a')
True

你可以使用 dir() 函数列出对象的有效属性。
>>> dir(dict)
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values', 'viewitems', 'viewkeys', 'viewvalues']

2
my_dict.has_key(unicode(2012)): has_key函数用于在字典中查找键。字典中的键不是属性,而是独立的元素。
引用块中提到的hasattr(object, name)函数接受一个对象和一个字符串作为参数。如果该字符串是对象的属性之一,则返回True;否则返回False。这个函数实际上是通过调用getattr(object, name)来判断是否会出现异常来实现的。
因此,可以看出,尽管字典是对象,但字典的键并不是字典的属性。

1

字典是一个对象,就像Python中的任何东西都是一个对象一样。但是,对象的属性和字典的键之间存在区别。

字典不将其键存储为属性!

访问字典键的唯一方法是通过__getitem__方法或[]操作符。

如果您想以这种方式访问项,则可以重写__getattr__方法,并使其返回__getitem__结果。

您还可以创建类似于此的内容:

   class ObjectDict(dict):
       def __init__(self, *args, **kws):
           super(ObjectDict, self).__init__(*args, **kws)
           self.__dict__ = self

这将导致以下行为:
>>> d = ObjectDict()
>>> d['a'] = 3
>>> d.a
3
>>> hasattr(d, 'a')
True
但是这已知会在Python中导致内存泄漏。

0

attribute 返回实例的某些属性,以便您可以操作它。 现在理解 attribute 总是会返回一些东西,但如果一个 key 不在 d 中,d[key] 将引发 KeyError

hasattr 检查一个 instantce 是否具有该 attribute。由于 dictionarykeys 不是 dictionaryattribute,因此它返回 False

示例:

# python3
>>> d = {1:True}
>>> dir(d) # this shows all the attributes of the instance
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__'
, '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__',
 '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '_
_new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__'
, '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get
', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
>>> hasattr(d,'keys')
True
>>> 1 in d.keys()
True

属性指的是类方法或常量吗?不,属性就是属性。函数是属性,再加上描述符协议,使它们返回为方法。属性和描述符都是属性。您可以将函数存储在实例上作为属性,它们也可以被调用,但不是方法。等等。Python中没有常量。 - Martijn Pieters
不好意思,我写错了。我是指类常量。我的意思是属性指的是在对象上执行某种操作并返回另一个对象的东西。而字典键则完全不同。我可能是错误的,请告诉我更多信息或给我一些链接。 - rnbguy
1
规范参考请查看Python 数据模型文档 - Martijn Pieters

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