0

If we have a class with variables, is there a way to turn those variables into a list?

For example:

class Son(_Dad):
    var1 = 'foo'
    var2 = 'bar'
    # code here to generate varList = ['foo', 'bar']

this class will be changed over time, and variables might be added and removed. Would it be possible to generate a list using the previously declared variables? I tried messing around with the dir() function but to the best of my knowledge, I could only get attributes from the class, and I need variables.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • You might be able to leverage the `vars()` method, which shows a number of the object's attributes: https://docs.python.org/3/library/functions.html#vars – Brydenr Dec 12 '19 at 15:48
  • i dont think python gives you a way to find all variables in a method and put them in a list. You can create a list and add all variables you are using by .append method – Jawad Dec 12 '19 at 15:48
  • @Jawad. This is not happening in a method – Mad Physicist Dec 12 '19 at 15:49
  • I mean you can always do `my_list = [var1, var2]` – Maxxik CZ Dec 12 '19 at 15:50
  • Why do you not just declare a list/dictionary in your class? – Mad Physicist Dec 12 '19 at 15:50
  • @MadPhysicist This class has several hundred hard coded variables that are used as reference variables throughout a code base. As the code base is developed, some variables are removed, and new ones are added. A list would not work since the variables would have to be referenced by index list[100], and if an earlier variable is removed, list[100] now has a new value that would cause a failure in a test. I am fiddling with the idea of using a dictionary though. But this (if it's possible) would be the best of both worlds for what I need. – Hierarchy009 Dec 12 '19 at 16:00
  • 1
    @Hierarchy009. Why do you need a list then? This seems a bit XY-ish. Why not use a dictionary (which you can subset from the class `__dict__`)? – Mad Physicist Dec 12 '19 at 16:13

6 Answers6

0

You can use:

[attr for attr in dir(class_instance) if not callable(getattr(class_instance, attr)) and not attr.startswith("__")]

where class_instance is an instance of your class.

Emmanuel Ban
  • 105
  • 9
0

You can do this in the class itself without creating it's object. Here I am leveraging locals and vars method. Check below:

class Abc:
    a = 5
    b = 3
    attr_list = locals().copy()
    all_vars = vars().copy()
    c = [all_vars[attr] for attr in attr_list if attr in all_vars and not callable(all_vars[attr]) and not attr.startswith("__")]

    def check_c(self):
        print self.c

k = Abc()
k.check_c() #prints [5,3]

just define all the variables before attr_list and then the variable c will contain the list of values of all the variables. Hope this helps.

HNMN3
  • 542
  • 3
  • 9
0

You can use vars() to do it (tested in Python 3.8.0). It will examine everything defined in the class up to the point it is used.

class _Dad: pass


class Son(_Dad):
    var1 = 'foo'
    var2 = 'bar'

    # Generate varlist = ['foo', 'bar']
    varlist = [value for key, value in vars().items() if not key.startswith('_')]
    print('varlist:', varlist)

    def method():  # Won't be included in varlist.
        pass

    var3 = 'baz'  # Won't be included in varlist.

Output:

varlist: ['foo', 'bar']
martineau
  • 119,623
  • 25
  • 170
  • 301
0

Have you thought about using a metaclass? You could grab all new class fields on its creation, and then use them to create either the list or the dictionary if needed.

>>>class DadMeta(type):
...    def __new__(mcs, name, bases, dct):
...        values_dict = {}
...        values_list = []
...        for name, value in dct.items():
...            if not callable(value) and not name.startswith("__"):
...                values_dict[name] = value
...                values_list.append(value)
...        
...        cls = super().__new__(mcs, name, bases, dct)   
...        cls.values_list = getattr(cls, "values_list", []) + values_list
...        cls.values_dict = {**getattr(cls, "values_dict", {}), **values_dict}
...               
...        return cls
...                
>>>class Dad(metaclass=DadMeta):
...    var_1 = "foo"
...    var_2 = "bar"
...
>>>Dad.values_list
['foo', 'bar']
>>>Dad.values_dict
{'var_1': 'foo', 'var_2': 'bar'}
>>>class Son(Dad):
...    var_3 = "test"
...    var_4 = "meh"
...
>>>Son.values_list
['foo', 'bar', 'test', 'meh']
>>>Dad.values_list
['foo', 'bar']
>>>Son.values_dict
{'var_1': 'foo', 'var_2': 'bar', 'var_3': 'test', 'var_4': 'meh'}

This should also work nicely with inheritance, if needed.

mfrackowiak
  • 1,294
  • 8
  • 11
-1

You might be looking for This answer here. In your case this would be:

a for a in dir(Son) if not a.startswith('__')]
Felix
  • 82
  • 4
  • 1
    This will return the methods too. – Emmanuel Ban Dec 12 '19 at 15:54
  • 1
    @EmmanuelBan Yes that's right. The linked answer (and of course your answer) handle this case too. – Felix Dec 12 '19 at 15:57
  • If the variable name is __foo it will be mangled as _Son__var1 and it won't print properly the variable name in that case. – partha biswas Dec 12 '19 at 16:02
  • It's python standard, that internal variables start and end with __ so in my example there is the need for a check at the end for __ aswell. Anyway, it's kind of personal preference if you want to treat variables only starting with __ as internal variables or not. – Felix Dec 12 '19 at 16:09
-1

You can do this using a global list variable

class list_appender:
    def __init__(self):
        global list_out
        list_out=[]
    def appender(self,input_ele):
        self.input_ele = input_ele
        list_out.append(self.input_ele)
        return list_out

k= list_appender()

print(k.appender("foo"))

print(k.appender("bar"))

the output of this will be

['foo']
['foo', 'bar']
McLovin
  • 555
  • 8
  • 20