Hi again,

I'm trying to use a python wrapper for the last.fm api. It's pretty old but seems to be working ok (for now). See http://code.google.com/p/pyscrobble/

Anyway, I'm having trouble extracting some information from the output.

import scrobble

user = scrobble.User('mattfeliks') # invoke user class
recent = user.recent_tracks # assign recent_tracks attribute

Now, if I do "print recent", I get this:

matt@ub:~$ python testinglast.py 
[(<scrobble.Track(<scrobble.Artist(u'Groove Armada') - u'Hands of Time'), datetime.datetime(2009, 4, 8, 16, 28, 46)), (<scrobble.Track(<scrobble.Artist(u'The Kills') - u'The Good Ones'), datetime.datetime(2009, 4, 8, 16, 25, 14)), (<scrobble.Track(<scrobble.Artist(u'Hoobastank') - u'Ready for You'), datetime.datetime(2009, 4, 8, 16, 22)), (<scrobble.Track(<scrobble.Artist(u'Fairport Convention') - u'Crazy Man Michael'), datetime.datetime(2009, 4, 8, 16, 15, 51)), (<scrobble.Track(<scrobble.Artist(u'Jos\xe9 Gonz\xe1lez') - u'The Nest'), datetime.datetime(2009, 4, 8, 16, 13, 25)), (<scrobble.Track(<scrobble.Artist(u'Son Volt') - u'Drown'), datetime.datetime(2009, 4, 8, 16, 9, 59)))]

and so on...

Now, I would like to just get the "Artist" attribute from this mess. But when I try something like:

for tracks in recent:
	print tracks.Track

I keep getting errors along the lines of:

AttributeError: 'tuple' object has no attribute 'Track'

If there is no attribute 'Track', why does it have 'scrobble.Track' in the output?

Am I missing something obvious? Perhaps I need to unpack the tuple?

Recommended Answers

All 9 Replies

Each tuple in that list has an instance of scrobble.Track, which contains an instance of scrobble.Artist; however the tuple itself does not have those attributes.

I'm not entirely sure on the specifics of the scrobble module, but a way to unpack the tuple would be like this:

for tracks in recent:
  track_info, track_date = tracks
  print track_info

Perhaps the scrobble.Track class has a built-in __repr__ overload so that printing the object itself will result in 'Groove Armada' - 'Hands of Time' instead of <scrobble.Track(<scrobble.Artist(u'Groove Armada') - u'Hands of Time') Although, perhaps once you have that tuple unpacked you can use something like track_info.Artist .. I guess it will take a little experimenting

Hi jlm699, thanks for your help.

However I'm not too clear on what this code does. Specifically, what does the line:

track_info, track_date = tracks

do? Does this unpack the tuple?

I'm also unclear on the relation between the classes and attributes. In the documentation, there are 3 classes: User, Artist and Track. How can one attribute (scrobble.Artist) be inside an instance of a different class (Track)?

If I use the above code, I get this output now:

<scrobble.Artist(u'Groove Armada') - u'Hands of Time'

How could I unpack this to just get 'Groove Armada'? I tried another 'for' loop to select the scrobble.Artist but it just isn't happening.

Sorry to bombard with questions, but I'd like to get to the bottom of this. Also I think my understanding of objects is distinctly lacking.

Anyway, thanks for your time.

However I'm not too clear on what this code does. Specifically, what does the line track_info, track_date = tracks do? Does this unpack the tuple?

Precisely. Here's an illustrative example:

>>> my_tuple = ('Item 1', 2)
>>> elem_1, elem_2 = my_tuple
>>> elem_1
'Item 1'
>>> elem_2
2
>>>

I'm also unclear on the relation between the classes and attributes. In the documentation, there are 3 classes: User, Artist and Track. How can one attribute (scrobble.Artist) be inside an instance of a different class (Track)?

Basically this seems to be something that is done in the instantiation of the parent class. It would make sense to have an over-arching Track class that contains an instance of the Artist class; however we are getting ahead of ourselves, since Artist might not be a class at all and really just an element of the Track class.

Have you tried doing something like this:

track_info, track_date = tracks
dir(track_info)

This should print out all the methods and attributes of that instance of the Track class. Hopefully this will help to highlight what functions and elements you're able to make use of to get the info you're looking for.

If I use the above code, I get this output now:

<scrobble.Artist(u'Groove Armada') - u'Hands of Time'

How could I unpack this to just get 'Groove Armada'?

See my last comment...

