19

I have a dict like this:

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

Output with has_key:

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

Output with hasattr:

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

I couldn't understand why this behaves differently. I googled and found out that it is because dict and objects are different.

But, still I couldn't understand the difference properly.

(BTW : I am using python 2.7)

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
John Prawyn
  • 1,423
  • 3
  • 19
  • 28
  • 3
    By the way: `has_key` is *deprecated* and was removed in python3. Use `the_key in the_dictionary` instead. – Bakuriu Jul 20 '13 at 13:39

5 Answers5

31

dict instances are objects too. But their keys are just not exposed as as attributes.

Exposing the keys as attributes (too or instead of item access) would lead to namespace pollution; you'd never be able to use a has_key key, for example. has_key is already an attribute on dictionaries:

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

Attributes of objects and the contents of dictionaries are two separate things, and the separation is deliberate.

You can always subclass dict to add attribute access using the __getattr__() hook method:

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

Demo:

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

Existing attributes on the dict class take priority:

>>> demo['has_key'] = 'monty'
>>> demo.has_key
<built-in method has_key of AttributeDict object at 0x7fa2a8464130>
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • You're too fast for me :-/ I wanted to write a similar answer, but then I saw "3 new answers"... – Martin Thoma Jul 20 '13 at 10:27
  • 5
    @moose: On weekends the balance between question askers and answerers appears to be skewed a little towards answerers.. :-) – Martijn Pieters Jul 20 '13 at 10:31
  • Thank you @martijn-pieters, really nice explanation, I couldn't understand about the namespace pollution, can you please explain that to me? – John Prawyn Jul 22 '13 at 08:22
  • 1
    @JohnPrawyn: the attributes on an object are a namespace; `dct.keys()`, `dct.items()`, etc. If dictionary contents were also attributes, they pollute that namespace; you start mixing extra names into it. – Martijn Pieters Jul 22 '13 at 08:42
  • @MartijnPieters really cool, but when we subclass a dict to add attribute access using the __getattr__() hook method, are we not polluting the namespace? – John Prawyn Jul 22 '13 at 08:50
  • @JohnPrawyn: You are :-) I was offering it as a work-around, in case you *really* wanted to have attribute access to the contents *anyway*. Using the `__getattr__` hook does mean that existing attributes on the dictionary come first. Any keys that match existing attributes are thus masked and not accessible as attributes. – Martijn Pieters Jul 22 '13 at 09:22
  • @MartijnPieters, Thanks a lot, for the answer and your patience and towards me. – John Prawyn Jul 22 '13 at 09:42
3

has_key checks for the existence of a key in the dictionary. (One your code defines while creating a dictionary) hasattr checks if the object has an attribute.

Dictionaries are objects, and they have certain attributes. hasattr checks for those.

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

You can use dir() which lists out the valid attributes for an object.

>>> 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']
Sukrit Kalra
  • 33,167
  • 7
  • 69
  • 71
2

my_dict.has_key(unicode(2012)): has_key looks for a key in a dictionary. Keys in a dictionary are not attributes and

hasattr(object, name)

The arguments are an object and a string. The result is True if the string is the name of one of the object’s attributes, False if not. (This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.)

from which you can see that, although dict are objects, dict's keys are not the dict's attributes;

kiriloff
  • 25,609
  • 37
  • 148
  • 229
1

Dict is an object, as anything in Python is an object. There's, however, a difference between an objects attribute and a dicts key. The dict doesn't store its keys as attributes! The only way to access a dicts keys is through the __getitem__ method or the [] operator.

If you want to access the items this way, you can override the __getattr__ method and make it return the __getitem__ result in stead.

You can also create something like this:

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

Which will result in this behavior:

>>> d = ObjectDict()
>>> d['a'] = 3
>>> d.a
3
>>> hasattr(d, 'a')
True

But this is known to cause memory leaks in Python

immortal
  • 3,118
  • 20
  • 38
0

attribute returns some property of an instance so that you can manipulate it. Now understand attribute will always return something, but if a key is not in d, d[key] will raise KeyError.

hasattr checks if an instantce has that attribute or not. Since dictionary keys are no attribute to the dictionary it returns False.

example :

# 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
rnbguy
  • 1,369
  • 1
  • 10
  • 28
  • *attribute means class method or constant*. No, attribute means attribute. Functions are attributes, coupled with the [descriptor protocol](http://docs.python.org/2/howto/descriptor.html) makes them return as methods. Properties are attributes and descriptors. You can store functions on instances as attributes, they are then callable too but not methods. Etc. There are no constants in Python. – Martijn Pieters Jul 20 '13 at 10:37
  • no i meant class constant. sorry I wrote it wrong. What I meant, attribute means, something which does some kind of operation on an object and return some another object. whereas, dict keys are totally different. I might be wrong, tell me more or give me some link. – rnbguy Jul 20 '13 at 10:41
  • 1
    The canonical reference is the [Python datamodel documentation](http://docs.python.org/2/reference/datamodel.html). – Martijn Pieters Jul 20 '13 at 10:46