Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to list the attributes of a class without instantiating an object?

In Python 3.5, say I have:

class Foo:
    def __init__(self, bar, barbar):
        self.bar = bar
        self.barbar = barbar

I want to get the list ["bar", "barbar"] from the class.

I know I can do:

foo = Foo(1, 2)
foo.__dict__.keys()

Is there a way to get ["bar", "barbar"] without instantiating an object?

like image 805
vwrobel Avatar asked Jun 12 '17 13:06

vwrobel


People also ask

Can a class attribute be used without an instance of the class?

while you can access class attributes using an instance it's not safe to do so. In python, the instance of a class is referred to by the keyword self. Using this keyword you can access not only all instance attributes but also the class attributes.

How do I get all attributes of a class?

Method 1: To get the list of all the attributes, methods along with some inherited magic methods of a class, we use a built-in called dir() . Method 2: Another way of finding a list of attributes is by using the module inspect .

Which method is used to display the attributes of a class?

Accessing the attributes of a classgetattr() − A python method used to access the attribute of a class. hasattr() − A python method used to verify the presence of an attribute in a class. setattr() − A python method used to set an additional attribute in a class.

Can a class attribute be an object?

A class attribute is a Python variable that belongs to a class rather than a particular object. It is shared between all the objects of this class and it is defined outside the constructor function, __init__(self,...) , of the class.


2 Answers

No because the attributes are dynamic (so called instance attributes). Consider the following,

class Foo:
    def __init__( self ):
        self.bar = 1

    def twice( self ):
        self.barbar = 2

f = Foo()
print( list(f.__dict__.keys() ) )
f.twice()
print( list(f.__dict__.keys() ) )

In the first print, only f.bar was set, so that's the only attributes that's shown when printing the attribute keys. But after calling f.twice(), you create a new attribute to f and now printing it show both bar and barbar.

like image 173
Lærne Avatar answered Oct 12 '22 01:10

Lærne


Warning - The following isn't foolproof in always providing 100% correct data. If you end up having something like self.y = int(1) in your __init__, you will end up including the int in your collection of attributes, which is not a wanted result for your goals. Furthermore, if you happen to add a dynamic attribute somewhere in your code like Foo.some_attr = 'pork', then you will never see that either. Be aware of what it is that you are inspecting at what point of your code, and understand why you have and don't have those inclusions in your result. There are probably other "breakages" that will not give you the full 100% expectation of what are all the attributes associated with this class, but nonetheless, the following should give you something that you might be looking for.

However, I strongly suggest you take the advice of the other answers here and the duplicate that was flagged that explains why you can't/should not do this.

The following is a form of solution you can try to mess around with:

I will expand on the inspect answer.

However, I do question (and probably would advice against) the validity of doing something like this in production-ready code. For investigative purposes, sure, knock yourself out.

By using the inspect module as indicated already in one of the other answers, you can use the getmembers method which you can then iterate through the attributes and inspect the appropriate data you wish to investigate.

For example, you are questioning the dynamic attributes in the __init__

Therefore, we can take this example to illustrate:

from inspect import getmembers


class Foo:
    def __init__(self, x):
        self.x = x
        self.y = 1
        self.z = 'chicken'


members = getmembers(Foo)

for member in members:
    if '__init__' in member:
        print(member[1].__code__.co_names)

Your output will be a tuple:

('x', 'y', 'z')

Ultimately, as you inspect the class Foo to get its members, there are attributes you can further investigate as you iterate each member. Each member has attributes to further inspect, and so on. For this particular example, we focus on __init__ and inspect the __code__ (per documentation: The __code__ object representing the compiled function body) attribute which has an attribute called co_names which provides a tuple of members as indicated above with the output of running the code.

like image 13
idjaw Avatar answered Oct 11 '22 23:10

idjaw