EDIT: If at all possible I suggest loading up your Python interpreter and step through your code so that it's easier to seek out the info you're looking for. I use PyCrust for my debugging and research because it has auto-complete and automatic look-ups for elements of each and every object. Something like that would most certainly help you identify how to handle these scrobble items.

HTH!

Aha, I've done it :)


Python 2.5.2 (r252:60911, Oct 5 2008, 19:24:49) [GCC 4.3.2] on linux2 Type "help", "copyright", "credits" or "license" for more information.

>>> import scrobble >>> user = scrobble.User('mattfeliks') >>> user.recent_tracks

[(<scrobble.Track(<scrobble.Artist(u'Groove Armada') - u'Hands of Time'), datetime.datetime(2009, 4, 8, 16, 28, 46)), (<scrobble.Track(<scrobble.Artist(u'The Kills') - u'The Good Ones'), datetime.datetime(2009, 4, 8, 16, 25, 14)), (<scrobble.Track(<scrobble.Artist(u'Hoobastank') - u'Ready for You'), datetime.datetime(2009, 4, 8, 16, 22)), (<scrobble.Track(<scrobble.Artist(u'Fairport Convention') - u'Crazy Man Michael'), datetime.datetime(2009, 4, 8, 16, 15, 51)), (<scrobble.Track(<scrobble.Artist(u'Jos\xe9 Gonz\xe1lez') - u'The Nest'), datetime.datetime(2009, 4, 8, 16, 13, 25)))

>>> for tracks in recent: ... track_info, track_date = tracks ... print track_info ... <scrobble.Artist(u'Groove Armada') - u'Hands of Time' <scrobble.Artist(u'The Kills') - u'The Good Ones' <scrobble.Artist(u'Hoobastank') - u'Ready for You' <scrobble.Artist(u'Jos\xe9 Gonz\xe1lez') - u'The Nest'

