If by "call" you actually mean call and not just "access" you can simply implement a __call__ method:
class PropertyExpr:
def __init__(self, value, expr):
self.value = value
self.expr = expr
def __call__(self):
return self.value
prop1 = PropertyExpr(5, "test")
val = prop1()
print(val)
Output:
5
In that case, the result of calling prop1() can be really anything.
Other than that, what you want is not possible. You could override the __new__ method, but that will also change the type of what you're creating. So if you're returning 5 your object will be 5, but it will also be an int and no longer an instance of PropertyExpr and all your additional attributes will be lost:
class PropertyExpr():
def __new__(cls, value, expr):
return value
def __init__(self, value, expr):
self.value = value
self.expr = expr
prop1 = PropertyExpr(5, "test")
print(prop1, type(prop1))
try:
print(prop1.expr)
except Exception as e:
print(e)
Output:
5 <class 'int'>
'int' object has no attribute 'expr'
After some trying around, I've figured out a way to dynamically change the type of the constructor, however I would advise against actually using this:
class PropertyExpr:
def __new__(cls, tp, value, *args, **kwargs):
return type("FakeType", (tp,), kwargs)(value)
prop1 = PropertyExpr(int, 5, expr="expr int")
print(prop1, " -> ", prop1.expr)
prop2 = PropertyExpr(list, "5", expr="expr list")
print(prop2, " -> ", prop2.expr)
prop3 = PropertyExpr(str, "abc", expr="expr string")
print(prop3, " -> ", prop3.expr)
Output:
5 -> expr int
['5'] -> expr list
abc -> expr string
You can pass the type you wish to sub-class as the first parameter, the second parameter should be a value accepted by the type's contructor and then you can pass in arbitrary kwargs that will be added as attributes to the created object.
is there a way to make is so that type(prop1) would still be PropertyExpr? Edit: for example, if we can do isinstance(prop1, PropertyExpr) = True then everything would be perfect
I could not get that to work (that does not mean that others cannot), but I managed to make it work with multi inheritance, so you can use isinstance(prop1, PropertyExprMixin):
class PropertyExprMixin:
pass
class PropertyExpr:
def __new__(cls, tp, value, *args, **kwargs):
return type("PropertyExprMixin", (tp,PropertyExprMixin), kwargs)(value)
prop1 = PropertyExpr(int, 5, expr="expr int")
print(prop1, " -> ", prop1.expr, type(prop1), isinstance(prop1, int), isinstance(prop1, PropertyExprMixin))
prop2 = PropertyExpr(list, "5", expr="expr list")
print(prop2, " -> ", prop2.expr, type(prop2), isinstance(prop2, list), isinstance(prop2, PropertyExprMixin))
prop3 = PropertyExpr(str, "abc", expr="expr string")
print(prop3, " -> ", prop3.expr, type(prop3), isinstance(prop3, str), isinstance(prop3, PropertyExprMixin))
Output:
5 -> expr int <class '__main__.PropertyExprMixin'> True True
['5'] -> expr list <class '__main__.PropertyExprMixin'> True True
abc -> expr string <class '__main__.PropertyExprMixin'> True True
prop1.expr? Sinceprop1turns intoprop1.value, that's equivalent toprop1.value.expr__str__does not get called when you use justprop1... You have a fundamental misunderstanding of how that works. In any case, what you are asking for isn't possible in Python, and shouldn't ever be necessary (or I would argue, useful)__str__gets called if you write something likeprint(prop1). You certainly could use that method to make this do what you want there, but not for more general references toprop1.print(prop1 + 1), which would complain about trying to concatenate a string with an integer.