0

I'm having a hell of a hard time trying to figure out why my print(Node(3).value) is printing 0. Any ideas?

When I run this code, I get

First: 6 Value: 8 Children: [9]
First: 8 Value: 23 Children: [9]
First: 3 Value: 3 Children: [5]
First: 6 Value: 8 Children: [7]
First: 4 Value: 20 Children: [8]
First: 1 Value: 17 Children: [8]
First: 8 Value: 23 Children: [10]
First: 5 Value: 11 Children: [8]
First: 1 Value: 17 Children: [2]
0

Any ideas? I know I'm not saving the Nodes anywhere but my mind can't wrap around it. Any help would be appreciated, thanks!

# Complete the primeQuery function below.
n = 10
first = [6, 8, 3, 6, 4, 1, 8, 5, 1]
second = [9, 9, 5, 7, 8, 8, 10, 8, 2]
values = [17, 29, 3, 20, 11, 8, 3, 23, 5, 15]
queries = [1, 8, 9, 6, 4, 3]


class Node(object):
    def __init__(self, data, value=0):
        self.data = data
        self.value = value
        self.children = []

    def addChild(self, child):
        self.children.append(child)

    def setValue(self, givenV):
        self.value = givenV


def primeQuery(n, first, second, values, queries):
    i = 0
    while i < n - 1:
        f = Node(first[i], values[first[i] - 1])

        s = Node(second[i], values[second[i] - 1])

        f.addChild(s.data)

        print(f"First: {f.data} Value: {f.value} Children: {f.children}")

        i += 1

    print(Node(3).value)


primeQuery(n, first, second, values, queries)
Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50

1 Answers1

0

Statement

Node(3).value

creates Node object with data set to 3 & value set to 0 (default argument), so when you're getting its value it returns 0.

If you want to create Node objects and then re-use them -- you should store them in some sort of container.

Assuming that

  • data field is a hashable unique identifier of Node,
  • if Node has been created and we're passing different value -- old value persists

I see at least 2 approaches here:

  • Create container on the top level, explicitly add Nodes to it upon creation and access them using it afterwards like

    def primeQuery(n, first, second, values, queries):
        i = 0
        # top-level container of ``Node``s
        nodes = {}
    
        def get_or_create_node(data, value):
            try:
                # trying to access already created ``Node``
                result = nodes[data]
            except KeyError:
                # no ``Node`` found, create it...
                result = Node(data, value)
                # ... and register
                nodes[data] = result
            return result
    
        while i < n - 1:
            f = get_or_create_node(first[i], values[first[i] - 1])
            s = get_or_create_node(second[i], values[second[i] - 1])
    
            f.addChild(s.data)
    
            print(f"First: {f.data} Value: {f.value} Children: {f.children}")
    
            i += 1
    
        print(nodes[3].value)
        # we can return ``nodes`` here if we want to use them outside of given function
    

    gives us

    First: 6 Value: 8 Children: [9]
    First: 8 Value: 23 Children: [9]
    First: 3 Value: 3 Children: [5]
    First: 6 Value: 8 Children: [9, 7]
    First: 4 Value: 20 Children: [8]
    First: 1 Value: 17 Children: [8]
    First: 8 Value: 23 Children: [9, 10]
    First: 5 Value: 11 Children: [8]
    First: 1 Value: 17 Children: [8, 2]
    3
    
  • Extension of previous approach: restrict Nodes creation using global function which caches already created ones:

    # in module namespace
    def get_or_create_node(data, value=0,
                           *,
                           # hack, see **note**
                           cache={}):
        # we've moved class definition inside of a function to restrict its instances creation
        class Node(object):
            def __init__(self, data, value):
                self.data = data
                self.value = value
                self.children = []
    
            def addChild(self, child):
                self.children.append(child)
    
            def setValue(self, givenV):
                self.value = givenV
    
        try:
            result = cache[data]
        except KeyError:
            result = Node(data, value)
            cache[data] = result
        return result
    

    (note: here we're using "hack" with mutable default argument, more info may be found in this thread)

    Then everywhere when we need to create/access Node object -- we should use this function only like

    def primeQuery(n, first, second, values, queries):
        i = 0
        while i < n - 1:
            f = get_or_create_node(first[i], values[first[i] - 1])
            s = get_or_create_node(second[i], values[second[i] - 1])
    
            f.addChild(s.data)
    
            print(f"First: {f.data} Value: {f.value} Children: {f.children}")
    
            i += 1
    
        print(get_or_create_node(3).value)
    

    this will give us the same output as before.

Further improvements

changing value

If our assumption about value being persistent turns out to be wrong -- we can simply add changing of value if it was passed like:

  • in first approach:

    def get_or_create_node(data, value):
        try:
            # trying to access already created ``Node``
            result = nodes[data]
        except KeyError:
            # no ``Node`` found, create it...
            result = Node(data, value)
            # ... and register
            nodes[data] = result
        # adding ``else``-clause
        else:
            result.setValue(value)
        return result
    
  • in second approach: set value parameter's default value to None and check if it was specified like

    def get_or_create_node(data, value=None,
                           *,
                           cache={}):
        class Node(object):
            def __init__(self, data, value):
                self.data = data
                self.value = value
                self.children = []
    
            def addChild(self, child):
                self.children.append(child)
    
            def setValue(self, givenV):
                self.value = givenV
    
        try:
            result = cache[data]
        except KeyError:
            if value is None:
                # default value
                value = 0
            result = Node(data, value)
            cache[data] = result
        else:
            if value is not None:
                result.setValue(value)
        return result
    

inheriting from object

If we're working in Python 3 -- there is no need to specify object as base class, it will be set by default (see this thread), so instead of

class Node(object):
    ...

we can simply write

class Node:
    ...

use for-loop instead of while

We don't need to use while-loop & increment i by hand when we have for-loop and range objects, so instead of

i = 0
while i < n - 1:
    ...
    i += 1

we can write

for i in range(n - 1):
    ...

also I think here is a typo, because we're skipping case with i equal to n - 1, so we can fix it:

for i in range(n):
    ...

finally we can see that there is no need in i index when we can use zip built-in & iterate over pairs of first & second lists elements like

for first_data, second_data in zip(first, second):
    f = get_or_create_node(first_data, values[first_data - 1])
    s = get_or_create_node(second_data, values[second_data - 1])

    f.addChild(s.data)

    print(f"First: {f.data} Value: {f.value} Children: {f.children}")
Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50