Thomas Matthew
Thomas Matthew

Reputation: 2886

typecast classes in python: how?

Here, I am attempting to mock up a social media profile as a class "Profile", in which you have name, a group of friends, and the ability to add and remove friends. There is a method that I would like to make, that when invoked, will print the list of friends in alphabetical order.

The issue: I get a warning that I cannot sort an unsortable type. Python is seeing my instance variable as a "Profile object", rather than a list that I can sort and print.

Here is my code:

class Profile(object):
    """
    Represent a person's social profile

    Argument:
    name (string): a person's name - assumed to uniquely identify a person

    Attributes:
    name (string): a person's name - assumed to uniquely identify a person
    statuses (list): a list containing a person's statuses - initialized to []
    friends (set): set of friends for the given person.
                   it is the set of profile objects representing these friends.
    """

    def __init__(self, name):
        self.name = name
        self.friends = set()
        self.statuses = []

    def __str__(self):
        return self.name + " is " + self.get_last_status()

    def update_status(self, status):
        self.statuses.append(status)
        return self

    def get_last_status(self):
        if len(self.statuses) == 0:
            return "None"
        else:
            return self.statuses[-1]

    def add_friend(self, friend_profile):
        self.friends.add(friend_profile)
        friend_profile.friends.add(self)
        return self

    def get_friends(self):
        if len(self.friends) == 0:
            return "None"
        else:
            friends_lst = list(self.friends)
            return sorted(friends_lst)

After I fill out a list of friends (from a test module) and invoke the get_friends method, python tells me:

 File "/home/tjm/Documents/CS021/social.py", line 84, in get_friends
    return sorted(friends_lst)
TypeError: unorderable types: Profile() < Profile()

Why can't I simply typecast the object to get it in list form? What should I be doing instead so that get_friends will return an alphabetically sorted list of friends?

Upvotes: 2

Views: 1775

Answers (2)

Ozgur Vatansever
Ozgur Vatansever

Reputation: 52163

Sorting algorithms look for the existence of __eq__, __ne__, __lt__, __le__, __gt__,__ge__ methods in the class definition to compare instances created from them. You need to override those methods in order to tweak their behaviors.

For performance reasons, I'd recommend you to define some integer property for your class like id and use it for comparing instead of name which has string comparison overhead.

class Profile(object):
    def __eq__(self, profile):
        return self.id == profile.id # I made it up the id property.

    def __lt__(self, profile):
        return self.id < profile.id

    def __hash__(self):
        return hash(self.id)

    ...

Alternatively, you can pass a key function to sort algorithm if you don't want to bother yourself overriding those methods:

>>> friend_list = [<Profile: id=120>, <Profile: id=121>, <Profile: id=115>]
>>> friend_list.sort(key=lambda p: p.id, reverse=True)

Using operator.attrgetter;

>>> import operator
>>> new_friend_list = sorted(friend_list, key=operator.attrgetter('id')) 

Upvotes: 6

SingleNegationElimination
SingleNegationElimination

Reputation: 156178

I think i'll take a crack at this. first, here's teh codes:

from collections import namedtuple

class Profile(namedtuple("Profile", "name")):
    def __init__(self, name):
        # don't set self.name, it's already set!
        self.friends = set({})
        self.statuses = list([])

    # ... and all the rest the same.  Only the base class changes.

what we've done here is to create a class with the shape of a tuple. As such, it's orderable, hashable, and all of the things. You could even drop your __str__() method, namedtuple provides a nice one.

Upvotes: 1

Related Questions