0

In Python, it's no secret that you can implement attributes of an instance of a class dynamically. Here's what that looks like:

class MyClass(object):
    def __getattribute__(self, name):
        if name == 'dynamic_attribute':
            return "dynamic attribute value"
        return super().__getattribute__(name)

# Create an instance of my class
obj = MyClass()

# I've implemented the 'dynamic_attribute' attribute dynamically, so I can
# reference it without ever having assigned it a value
print(obj.dynamic_attribute)

# As I've only implemented that one attribute dynamically, other attributes
# behave in the standard way
try:
    print(obj.regular_attribute)
except AttributeError as ex:
    print(f"EXPECTED ERROR: {ex}")
obj.regular_attribute = "regular attribute value"
print(obj.regular_attribute)

Result:

dynamic attribute value
EXPECTED ERROR: 'MyClass' object has no attribute 'regular_attribute'
regular attribute value

Is there any way to do this same thing with a class attribute rather than a class instance attribute? Here's an example:

print(MyClass.dynamic_class_attribute)

I want this line of code to return a value that is computed somewhere in my code without me having to have defined a 'dynamic_class_attribute' attribute explicitly on the MyClass class. Can this be done? TIA.

PS: I've never done anything with metaclasses, but I know of their existence. I read up a little on them hoping for an obvious answer to this question. I didn't find one.

UPDATE:

Someone asked why I wanted this. Maybe others would find my use case interesting. I was basically just being a bit anal about simplifying an API I'm creating. It isn't quite as simple as this, but it's basically a singleton thing, and all I'm really doing is trying to replace this:

SomeSingletonClass.get().some_method()

with this:

SomeSingletonClass.some_method()

At any moment, there's exactly one SomeSingletonClass instance, and I reference that instance a lot, so I wanted to simplify the global reference to it as much as possible. With Raymond H's answer, I did it. It works great.

4
  • 1
    Why do you want to do this? Commented Aug 27, 2022 at 23:11
  • 1
    Because many people ask how to implement obscure scenarios, when in fact the problem they want to solve actually has a mainstream solution. Commented Aug 27, 2022 at 23:16
  • Ah...yeah, gotcha. I'm doing this because I want an API I'm developing to take on a very specific form. I know there are other ways I can do this. I'm already doing them. I just want something even briefer and cleaner. Commented Aug 27, 2022 at 23:19
  • Yes, you need a metaclass. See What are metaclasses in Python? for a general overview. For instance attributes, you set up the logic in the class. Classes are objects; they are instances of type by default. A class attribute is simply an instance attribute of something that happens to be a class. So you would have to set up the logic in the class's class, i.e. a metaclass. You cannot alter type, but you can make the class use a different metaclass instead (likely one that subclasses type). Commented Aug 27, 2022 at 23:21

1 Answer 1

7

Is there any way to do this same thing with a class attribute rather than a class instance attribute?

Yes, just move the logic into a metaclass:

class MyMetaClass(type):
    def __getattribute__(self, name):
        if name == 'dynamic_attribute':
            return "dynamic attribute value"
        return super().__getattribute__(name)

class C(metaclass=MyMetaClass):
    pass

print(C.dynamic_attribute)

The basic idea is that regular classes control the behavior of instances and metaclasses control the behavior of classes.

Sign up to request clarification or add additional context in comments.

2 Comments

Ah...nice. This may very well do it. Thanks! The only question I have is if I have enough context to do what I want to do. But that should be reachable via the self parameter. I'll give it a shot. Thanks!
Maybe it is worth mentioning this works for ordinary methods and attributes, but won't work for special "dunder" methods. That is, if you create an __add__ method dynamically in the metaclass by returning it in __getattribute__, instances of that class will not be able to operate with the +: for the behavior of the operator defining and other special methods, Python checks the methods directly in the class slots, and don't go through __getattribute__.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.