>>> dir(track_info) ['_Track__albumname', '_Track__artist', '_Track__getAlbumName', '_Track__getArtist', '_Track__getGlobalTags', '_Track__getLength', '_Track__getMbId', '_Track__getName', '_Track__length', '_Track__mbid', '_Track__name', '_Track__setAlbumName', '_Track__setLength', '__class__', '__cmp__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'albumname', 'artist', 'getUserTags', 'global_tags', 'length', 'mbid', 'name']

>>> for tracks in recent: ... track_info, track_date = tracks ... print track_info.artist ...

Groove Armada The Kills Hoobastank

Traceback (most recent call last): File "<stdin>", line 3, in <module> UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 3: ordinal not in range(128)

[/code] Now I just need to sort out the character encoding (encode in UTF-8 presumably, since it's ripped from a website), and I'm done.

Thanks for your help, and I will check out Pycrust.[code]
Python 2.5.2 (r252:60911, Oct 5 2008, 19:24:49)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> import scrobble
>>> user = scrobble.User('mattfeliks')
>>> user.recent_tracks

[(<scrobble.Track(<scrobble.Artist(u'Groove Armada') - u'Hands of Time'), datetime.datetime(2009, 4, 8, 16, 28, 46)), (<scrobble.Track(<scrobble.Artist(u'The Kills') - u'The Good Ones'), datetime.datetime(2009, 4, 8, 16, 25, 14)), (<scrobble.Track(<scrobble.Artist(u'Hoobastank') - u'Ready for You'), datetime.datetime(2009, 4, 8, 16, 22)), (<scrobble.Track(<scrobble.Artist(u'Fairport Convention') - u'Crazy Man Michael'), datetime.datetime(2009, 4, 8, 16, 15, 51)), (<scrobble.Track(<scrobble.Artist(u'Jos\xe9 Gonz\xe1lez') - u'The Nest'), datetime.datetime(2009, 4, 8, 16, 13, 25)))

>>> for tracks in recent: ... track_info, track_date = tracks ... print track_info ... <scrobble.Artist(u'Groove Armada') - u'Hands of Time' <scrobble.Artist(u'The Kills') - u'The Good Ones' <scrobble.Artist(u'Hoobastank') - u'Ready for You' <scrobble.Artist(u'Jos\xe9 Gonz\xe1lez') - u'The Nest'

>>> dir(track_info) ['_Track__albumname', '_Track__artist', '_Track__getAlbumName', '_Track__getArtist', '_Track__getGlobalTags', '_Track__getLength', '_Track__getMbId', '_Track__getName', '_Track__length', '_Track__mbid', '_Track__name', '_Track__setAlbumName', '_Track__setLength', '__class__', '__cmp__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'albumname', 'artist', 'getUserTags', 'global_tags', 'length', 'mbid', 'name']

>>> for tracks in recent: ... track_info, track_date = tracks ... print track_info.artist ...

Groove Armada The Kills Hoobastank

Traceback (most recent call last): File "<stdin>", line 3, in <module> UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 3: ordinal not in range(128)

[/code] Now I just need to sort out the character encoding (encode in UTF-8 presumably, since it's ripped from a website), and I'm done.

Thanks for your help, and I will check out Pycrust.

Ok, I'm having trouble encoding characters from this output.

Essentially, python tries to use the ascii codec to output the Artist class.

Here's what happens each time:

Groove Armada
The Kills
Hoobastank
Fairport Convention
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 3: ordinal not in range(128)

(in case it isn't clear, it's throwing an error for trying to encode an accented 'e')

I have looked up character encodings and the quick solution should be to just encode all the output as UTF-8. I have done this successfully in the past with other things. However, when I try this, I get this error:

mystring = unicode(track_info.artist,'utf-8')
TypeError: coercing to Unicode: need string or buffer, Artist found

The same thing happens with encode().

So it's not a string, which is causing problems:

>>> type(track_info.artist)
<class 'scrobble.artist.Artist'>

And I can't convert it to a string because it throws the ascii error again.

What can I do?

You can try declaring the file as a different enoding all together with something like this: # -*- coding: <encoding-name> -*- .

Refer to this documentation on declaring new encodings.

Ok, I am really confused.

for tracks in recent:
        track_info, track_date = tracks
    print track_info.artist

Output:

Groove Armada
The Kills
Hoobastank
Fairport Convention
Son Volt
Johnny Cash
Bright Eyes
The Surfaris
Richard & Linda Thompson
David Bowie

But when I do this instead:

for tracks in recent:
        track_info, track_date = tracks
        artist_list.append(track_info.artist)
    print artist_list

Output:

[<scrobble.Artist(u'Groove Armada'), <scrobble.Artist(u'The Kills'), <scrobble.Artist(u'Hoobastank'), <scrobble.Artist(u'Fairport Convention'), <scrobble.Artist(u'Son Volt'), <scrobble.Artist(u'Johnny Cash'), <scrobble.Artist(u'Bright Eyes'), <scrobble.Artist(u'The Surfaris'), <scrobble.Artist(u'Richard & Linda Thompson'), <scrobble.Artist(u'David Bowie')]

Why does it get messed up?

I may just sort out a regex to tidy this up. But that seems like a stupid workaround.

Edit: I tried the encoding thing, it didn't work but I'll do a bit more reading and I should get it ironed out.

Why does it get messed up?

Well apparently the Artist class has overloaded the __repr__ method so that when you print an instance of the class, it gives you a pretty output in string form instead of the reference to the class itself.

When you print the list containing these instances, the __repr__ function is never called since you're not printing the instance but rather the container holding those instances...

Does that explanation make sense? Maybe some code will help to explain what I mean:

>>> class ugly_class(object):
...     def __init__( self ):
...         self.test_var = 'Blank'
...     
>>> class pretty_class(object):
...     def __init__( self ):
...         self.test_var = 'Blank'
...     def __repr__( self ):
...         return 'The value of my test_var is %s' % self.test_var
...     
>>> my_uc = ugly_class()
>>> my_pc = pretty_class()
>>> my_uc
<__main__.ugly_class object at 0x01E85EB0>
>>> my_pc
The value of my test_var is Blank
>>>

As you can see, since I overloaded the __repr__ method in the pretty_class it gave me a nice pretty looking string output instead of the reference to the class instance itself.

HTH

Thanks, that is a very clear example.

I am going to rewrite this module at some point because it's really broken in places.

Anyway, I got the list working, by forcing it to output a string:

import scrobble

def get_recent_artists():
    user = scrobble.User('mattfeliks')    #invoke User object
    recent = user.recent_tracks
    artist_list = []

    for tracks in recent:
        track_info, track_date = tracks   #unpack recent tracks tuple
        artist_list.append(str(track_info.artist))
    return artist_list

print get_recent_artists()

Gives:

['Groove Armada', 'The Kills', 'Hoobastank', 'Fairport Convention', 'Son Volt', 'Johnny Cash', 'Bright Eyes', 'The Surfaris', 'Richard & Linda Thompson', 'David Bowie']

Phew.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.