11

I have been wondering for a while if there is easier way to assign class attributes to method local namespace. For example, in dosomething method, I explicitly make references to self.a and self.b:

class test:
    def __init__(self):
        self.a = 10
        self.b = 20

    def dosomething(self):
        a = self.a
        b = self.b
        return(a + b)

But sometimes I have a lot of variables (more than 10) and it gets messy to type and look at - I would have bunch of var = self.var statements at the beginning of a method.

Is there any way to do this more compact way? (I know updating local() is not a good idea)

Edit: Ideally, what I want is:

def dosomething(self):
    populate_local_namespace('a', 'b')
    return(a + b)
3
  • Why are you trying to do this in the first place? What's wrong with having a one-line dosomething method that looks like "return (self.a + self.b)"? What benefit do you expect to get from this? Commented Jun 9, 2013 at 4:07
  • 4
    I have my reasons - especially, I'm doing complex calculations and many self.'s make expressions very long and ugly. Commented Jun 9, 2013 at 4:16
  • @munn why would you want to pollute the code with all those self's .. Commented May 21, 2019 at 14:26

3 Answers 3

20

Q. Is there any way to do this more compact way?

1. If the variables are read-only, it would be reasonably Pythonic to factor-out a multi-variable accessor method:

class Test:

    def __init__(self):
        self.a = 10
        self.b = 20
        self.c = 30

    def _read_vars(self):
        return self.a, self.b, self.c

    def dosomething(self):
        a, b, c = self._read_vars()
        return a + b * c

    def dosomethingelse(self):
        a, b, c = self._read_vars()
        return a - b * c

If the variables aren't read-only, it is best to stick with self.inst_var = value. That is the normal way to write Python code and is usually what most people expect.


2. Once in a while you will see people abbreviate self with a shorter variable name. It is used when the readability benefits of decluttering outweigh the readability cost of using a non-standard variable name:

def updatesomethings(s):
    s.a, s.b, s.c = s.a + s.c, s.b - s.a, s.c * s.b

3. Another way to handle a very large number instance variable is to store them in a mutable container for ease of packing and unpacking:

class Test:

    def __init__(self, a, b, c, d, e, f, g, h, i):
        self._vars = [a, b, c, d, e, f, g, h, i]

    def fancy_stuff(self):
        a, b, c, d, e, f, g, h, i = self._vars
        a += d * h - g
        b -= e * f - c
        g = a + b - i
        self._vars[:] = a, b, c, d, e, f, g, h, i

4. There is also a dictionary manipulation approach that would work, but it has a code smell that most Pythonistas would avoid:

def updatesomethings(self):
    a = 100
    b = 200
    c = 300
    vars(self).update(locals())
    del self.self
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks. Actually this is exactly what I have been doing. But this is not flexible enough, since when I want to change the variables I have to change both places. It would be great if there is a way where I can just pass the name of an attribute to a function as a string and the attribute becomes a local variable.
@joon I've added some other options for you as well.
Yes 4 does look scary :) I would just use calling function way (_read_vars()) for now. Thanks a lot for your help!!
@RaymondHettinger: Isn't it time for a variation of the splash-operator to automatically add arguments from __init__ to an object?! Analog to the *args and **kwargs we could do a +arg1, +args=None, which would automatically add the argument as an attribute to an object, sparing the writer and reader to go over superfluos repetition of assigning __init__-arguments to object-attributes.
concerning 3: can you explain what self._vars[:] = a, b, c, d, e, f, g, h, i does and why you didn't use self._vars = [a, b, c, d, e, f, g, h, i]?
-2

You can easily solve this problem with a tradeoff, by storing the variables in a dictionary.

data = {}
copy_to_local_variables = ["a", "b", "c", "d"]
for var_name in copy_to_local_variables:
    data[var_name] = getattr(self, var_name)

(Though I am unable to understand why you need to copy class attributes to method local namespace)

1 Comment

Won't this just make a dict named data with those variables instead of making local variables referencing to class attributes? I don't think this is what I want.
-2

I found another way:

def dosomething(self):
    for key in ['a', 'b']:
       exec('{} = self.{}'.format(key, key))

    return(a + b)

I don't know if this is dangerous or not. Would be great if someone can comment on this.

2 Comments

It would be dangerous if key would ever be a string that came from an untrusted source.
It is also likely to be very slow, since every time the exec() call is made Python has to lex, parse, compile, and execute the brand-new statement as though seeing it for the first time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.