Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - short syntax and list comprehension and iterations

I am a big fan of Python's list comprehension and I think it's actually easier to read than a regular iteration.

I am writing a constructor that has optional arguments in **kwargs that could be used to set the class' attrs, and I'd like to write it as a list comprehension, but it seems pointless to have a list which doesn't actually do anything. consider this:

class Book(object):
    attrs = ('id', 'title', 'authors',...)
    def __init__(self, *args, **kwargs):
        [setattr(self, attr, kwargs.get(attr) 
              for attr in O.attrs if kwargs.get(attr)]

While the syntax looks good, I think this is bad coding because you have created a list of

[None, None, None...]

that's just sitting out there. And yes granted I know it's collected after the constructor runs, but I still don't like it. If I read someone's code that had that, I'd be unimpressed.

Is there maybe a better way to do that iteration that's as as clean as a list comprehension but doesn't create useless objects

EDIT:

In response to use the loop

class Book(object):
    attrs = ('id', 'title', 'authors',...)
    def __init__(self, *args, **kwargs):
         for attr in attrs:
             if kwargs.get(attr):
                 setattr(self, attr, kwargs.get(attr)

Is that what you guys mean? If not, what a better way to write that?

like image 605
Sam Hammamy Avatar asked Apr 08 '26 22:04

Sam Hammamy


2 Answers

Instead of a list comprehension, you could use a generator expression, updating the instance __dict__ attribute directly:

class Book(object):
    attrs = ('id', 'title', 'authors',...)
    def __init__(self, *args, **kwargs):
        self.__dict__.update(
            (attr, kwargs[k]) for k in kwargs.viewkeys() & self.attrs)

This also makes use of dictionary views; in Python 3, just use kwargs.keys() instead of kwargs.viewkeys(). A dictionary view acts as a set; the & self.attrs expression produces the intersection between the keys of kwargs and your list of attributes.

The generator expression produces (key, value) pairs, one of the two acceptable inputs for the dict.update() method.

Updating self.__dict__ is just fine; the Python 2.7 standard library uses this technique in 7 different locations, plus some 30 other cases of mutation. Do take into account that if you are using descriptors (@property and / or other custom descriptors) then accessing self.__dict__ would bypass those.

As for list comprehensions for the side effects: just don't. You are indeed wasting cycles creating a list object that is immediately discarded, confusing expectations as to what a list comprehension is for.

You can also combine this with explicit setattr() calls, in a regular for loop:

for attr in kwargs.viewkeys() & self.attrs:
    setattr(self, attr, kwargs[attr])
like image 75
Martijn Pieters Avatar answered Apr 11 '26 11:04

Martijn Pieters


How about:

for attr in [a for a in attrs if a in kwargs]:
    setattr(self, attr, kwargs[attr])

That's reasonably simple and sweet...

Or as Chris recommends:

for attr in (a for a in attrs if a in kwargs):

would be better. (Generator rather than list)

like image 41
Daniel Avatar answered Apr 11 '26 12:04

Daniel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!