0

I have a list of objects in Python 3.5.1 with several named attributes. The list represents a group of C variables and as such each object has three named attributes that correspond to three common base types: uint8, uint16, and uint32.

class variableList(object):
    def __init__(self, eight, sixteen, thirtytwo):
        self.size_8 = eight
        self.size_16 = sixteen
        self.size_32 = thirtytwo

The value of these named attributes is either (Nonetype) None or (str)'E'. Per object, only one of the named attributes will have some non-None value.

I want to sort this list of objects such that all objects with non-None value for size_32 are on top, followed by size_16, followed by size_8.

In python 2.7.1 I successfully used this, but it felt like black magic:

size_filtered_list = sorted(filtered_list, key=lambda y: (y.size_32, y.size_16, y.size_8))

but in 3.5.1 this no longer works. Suggestions?

3
  • If you define __lt__ and __eq__ methods you could sort your variables without a key argument. Because I am unfarmiliar with C could you explain basically what (str)'E' would mean in python? Commented Jun 24, 2016 at 20:22
  • Is there a reason you're using effectively boolean flags as None and 'E' rather than just using False and True? If you weren't using incompatible types, your sort could simplify to an import at the top of the file, from operator import attrgetter, with the sort: sorted(filtered_list, key=attrgetter('size_32', 'size_16', 'size_8')), which works fine in all versions of Python because you're sorting comparable types. Commented Jun 24, 2016 at 20:50
  • Or if you add another variable as in self.size_8_exist, you can still use the same technique. Commented Jun 24, 2016 at 20:52

2 Answers 2

1

You should move to using compatible types; sorting None and 'E' is nonsensical, and while Python 2's "arbitrary but consistent" sort rules for incompatible types allows it, it's terrible practice. If the only values are None and 'E', then they're really just terrible versions of booleans; you could just as easily use False and True and your key function would work fine in both Py2 and Py3; you could even "optimize" a little with operator.attrgetter:

from operator import attrgetter

size_filtered_list = sorted(filtered_list, key=attrgetter('size_32', 'size_16', 'size_8'))

Alternatively, using the original lambda based code, you could make it work by replacing None with the empty string in the key, so you compare compatible types (while still treating the None as less than every other string):

size_filtered_list = sorted(filtered_list, key=lambda y: (y.size_32 or '', y.size_16 or '', y.size_8 or ''))

It's kind of silly, but so is using None and 'E' for what amounts to boolean data.

Sign up to request clarification or add additional context in comments.

Comments

0

I would just implement the comparison operators for the class. Assuming you do not want to sort based on the values stored in each of these attributes, this implementation should work.

class variableList(object):
    def __init__(self, eight, sixteen, thirtytwo):
        self.size_8 = eight
        self.size_16 = sixteen
        self.size_32 = thirtytwo

    def __lt__(self, other):
        if self.size_8 is not None:
            return self != other
        elif self.size_16 is not None:
            return True if other.size_32 is not None else False
        else:
            return False

    def __eq__(self, other):
        if self.size_8 is not None:
            return other.size_8 is not None
        elif self.size_16 is not None:
            return other.size_16 is not None
        else:
            return other.size_32 is not None

And for completeness, you can also implement the greater than operator, but it is not necessary for sorting:

    def __gt__(self, other):
        if self.size_32 is not None:
            return self != other
        elif self.size_16 is not None:
            return False if other.size_32 is not None else True
        else:
            return False

If you want the values of the same types to be sorted, you will have to add another level of nesting to compare these values.

4 Comments

never rely on the implementation detail that python sorting only uses __lt__ you can use functools.total_ordering to automate creating the other comparison methods once __eq__ and one of them is defined.
Thanks, I did not know about that. I am always paranoid that I made a silly mistake with the comparison operators, so automating the others would be much preferable than writing them all myself.
You are correct, in that case this seems much less flawed, but still y = variableList(1, None, None) ; print(y<y) prints True which is not correct.
Thanks for catching that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.