views:

192

answers:

4

Is there a more Pythonic way of doing this?:

            if self.name2info[name]['prereqs'] is None:
                self.name2info[name]['prereqs'] = []
            if self.name2info[name]['optionals'] is None:
                self.name2info[name]['optionals'] = []

The reason I do this is because I need to iterate over those later. They're None to begin with sometimes because that's the default value. It's my workaround to not making [] a default value.

Thanks.

+3  A: 

If you prefer this:

self.name2info[name]['prereqs'] = self.name2info[name]['prereqs'] or []

Zach
Both `None` and `[]` evaluate to `False`, don't they? Why does that work?
Alex Bliskovsky
@Alex: yes, [] will evaluate to False, so if the entry is already an empty list then it will be replaced by another empty list. The only time this will be an issue is if you want multiple entries to refer to the same list - most of the time you do NOT want this, and having multiple references to the same list can cause subtle bugs (e.g. when using an empty list as a default parameter to a function, which is a common beginner's mistake).
Dave Kirby
If you drop the explicit test `is None` you basically replace everything that **evaluates** to `False` in a boolean context with an empty list: `None`, but also `''` (empty string), `[]` (empty list), `{}` (empty dictiontary), `0` (zero), `False` (boolean). This might at one point be a factor for introducing harder to catch errors.
ChristopheD
A: 

You could do it this way:

if not self.name2info[name]['prereqs']: self.name2info[name]['prereqs'] = []

or this way

self.name2info[name]['prereqs'] = [] if not self.name2info[name]['prereqs'] else self.name2info[name]['prereqs']
Wayne Werner
What if `self.name2info[name]['prereqs']` contains `False` instead of `None`? I think you should test explicitly for None with `if x is None`...
ChristopheD
I suppose it depends on what he wants - it looks like (from his code) that the `prereqs` will either be None, or a list of values. Even a single-element list `[False]` equates to True. Of course **if the OP is planning to store any values besides a list or None, then you are correct**. But if `prereqs` will always be contained in a list, then my method is perfectly appropriate.
Wayne Werner
+1  A: 

If you can't fix the input you could do this (becomes 'better' if you need to add more):

for prop in ['prereqs', 'optionals']:
    if self.name2info[name][prop] is None:
        self.name2info[name][prop] = []

But replacing these values to be iterating over the empty list you just added doesn't make a whole lot of sense (unless maybe if you're appending something to this list at some point). So maybe you could just move the test for None-ness right before the iteration:

prereqs = self.name2info[name]['prereqs']
if prereqs is not None:
    for prereq in prereqs:
        do_stuff(prereq)

Slightly going off-topic now, but if you ever want to test if an item is iterable at all, a common (pythonic) way would be to write:

try:
    my_iterable_obj = iter(my_obj)
except TypeError:
    # not iterable
ChristopheD
A: 

Every one of those attribute and dict lookups takes time and processing. It's Pythonic to look up self.name2info[name] just once, and then work with a temporary name bound to that dict:

rec = self.name2info[name]
for key in "prereqs optionals required elective distance".split():
    if key not in rec or rec[key] is None:
        rec[key] = []

Now if need to add another category, like "AP_credit", you just add that to the string of key names.

Paul McGuire