Reputation: 3
I have a class like this:
Class PropertyExpr:
def __init__(self, value, expr):
self.value = value
self.expr = expr
Let's say I initialize the class like this:
prop1 = PropertyExpr(5, "test")
I understand that if I want to get access to value
or expr
I would call prop1.value
or prop1.expr
.
However, I want to make it so that whenever I call prop1
, it will automatically return prop1.value
. prop1.expr
would still get access to the expr
variable
Since value
can be any datatype, I don't think using __str__
would work.
Thank you for any help!
EDIT: This was my current approach, but it only work for int, and I need to extend this so that it work for other data type as well, such as list:
class PropertyExpr(int):
def __new__(self, value: int, expr: Optional[str]=None, *args, **kwargs):
return int.__new__(self, value, *args, **kwargs)
def __init__(self, value: int, expr: Optional[str]=None, *args, **kwargs):
int.__init__(value, *args, **kwargs)
if expr is None:
self.expr = str(value)
else:
self.expr = expr
So when I create an instance:
prop1 = PropertyExpr(5, "test")
So when I use prop1
, it would return 5, and prop1.expr = "test"
.
Upvotes: 0
Views: 193
Reputation: 10782
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
Upvotes: 1
Reputation: 8437
You can just do this:
class PropertyExpr(int):
def __new__(cls, value, expr):
obj = super().__new__(cls, value)
obj.expr = expr
return obj
var = PropertyExpr(5, "test")
print(var)
print(var.expr)
Output:
$ python3 so.py
5
test
Upvotes: 0