0

I need to find all the classes and the relative value or string for a given object " EXAMPLE_1".

I tried with vars(obJ) but the result is not what I want since I need to find the name of the classes, then if it is "Operation" find again all the classes, if it's "Value" take the value inside it.

How can I loop inside the Object until the last argument is "Value"?

class Expression:
pass

class Operation(Expression):
    def __init__(self, op, arg1, arg2):
         self.op = op               # a string; always one of '+' '-' '*' '/'
         self.arg1 = arg1       # an Expression
         self.arg2 = arg2       # an Expression

class Value(Expression):
     def __init__(self, value):
     self.value = value # an integer; always positive

EXAMPLE_1 = Operation('*',Value(2),Operation('+',Value(5),Value(7)))

When I run the code

print(vars(EXAMPLE_1)) 
>>> {'op': '*', 'arg1': <__main__.Value object at 0x00000263B0D381D0>, 'arg2': <__main__.Operation object at 0x00000263B0D385F8>}
azro
  • 53,056
  • 7
  • 34
  • 70
Alex
  • 93
  • 1
  • 1
  • 6
  • You want to print or compute ? – azro Dec 14 '19 at 14:19
  • What result *do* you want? `Operation` is effectively a tree of expressions; you probably want a method that will walk that tree in a particular order. – chepner Dec 14 '19 at 14:19
  • The printing was an example, I need to compute since as @chepner wrote, I need to walk the tree in order to analyze each part of the class. – Alex Dec 14 '19 at 14:42
  • @Reznik, thanks but I cannot change the classes already defined. – Alex Dec 14 '19 at 14:46

2 Answers2

1

Read about the __repr__ function, there is a good explnation here

class Expression:
    pass

class Operation(Expression):
    def __init__(self, op, arg1, arg2):
         self.op = op               # a string; always one of '+' '-' '*' '/'
         self.arg1 = arg1       # an Expression
         self.arg2 = arg2       # an Expression

    def __repr__(self):
        return str(self.arg1) + self.op + str(self.arg2)

class Value(Expression):
     def __init__(self, value):
         self.value = value # an integer; always positive

    def __repr__(self):
         return str(self.value)
Reznik
  • 2,663
  • 1
  • 11
  • 31
0

You can write a simple function that will iterate over the tree, producing operator symbols for Operation instances and values for Value instances.

def in_order_walk(expr):
    if isinstance(expr, Operation):
        yield from in_order_walk(expr.arg1)
        yield expr.op
        yield from in_order_walk(expr.arg2)
    elif isinstance(expr, Value):
        yield expr.value

An example of its use:

>>> list(in_order_walk(EXAMPLE_1))
[2, '*', 5, '+', 7]

However, in_order_walk would better be written as a method of the Expression class, to be overriden by subclasses, to avoid the need for explicit type sniffing.

class Expression:
    def in_order_walk(self):
        pass

class Operation(Expression):
    def __init__(self, op, arg1, arg2):
         self.op = op               # a string; always one of '+' '-' '*' '/'
         self.arg1 = arg1       # an Expression
         self.arg2 = arg2       # an Expression

    def in_order_walk(self):
        yield from self.arg1.in_order_walk()
        yield self.op
        yield from self.arg2.in_order_walk()

class Value(Expression):
     def __init__(self, value):
         self.value = value # an integer; always positive

     def in_order_walk(self):
         yield self.value

Then

>>> list(EXAMPLE_1.in_order_walk())
[2, '*', 5, '+', 7]

This approach is probably simpler as you expand your hierarchy. For instance, to handle operator precedence more cleanly, you would probably have separate classes for + and *.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thanks! This works as I wanted! I just changed the yields [ op,arg1,arg2] in order to keep the original order ( prefix notation ) of the expression since I already wrote the function to convert from prefix to postfix/infix notation. – Alex Dec 14 '19 at 15:38
  • You can easily change `in_order_walk` to `pre_order_walk` by swapping the order of `yield from in_order_walk(expr.arg1)` and `yield expr.op`. – chepner Dec 14 '19 at 15:40