Alternative to locals() in printing a table with a header
Asked Answered
R

3

1

[Python 3.1]

Edit: mistake in the original code.

I need to print a table. The first row should be a header, which consists of column names separated by tabs. The following rows should contain the data (also tab-separated).

To clarify, let's say I have columns "speed", "power", "weight". I originally wrote the following code, with the help from a related question I asked earlier:

column_names = ['speed', 'power', 'weight']

def f(row_number):
  # some calculations here to populate variables speed, power, weight
  # e.g., power = retrieve_avg_power(row_number) * 2.5
  # e.g., speed = math.sqrt(power) / 2
  # etc.
  locals_ = locals()
  return {x : locals_[x] for x in column_names}

def print_table(rows):
  print(*column_names, sep = '\t')
  for row_number in range(rows):
    row = f(row_number)
    print(*[row[x] for x in component_names], sep = '\t')

But then I learned that I should avoid using locals() if possible.

Now I'm stuck. I don't want to type the list of all the column names more than once. I don't want to rely on the fact that every dictionary I create inside f() is likely to iterate through its keys in the same order. And I don't want to use locals().

Note that the functions print_table() and f() do a lot of other stuff; so I have to keep them separate.

How should I write the code?

Rattat answered 1/11, 2010 at 8:25 Comment(0)
K
2
class Columns:
    pass

def f(row_number):
    c = Columns()
    c.power = retrieve_avg_power(row_number) * 2.5
    c.speed = math.sqrt(power) / 2
    return c.__dict__

This also lets you specify which of the variables are meant as columns, instead of rather being temporary in the function.

Kaohsiung answered 1/11, 2010 at 8:44 Comment(4)
Would this be essentially identical to creating a dictionary columns = {}, and populating it like this: columns['speed'] = math.sqrt(columns['power'])/2?Rattat
And btw, is using __dict__ any less of a problem for optimizers than using locals()?Rattat
Exactly so, except that I find the attribute syntax easier to read and type.Abohm
Storing into an object is much more expensive than storing into a local variable. So you shouldn't assign to c.power in a loop. However, since you eventually need to store the values all in a dictionary, there is no cost involved. The problem to optimizers is this: if locals() is used, they also need to stop using efficient access to local variables (so that they actually become slower than instance attributes).Abohm
P
0

You could use an OrderedDict to fix the order of the dictionaries. But as I see it that isn't even necessary. You are always taking the keys from the column_names list (except in the last line, I assume that is a typo), so the order of the values will always be the same.

Perversity answered 1/11, 2010 at 8:29 Comment(2)
I think It's hard to populate an OrderedDict without locals() and without repeating the names. (Also, note that the order I calculate the variables in, isn't necessarily the same as the column order required in the output table.)Rattat
@max: Ok, now I understand the problem.Kazoo
S
0

an alternative to locals() will be to use the inspect module

import inspect

def f(row_number):
    # some calculations here to populate variables speed, power, weight
    # e.g., power = retrieve_avg_power(row_number) * 2.5
    # e.g., speed = math.sqrt(power) / 2
    # etc.
    locals_ = inspect.currentframe().f_locals
    return {x : locals_[x] for x in column_names }
Sylvester answered 1/11, 2010 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.