{article Dive into Python}{title} {text} {/article}

Getting and Setting Items

The __getitem__ Special Method

{source}
<!-- You can place html anywhere within the source tags -->
<link type="text/css" rel="Stylesheet" href="/syntaxhighlighter_3.0.83/styles/shThemeDefault.css"/>
<pre class="brush:py;">

Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 10:45:13) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import fileinfo
>>> def __getitem__(self, key): return self.data[key]

>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'name': '/music/_singles/kairo.mp3'}
>>> f.__getitem__("name")
'/music/_singles/kairo.mp3'
>>> f["name"]
'/music/_singles/kairo.mp3'
>>>




</pre>


<script language="javascript" type="text/javascript">
    // You can place JavaScript like this

</script>
<?php
    // You can place PHP like this

?>
{/source}

>>> import fileinfo
>>> def __getitem__(self, key): return self.data[key]

>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'name': '/music/_singles/kairo.mp3'}
>>> f.__getitem__("name")
'/music/_singles/kairo.mp3'
>>> f["name"]
'/music/_singles/kairo.mp3'
>>>

The __getitem__ special method looks simple enough. Like the normal methods clear, keys, and
values, it just redirects to the dictionary to return its value. But how does it get called? Well, you can call
__getitem__ directly, but in practice you wouldn't actually do that; I'm just doing it here to show you how it
works. The right way to use __getitem__ is to get Python to call it for you.

This looks just like the syntax you would use to get a dictionary value, and in fact it returns the value you
would expect. But here's the missing link: under the covers, Python has converted this syntax to the method call
f.__getitem__("name"). That's why __getitem__ is a special class method; not only can you call it
yourself, you can get Python to call it for you by using the right syntax.

The __setitem__ Special Method

{source}
<!-- You can place html anywhere within the source tags -->
<link type="text/css" rel="Stylesheet" href="/syntaxhighlighter_3.0.83/styles/shThemeDefault.css"/>
<pre class="brush:py;">

Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 10:45:13) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import fileinfo
>>> def __setitem__(self, key, item): self.data[key] = item

>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'name': '/music/_singles/kairo.mp3'}
>>>
>>> f.__setitem__("genre", 31)
>>> f
{'name': '/music/_singles/kairo.mp3', 'genre': 31}
>>> f["genre"] = 32
>>> f
{'name': '/music/_singles/kairo.mp3', 'genre': 32}
>>>


</pre>

<script language="javascript" type="text/javascript">
    // You can place JavaScript like this

</script>
<?php
    // You can place PHP like this

?>
{/source}

>>> import fileinfo
>>> def __setitem__(self, key, item): self.data[key] = item

>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'name': '/music/_singles/kairo.mp3'}
>>>
>>> f.__setitem__("genre", 31)
>>> f
{'name': '/music/_singles/kairo.mp3', 'genre': 31}
>>> f["genre"] = 32
>>> f
{'name': '/music/_singles/kairo.mp3', 'genre': 32}
>>>

Like the __getitem__ method, __setitem__ simply redirects to the real dictionary self.data to do its work. And like __getitem__, you wouldn't ordinarily call it directly like this; Python calls __setitem__ for you when you use the right syntax.

This looks like regular dictionary syntax, except of course that f is really a class that's trying very hard to masquerade as a dictionary, and __setitem__ is an essential part of that masquerade. This line of code actually calls f.__setitem__("genre", 32) under the covers.

Overriding __setitem__ in MP3FileInfo

{source}
<!-- You can place html anywhere within the source tags -->

<link type="text/css" rel="Stylesheet" href="/syntaxhighlighter_3.0.83/styles/shThemeDefault.css"/>
<pre class="brush:py;">

Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 10:45:13) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> def __setitem__(self, key, item):
        if key == "name" and item:
            self.__parse(item)
        FileInfo.__setitem__(self, key, item)


>>>


</pre>

<script language="javascript" type="text/javascript">
    // You can place JavaScript like this

</script>
<?php
    // You can place PHP like this

?>
{/source}

>>> def __setitem__(self, key, item):
if key == "name" and item:
self.__parse(item)
FileInfo.__setitem__(self, key, item)

Notice that this __setitem__ method is defined exactly the same way as the ancestor method. This is important, since Python will be calling the method for you, and it expects it to be defined with a certain number of arguments. (Technically speaking, the names of the arguments don't matter; only the number of arguments is important.)

Here's the crux of the entire MP3FileInfo class: if you're assigning a value to the name key, you want to do something extra

The extra processing you do for names is encapsulated in the __parse method. This is another class method defined in MP3FileInfo, and when you call it, you qualify it with self. Just calling __parse would look for a normal function defined outside the class, which is not what you want. Calling self.__parse will look for a class method defined within the class. This isn't anything new; you reference data attributes the same way.

After doing this extra processing, you want to call the ancestor method. Remember that this is never done for you in Python; you must do it manually. Note that you're calling the immediate ancestor, FileInfo, even though it doesn't have a __setitem__ method. That's okay, because Python will walk up the ancestor tree until it finds a class with the method you're calling, so this line of code will eventually find and call the __setitem__ defined in UserDict.

Setting an MP3FileInfo's name

{source}
<!-- You can place html anywhere within the source tags -->
<link type="text/css" rel="Stylesheet" href="/syntaxhighlighter_3.0.83/styles/shThemeDefault.css"/>
<pre class="brush:py;">

Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 10:45:13) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import fileinfo
>>> mp3file = fileinfo.MP3FileInfo()
>>> mp3file
{'name': None}
>>> mp3file["name"] = "/music/_singles/kairo.mp3"
>>> mp3file
{'name': '/music/_singles/kairo.mp3'}
>>> mp3file["name"] = "/music/_singles/sidewinder.mp3"
>>> mp3file
{'name': '/music/_singles/sidewinder.mp3'}
>>>


</pre>

<script language="javascript" type="text/javascript">
    // You can place JavaScript like this

</script>
<?php
    // You can place PHP like this

?>
{/source}

Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 10:45:13) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import fileinfo
>>> mp3file = fileinfo.MP3FileInfo()
>>> mp3file
{'name': None}
>>> mp3file["name"] = "/music/_singles/kairo.mp3"
>>> mp3file
{'name': '/music/_singles/kairo.mp3'}
>>> mp3file["name"] = "/music/_singles/sidewinder.mp3"
>>> mp3file
{'name': '/music/_singles/sidewinder.mp3'}
>>>

First, you create an instance of MP3FileInfo, without passing it a filename. (You can get away with this because the filename argument of the __init__ method is optional.) Since MP3FileInfo has no __init__ method of its own, Python walks up the ancestor tree and finds the __init__ method of FileInfo. This __init__ method manually calls the __init__ method of UserDict and then sets the name key to filename, which is None, since you didn't pass a filename. Thus, mp3file initially looks like a dictionary with one key, name, whose value is None.

Now the real fun begins. Setting the name key of mp3file triggers the __setitem__ method on MP3FileInfo (not UserDict), which notices that you're setting the name key with a real value and calls self.__parse. Although you haven't traced through the __parse method yet, you can see from the output that it sets several other keys: album, artist, genre, title, year, and comment.

Modifying the name key will go through the same process again: Python calls __setitem__, which calls self.__parse, which sets all the other keys