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.
CodePudding user response:
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.