setattr with kwargs, pythonic or not?
Asked Answered
D

3

19

I'm using __init__() like this in some SQLAlchemy ORM classes that have many parameters (upto 20).

def __init__(self, **kwargs):
    for k, v in kwargs.iteritems():
        setattr(self, k, v)

Is it "pythonic" to set attributes like this?

Devaluate answered 11/4, 2009 at 6:31 Comment(0)
F
29

Yes. Another way to do this is.

def __init__(self, **kwargs):
    self.__dict__.update( kwargs )
Fragrant answered 11/4, 2009 at 11:4 Comment(3)
This one is shorter than the version in the question but keep in mind that it won't work for attributes that are properties (or other descriptors).Tantalizing
I am not recommend doing like this. It would sometimes meet some error. when update __dict__ builtin attribute.Pediatrics
This would also not work with __slot__ classes, where __dict__ does not exist.Override
D
9

Yes, if there's not a "nicer" way of supplying the arguments.

For example, using your ORM classes you mention, perhaps it would be more Python'y to allow..

col = Varchar()
col.index = True
col.length = 255

..rather than..

col = Varchar(index = True, length = 255)

Okay that's not the best example, since the **kwargs method would actually be nicer.. but my point is you should always consider alternative methods of achieving something, before using sometimes-discouraged things like **kwargs..

Another thing to keep in mind is you might lose behaviour a user expects, such as raising a TypeError if the user supplies an invalid keyword arg, which could be worked around like..

def __init__(self, **kwargs):
    valid_kwargs = ['x', 'y', 'z']
    for k, v in kwargs.iteritems():
        if k not in valid_kwargs:
            raise TypeError("Invalid keyword argument %s" % k)
        setattr(self, k, v)

A final thing to consider:

class Hmm:
    def __init__(self, **kwargs):
        for k, v in kwargs.iteritems():
            setattr(self, k, v)
    def mymethod(self):
        print "mymethod should print this message.."

x = Hmm(mymethod = None)
x.mymethod() # raises TypeError: 'NoneType' object is not callable
Dmitri answered 13/4, 2009 at 14:58 Comment(2)
Is it possible to catch the problem like the one you showed in class Hmm? For example, can __init__ check whether an attribute is already there, and if so, raise an exception rather than execute setattr?Whiff
@Whiff yep, simplest way would be to do if hasattr(self, k): ... and throw an errorDmitri
B
1

To me it seems pretty pythonic if you only need this in one place in your code.

The following link provides a more 'generic' approach to the same problem (e.g. with a decorator and some extra functionality), have a look at: http://code.activestate.com/recipes/551763/

Bulahbulawayo answered 11/4, 2009 at 6:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.