Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assign function arguments to `self`

I've noticed that a common pattern I use is to assign SomeClass.__init__() arguments to self attributes of the same name. Example:

class SomeClass():     def __init__(self, a, b, c):         self.a = a         self.b = b         self.c = c 

In fact it must be a common task for others as well as PyDev has a shortcut for this - if you place the cursor on the parameter list and click Ctrl+1 you're given the option to Assign parameters to attributes which will create that boilerplate code for you.

Is there a different, short and elegant way to perform this assignment?

like image 960
Jonathan Livni Avatar asked Dec 30 '11 18:12

Jonathan Livni


People also ask

What is Self in function parameter?

The self parameter is a reference to the current instance of the class, and is used to access variables that belongs to the class.

How do you pass a value to yourself in Python?

Self is the first argument to be passed in Constructor and Instance Method. Self must be provided as a First parameter to the Instance method and constructor. If you don't provide it, it will cause an error.

Can you assign self Python?

self is only a reference to the current instance within the method. You can't change your instance by setting self .


2 Answers

I sympathize with your sense that boilerplate code is a bad thing. But in this case, I'm not sure there even could be a better alternative. Let's consider the possibilities.

If you're talking about just a few variables, then a series of self.x = x lines is easy to read. In fact, I think its explicitness makes that approach preferable from a readability standpoint. And while it might be a slight pain to type, that alone isn't quite enough to justify a new language construct that might obscure what's really going on. Certainly using vars(self).update() shenanigans would be more confusing than it's worth in this case.

On the other hand, if you're passing nine, ten, or more parameters to __init__, you probably need to refactor anyway. So this concern really only applies to cases that involve passing, say, 5-8 parameters. Now I can see how eight lines of self.x = x would be annoying both to type and to read; but I'm not sure that the 5-8 parameter case is common enough or troublesome enough to justify using a different method. So I think that, while the concern you're raising is a good one in principle, in practice, there are other limiting issues that make it irrelevant.

To make this point more concrete, let's consider a function that takes an object, a dict, and a list of names, and assigns values from the dict to names from the list. This ensures that you're still being explicit about which variables are being assigned to self. (I would never suggest a solution to this problem that didn't call for an explicit enumeration of the variables to be assigned; that would be a rare-earth bug magnet):

>>> def assign_attributes(obj, localdict, names): ...     for name in names: ...         setattr(obj, name, localdict[name]) ... >>> class SomeClass(): ...     def __init__(self, a, b, c): ...         assign_attributes(self, vars(), ['a', 'b', 'c']) 

Now, while not horribly unattractive, this is still harder to figure out than a straightforward series of self.x = x lines. And it's also longer and more trouble to type than one, two, and maybe even three or four lines, depending on circumstances. So you only get certain payoff starting with the five-parameter case. But that's also the exact moment that you begin to approach the limit on human short-term memory capacity (= 7 +/- 2 "chunks"). So in this case, your code is already a bit challenging to read, and this would only make it more challenging.

like image 133
senderle Avatar answered Sep 17 '22 13:09

senderle


You could do this, which has the virtue of simplicity:

>>>  class C(object):     def __init__(self, **kwargs):         self.__dict__ = dict(kwargs) 

This leaves it up to whatever code creates an instance of C to decide what the instance's attributes will be after construction, e.g.:

>>> c = C(a='a', b='b', c='c') >>> c.a, c.b, c.c ('a', 'b', 'c') 

If you want all C objects to have a, b, and c attributes, this approach won't be useful.

(BTW, this pattern comes from Guido his own bad self, as a general solution to the problem of defining enums in Python. Create a class like the above called Enum, and then you can write code like Colors = Enum(Red=0, Green=1, Blue=2), and henceforth use Colors.Red, Colors.Green, and Colors.Blue.)

It's a worthwhile exercise to figure out what kinds of problems you could have if you set self.__dict__ to kwargs instead of dict(kwargs).

like image 45
Robert Rossney Avatar answered Sep 19 '22 13:09

Robert Rossney