Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inverse of hasattr in Python

Tags:

python

hasattr(obj, attribute) is used to check if an object has the specified attribute but given an attribute is there a way to know where (all) it is defined?

Assume that my code is getting the name of an attribute (or a classmethod) as string and I want to invoke classname.attribute but I don't have the classname.

One solution that comes to my mind is this

def finder(attr):
    for obj in globals():
        try:
            if globals()[obj].__dict__[attr]:
                return(globals()[obj])
        except:
            ...

usage:

class Lime(object):
    @classmethod
    def lfunc(self):
        print('Classic')

getattr(finder('lfunc'),'lfunc')() #Runs lfunc method of Lime class

I am quite sure that this is not the best (oe even proper way) to do it. Can someone please provide a better way.

like image 975
Dev Maha Avatar asked Mar 25 '23 07:03

Dev Maha


1 Answers

It is always "possible". Wether it is desirable is another history.

A quick and dirty way to do it is to iterate linearly over all classes and check if any define the attribute you have. Of course, that is subject to conflicts, and it will yield the first class that has such a named attribute. If it exists in more than one, it is up to you to decide which you want:

def finder(attr):
    for cls in object.__subclasses__(): 
         if hasattr(cls, attr):
              return cls
    raise ValueError

Instead of searching in "globals" this searches all subclasses of "object" - thus the classes to be found don't need to be in the namespace of the module where the finder function is.

If your methods are unique in teh set of classes you are searching, though, maybe you could just assemble a mapping of all methods and use it to call them instead.

Let's suppose all your classes inehrit from a class named "Base":

mapper = {attr_name:getattr(cls, attr_name)  for cls in base.__subclasses__() for attr_name, obj in cls.__dict__.items()
             if isinstance(obj, classmethod) }

And you call them with mapper['attrname']()

This avoids a linear search at each method call and thus would be much better.

- EDIT -

__subclassess__ just find the direct subclasses of a class, not the inheritance tree - so it won't be usefull in "real life" - maybe it is in the specifc case the OP has in its hands.
If one needs to find things across a inheritance tree, one needs to recurse over the each subclass as well.

As for old-style classes: of course this won't work - that is one of the motives for which they are broken by default in new code.

As for non-class attributes: they can only be found inspecting instances anyway - so another method has to be thought of - does not seem to be the concern of the O.P. here.

like image 54
jsbueno Avatar answered Apr 06 '23 02:04

jsbueno