2

Question

In python, what is the shortest/easiest way to create a function which allows me to access a variable by concatenating two string literals to generate the variable's name?


Background

In C, I can do something like so:

#define CONCAT(x,y) x ## _ ## y

Then, later in my code, I could do something like:

int i = CONCAT(PRODUCT,BANANA).

Assuming a macro exists with the name PRODUCT_BANANA, its value is now assigned to i. I can accomplish something similar in shell scripts via indirection.


Question - Redux

How can I accomplish this same functionality in python? I'm doing this because I have a python class with thousands of variables for dozens of different products, i.e.

class A(object):
    BANANA_ADDRESS0 = 0xABCD;
    PINEAPPLE_ADDRESS0 = 0x1234;
    BANANA_ADDRESS1 = 0x4567;
    PINEAPPLE_ADDRESS1 = 0x1000;

I'd like to be able to have a function that can be, for example, executed via someFunc("BANANA", "ADDRESS0"), resolve the value as A.BANANA_ADDRESS0, and return the associated value (0xABCD, in this case).


Extra

Assuming the above is possible, is it possible to have the function always interpret the supplied function arguments as string literals, so function calls don't need the arguments wrapped in single/double quotes? i.e. so it can be called via someFunc(BANANA, ADDRESS0), rather than someFunc("BANANA", "ADDRESS0")?

Community
  • 1
  • 1
Cloud
  • 18,753
  • 15
  • 79
  • 153
  • It's common to build a string and then use [getattr](https://docs.python.org/3/library/functions.html#getattr) built-in: `getattr(A_obj, var_name)` – 9dogs Dec 22 '17 at 19:50
  • 1
    Why do you not use a dictionary for this? `A={"BANANA_ADDRESS0`:0xABCD}` and access it with `A["BANANA"+"_"+"ADDRESS0"]` (and probably replacing "BANANA" and "ADDRESS0" with a variable? – MEE Dec 22 '17 at 19:50
  • If you would need this to be global use the [`vars()`](https://docs.python.org/3/library/functions.html#vars) function that allows you to set and get global variables – MEE Dec 22 '17 at 19:51
  • @MEE The code is dynamically generated, and the script requires that the names be "interleaved" (i.e. create definitions for `ADDRESS0` for all products, create definitions for `ADDRESS1` for all products, etc). I need to be able to extract the value without changing the definitions. – Cloud Dec 22 '17 at 19:52
  • If you want to access (set/write) the value from the variable `var1` just do this: `A[var1 + "_" + (some way to get the second part)]` – MEE Dec 22 '17 at 19:54

3 Answers3

3

The first part is easy:

class A(object):
    BANANA_ADDRESS0 = 0xABCD;
    PINEAPPLE_ADDRESS0 = 0x1234;
    BANANA_ADDRESS1 = 0x4567;
    PINEAPPLE_ADDRESS1 = 0x1000;

    @classmethod
    def some_func(cls, name_a, name_b):
        name = '{}_{}'.format(name_a, name_b)
        return getattr(cls, name)

value = A.some_func('BANANA', 'ADDRESS1')

But the second part is not possible unless you have a limited set of names, in which case you would also have to have

BANANA = 'BANANA'
PINEAPPLE = 'PINEAPPLE'

etc

lxop
  • 7,596
  • 3
  • 27
  • 42
2

If you just want to do this for class/instance attributes, you want getattr. It takes a string argument, which you can construct any way you like:

getattr(A, "BANANA" + "_" + "ADDRESS0")

You could of course write a simple wrapper if you wanted, but in my opinion that would be obfuscatory for no benefit.

Building the string passed to getattr any way you want includes using other constructs like for loops if appropriate. For instance, suppose you have iterables of elements of the attribute names and want to get them all:

for first_part in first_parts:
   for second_part in second_parts:
       print(getattr(A, first_part + "_" + second_part))

For your "extra" block - well, BANANA means a variable named BANANA, not the literal string. You could define such a string constant if you really wanted, but I wouldn't. There is no good way to tell Python to always assume an unresolved variable name actually means a string literal, and I think it would be a bad idea to do so if you could.

Peter DeGlopper
  • 36,326
  • 7
  • 90
  • 83
1

Just use a dict. i.e.

A = {
    "BANANA_ADDRESS0": 0xABCD,
    "PINEAPPLE_ADDRESS0": 0x1234,
    "PINEAPPLE_ADDRESS1": 0x1000
} 

And then you can do something like this:

var1 = "BANANA" # or any other value
var2 = "ADDRESS0" # or any other value
myValue = A[var1 + "_"  + var2]
MEE
  • 503
  • 1
  • 11
  • 21