Kenji
Kenji

Reputation: 3

Access instance variable by using instance name

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

Answers (2)

Mike Scotty
Mike Scotty

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

DevLounge
DevLounge

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

Related Questions