0

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".

4
  • If you did that, how would you access prop1.expr? Since prop1 turns into prop1.value, that's equivalent to prop1.value.expr Commented Apr 19, 2021 at 22:42
  • Note, __str__ does not get called when you use just prop1... 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) Commented Apr 19, 2021 at 22:46
  • __str__ gets called if you write something like print(prop1). You certainly could use that method to make this do what you want there, but not for more general references to prop1. Commented Apr 19, 2021 at 22:50
  • E.g. it wouldn't work for print(prop1 + 1), which would complain about trying to concatenate a string with an integer. Commented Apr 19, 2021 at 22:50

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

9 Comments

I don't think that's what he means, since he also wrote I would call prop1.value or prop1.expr. He's using "call" to mean "write".
they almost certainly mean "just access" unfortunately.
I don't think call() is what I'm looking for. I edited the post and your second approach looks somewhat similar to what I was using. I subclass the int class but that approach only work for int
@Kenji Please have a look at the updated answer.
@Mike very interesting, do you might explain why I should not use the third approach?
|
0

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

2 Comments

This is almost the same way as my current approach, but this only work if value is int. I need to have support for other types as well, such as list.
You cannot create a reusable class as the attribute can only be added at creation time from what I’ve tried.

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.