Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialunderfish
3,226 PointsIs using setattr() with **kwargs a good idea?
In the previous lesson I started messing around with ways to eliminate the redundancy of self.hit_points = kwargs.get('hit_points', 5)
and ended up doing the exact same thing Kenneth is teaching in this lesson (setattr(self, key, value)
).
In #python on IRC, I was told by someone this is a bad practice though. I just took his word for it since he seemed like an experienced programmer (and I've learned to just trust best practices), but after seeing Kenneth do this, I'm not sure what to think.
A few of the problems he mentioned were:
- typos break things (which I suppose they break stuff anyway)
- it's harder to debug
- makes code harder to understand
- lose autocompletion support
Kenneth Love care to chime in?
1 Answer
Chris Freeman
Treehouse Moderator 68,454 PointsAs you mentioned, using setattr(self, key, value)
can eliminate redundancy especially if there are a lot of kwargs to work through. One way to catch typos and rogue value would to use a "whitelist":
class MyClass():
def __init__(self, **kwargs):
whitelist = ['username', 'email', 'color', 'cheese']
for key, value in kwargs.items():
if key in whitelist:
setattr(self, key, value)
Testing:
$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class MyClass():
... def __init__(self, **kwargs):
... whitelist = ['username', 'email', 'color', 'cheese']
... for key, value in kwargs.items():
... if key in whitelist:
... setattr(self, key, value)
...
>>> c1 = MyClass()
>>> c1.username
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'username'
>>> c2 = MyClass(username='bob')
>>> c2.username
'bob'
>>> c3 = MyClass(car='Tesla')
>>> c3.car
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'car'
underfish
3,226 PointsThat's a pretty cool trick!
Maybe I'm being paranoid, but it still seems like some things might fall through the cracks. For instance, I can set car
, but if I don't call it for a while, it might not show up until it's a problem.
class MyClass():
def __init__(self, **kwargs):
whitelist = ['username', 'email', 'color', 'cheese']
for key, value in kwargs.items():
if key in whitelist:
setattr(self, key, value)
c1 = MyClass(username="Foo", car="Telsa")
print(c1.username) # Nothing to see here
class MyClass2():
def __init__(self, username, email, color, cheese):
return
c2 = MyClass2(username="Foo", car="Telsa") # Error'd!
print(c2.username) # Never gets here
Am I being overly cautious?
Chris Freeman
Treehouse Moderator 68,454 PointsYou can add an else
in the check to raise an error or print a warning:
class MyClass():
def __init__(self, **kwargs):
whitelist = ['username', 'email', 'color', 'cheese']
for key, value in kwargs.items():
if key in whitelist:
setattr(self, key, value)
else:
raise ValueError("unexpected kwarg value", key)
underfish
3,226 Pointsunderfish
3,226 PointsActually, typos would fail silently.
For instance:
colours='red'
would just add the attributecolours
when really we were looking forcolors
.