__getattr__ for static/class variables in python

PythonClass MethodGetattr

Python Problem Overview


I have a class like:

class MyClass:
     Foo = 1
     Bar = 2

Whenever MyClass.Foo or MyClass.Bar is invoked, I need a custom method to be invoked before the value is returned. Is it possible in Python? I know it is possible if I create an instance of the class and I can define my own __getattr__ method. But my scnenario involves using this class as such without creating any instance of it.

Also I need a custom __str__ method to be invoked when str(MyClass.Foo) is invoked. Does Python provide such an option?

Python Solutions


Solution 1 - Python

__getattr__() and __str__() for an object are found on its class, so if you want to customize those things for a class, you need the class-of-a-class. A metaclass.

class FooType(type):
    def _foo_func(cls):
        return 'foo!'

    def _bar_func(cls):
        return 'bar!'

    def __getattr__(cls, key):
        if key == 'Foo':
            return cls._foo_func()
        elif key == 'Bar':
            return cls._bar_func()
        raise AttributeError(key)

    def __str__(cls):
        return 'custom str for %s' % (cls.__name__,)

class MyClass:
    __metaclass__ = FooType

# in python 3:
# class MyClass(metaclass=FooType):
#    pass


print MyClass.Foo
print MyClass.Bar
print str(MyClass)

printing:

foo!
bar!
custom str for MyClass

And no, an object can't intercept a request for a stringifying one of its attributes. The object returned for the attribute must define its own __str__() behavior.

Solution 2 - Python

(I know this is an old question, but since all the other answers use a metaclass...)

You can use the following simple classproperty descriptor:

class classproperty(object):
    """ @classmethod+@property """
    def __init__(self, f):
        self.f = classmethod(f)
    def __get__(self, *a):
        return self.f.__get__(*a)()

Use it like:

class MyClass(object):

     @classproperty
     def Foo(cls):
        do_something()
        return 1

     @classproperty
     def Bar(cls):
        do_something_else()
        return 2

Solution 3 - Python

For the first, you'll need to create a metaclass, and define __getattr__() on that.

class MyMetaclass(type):
  def __getattr__(self, name):
    return '%s result' % name

class MyClass(object):
  __metaclass__ = MyMetaclass

print MyClass.Foo

For the second, no. Calling str(MyClass.Foo) invokes MyClass.Foo.__str__(), so you'll need to return an appropriate type for MyClass.Foo.

Solution 4 - Python

Surprised no one pointed this one out:

class FooType(type):
    @property
    def Foo(cls):
        return "foo!"
    
    @property
    def Bar(cls):
        return "bar!"

class MyClass(metaclass=FooType):
    pass

Works:

>>> MyClass.Foo
'foo!'
>>> MyClass.Bar
'bar!'

(for Python 2.x, change definition of MyClass to:

class MyClass(object):
    __metaclass__ = FooType

)

What the other answers say about str holds true for this solution: It must be implemented on the type actually returned.

Solution 5 - Python

Depending on the case I use this pattern

class _TheRealClass:
    def __getattr__(self, attr):
       pass

LooksLikeAClass = _TheRealClass()

Then you import and use it.

from foo import LooksLikeAClass
LooksLikeAClass.some_attribute

This avoid use of metaclass, and handle some use cases.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTuxdudeView Question on Stackoverflow
Solution 1 - PythonMatt AndersonView Answer on Stackoverflow
Solution 2 - Pythonshx2View Answer on Stackoverflow
Solution 3 - PythonIgnacio Vazquez-AbramsView Answer on Stackoverflow
Solution 4 - PythonJonas SchäferView Answer on Stackoverflow
Solution 5 - PythongeckosView Answer on Stackoverflow