Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python equivalent of Java's getClass().getFields()

I'm converting a piece of code from Java to Python and I don't know how to translate the following:

Field[] fields = getClass().getFields();
    for (int i = 0; i < fields.length; i++ ) {
        if (fields[i].getName().startsWith((String) param){ ....
like image 646
Butoni Avatar asked May 26 '11 13:05

Butoni


2 Answers

In Python, you can query an object's bindings with __dict__, e.g.:

>>> class A:
...     def foo(self): return "bar"
...
>>> A.__dict__
{'__module__': '__main__', 'foo': <function foo at 0x7ff3d79c>, '__doc__': None}

Also, this has been asked from a C# standpoint in: How to enumerate an object's properties in Python?

Instead of using __dict__ directly, you can use inspect.getmembers(object[, predicate]), which has useful methods like inspect.ismethod(object)

like image 155
csl Avatar answered Sep 27 '22 18:09

csl


Firstly, I'd emphasize that there is no such thing as getClass().getFields() in Python because an object can have a lot of fields that are not defined by class. Actually, to create a field in Python you just need to attribute a value to it. The fields are not defined, the are created:

>>> class Foo(object):
...     def __init__(self, value):
...         # The __init__ method will create a field
...         self.value = value
... 
>>> foo = Foo(42)
>>> foo.value
42
>>> # Accessing inexistent field
... foo.another_value
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'Foo' object has no attribute 'another_value'
>>> # Creating the field
... foo.another_value = 34
>>> # Now I can use it
... foo.another_value
34

So, you do not get the fields from a class. Instead, you get the fields from an object.

Also, Python methods are only fields with some special values. Methods are merely instances of functions:

>>> type(foo.__init__)

It is important to note that to make it clear that there is no such method as getClass().getMethods() in Python and the "equivalent" of getClass().getFields() will return methods as well.

Said that, how could you get the fields (or attributes, as they are frequently called in Python)? Of course you cannot get them from the class, since the objects store them. So you can get the name of the attributes of an object using the dir() function:

>>> dir(foo)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
 '__getattribute__', '__hash__', '__init__', '__module__', '__new__', 
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
 '__str__', '__subclasshook__', '__weakref__', 'another_value', 'value']

Once you got the attribute names, you can get each of them by using the getattr() function:

>>> getattr(foo, 'value')
42

To get all of them, you can use list comprehensions:

>>> [getattr(foo, attrname) for attrname in dir(foo)]
[<class '__main__.Foo'>, <method-wrapper '__delattr__' of Foo object at 0x2e36b0>,
 {'another_value': 34, 'value': 42}, None, <built-in method __format__ of Foo object at 0x2e36b0>, 
 <method-wrapper '__getattribute__' of Foo object at 0x2e36b0>, 
 ... # Lots of stuff
 34, 42]

At the end, you can find the values you set on some attributes.

However, this list will contain methods, too. Remember they are attributes as well. In this case, we can make our list comprehension avoid callable attrbutes:

>>> [attrname for attrname in dir(foo) if not callable(getattr(foo, attrname))]
['__dict__', '__doc__', '__module__', '__weakref__', 'another_value', 'value']

Now, getting the actual values:

>>> [getattr(foo, attrname) for attrname in dir(foo)
...      if not callable(getattr(foo, attrname))]
[{'another_value': 34, 'value': 42}, None, '__main__', None, 34, 42]

There still are some strange values there, such as __dict__, __doc__ etc. They are some stuff you may want to ignore. If so, just put another criteria in your list comprehension:

>>> [attrname for attrname in dir(foo)
...     if not attrname.startswith('__') and
...         not callable(getattr(foo, attrname))]
['another_value', 'value']
>>> [getattr(foo, attrname) for attrname in dir(foo)
...     if not attrname.startswith('__') and
...         not callable(getattr(foo, attrname))]
[34, 42]

There are other ways to do such things. For example, you can look a the __dict__ and __slots__ attributes of an object. However, I find the method I've presented to be clearer for beginners.

EDIT Two more points. First, the cls solution is really good because it suggests you to look at the inspect module.

Also, you may want to get both the name and the value of an attribute. You can get it generating a list of tuples:

>>> [(attrname, getattr(foo, attrname)) for attrname in dir(foo)
...     if not attrname.startswith('__') and
...         not callable(getattr(foo, attrname))]
[('another_value', 34), ('value', 42)]

Fortunately, the inspect.getmembers() function suggested by cls does it better.

>>> import inspect
>>> inspect.getmembers(foo)
[('__class__', <class '__main__.Foo'>),
 # ... Lots of stuff ...
 ('another_value', 34), ('value', 42)]

To remove methods, just avoid callables:

>>> inspect.getmembers(foo, lambda attr: not callable(attr))
[('__dict__', {'another_value': 34, 'value': 42}), ('__doc__', None), ('__module__', '__main__'), ('__weakref__', None), ('another_value', 34), ('value', 42)]

(Unfortunately, inspect.ismethod() didn't not work as I expected.)

There are a lot of internal stuff yet and we cannot get then out directly as we did with the methods. Nothing that a list comprehension can solve again:

>>> [(name, value) for name, value in inspect.getmembers(foo, lambda attr: not callable(attr))
...         if not name.startswith('__')]
[('another_value', 34), ('value', 42)]

Python is a very dynamic language and this solution cannot work as well in some cases. Consider that it is possible to have an object which should storage a function to be used somewhere. A function is a callable object and the attribute will not be presented. However, it is logically an attribute, a data to be used. You should have this kind of stuff in mind. However, I bet you'll not get such problems too frequently.

HTH

like image 45
brandizzi Avatar answered Sep 27 '22 18:09

brandizzi