简体   繁体   中英

Recursive function calls inside class seem to return multiple 'self' attributes?

I am trying to create a piece of code that can print out a linked list in different ways depending on how its called and from where in the list you choose to start.

To do this I have chosen a recursive function that you pass data to in the initial call, and it passes that data and more through its recursive calls in order to build up the desired output form. However when I try and recursively call the function, the 'self' argument appears to send twice, writing itself into the first two arguments in the function.

class LinkedList(object):

def __init__(self):
    self.head = Node()
    self.allNodeLocations = {}

def buildLinkedList(self, curNode, finish=99999, between=False, location=[0, 0], allNodeLocations={}, i=0):
    def __init__(self, name):
        self.curNode = curNode
        self.finish = finish
        self.between = between
        self.location = location
        self.allNodeLocations = allNodeLocations
        self.i = i

    if len(curNode.getChildren()) > 1:
            for child in curNode.getChildren():
                location, allNodeLocations, i = self.buildLinkedList(self, child, finish, between, 
                         location, allNodeLocations, i) #THIS IS WHERE THE CODE FAILS
            location[0] -= 1
            i -= 1
    return location, allNodeLocations, i

In the above case, the 'child' variable should be a Node object. Running the above script gives "TypeError: buildLinkedList() takes from 2 to 7 positional arguments but 8 were given"

If instead I call the function with the line

    self.buildLinkedList(self, child)

And check the values with pycharm debugger, both self, and the curNode (ie the child variable) both point to the same LinkedList object and the next variable in the function definition heading 'finish' contains the node object that child passed to the function that should instead be input under the curNode variable.

Please note I have removed a large amount of my code to make it easier to read here, the remainder of the code is below if need be. I apologise that there is currently a large amount of useless code, I intend on doing things with it in the future...

class Node(object):

    def __init__(self, busNum = None, data=None, children=None, parents=None):
        self.busNum = busNum
        self.data = data
        self.children = []
        self.parents = []
        if children:
            self.children += children
        if parents:
            self.parents += parents

    def getChildren(self):
        return self.children

    def getParents(self):
        return self.parents

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

    def setParent(self, parent):
        self.children.append(parent)

    def getData(self):
        return self.data

    def setData(self, data):
        self.data = data

    def toString(self):
        return "Bus Number: " + str(self.busNum) + ", Data: " + str(self.data)


class LinkedList(object):

    def __init__(self):
        self.head = Node()
        self.allNodeLocations = {}
        # self.curLocation = [0, 0]

    def addBus(self, bus, data, children, parents):
        newNode = Node(bus, data, children, parents)

    def buildLinkedList(self, curNode, finish=99999, between=False, location=[0, 0], allNodeLocations={}, i=0):
        def __init__(self, name):
            self.curNode = curNode
            self.finish = finish
            self.between = between
            self.location = location
            self.allNodeLocations = allNodeLocations
            self.i = i

        allNodeLocations[curNode] = location
        if i >= finish:
            return allNodeLocations

        if len(curNode.getChildren()) == 0:
                # If there are no children, set the location of the current bus, and go down a y level
            # location[0] -= 1
            location[1] += 1
            # i -= 1
            # curNode = prevNode
            return location, allNodeLocations, i
        elif len(curNode.getChildren()) == 1:
                # If there is 1 child, set its location and move onto its next child, once we return from there,
                # subtract 1 from the current iteration and x location
            location[0] =+ 1
            i =+ 1
            if i > finish:
                i -= 1
                return location, allNodeLocations, i
            # prevNode = curNode
            curNode = curNode.getChildren()[0]
            location, allNodeLocations, i = self.buildLinkedList(self, curNode, finish, between, location, allNodeLocations, i)
            location[0] -= 1
            i -= 1
            return location, allNodeLocations, i
        elif len(curNode.getChildren()) > 1:
                i += 1
                location[0] += 1
                for child in curNode.getChildren():
                    location, allNodeLocations, i = self.buildLinkedList(self, child, between=False)#, finish, between, location, allNodeLocations, i)
                location[0] -= 1
                i -= 1
        return allNodeLocations

e=Node(4,11)
f=Node(5,11)
g=Node(6,11)
h=Node(8,11)
j=Node(10,11)
k=Node(11,11)
i=Node(7,33,[h])
d=Node(9,66,[j,k])
c=Node(3,66,[i])
b=Node(2,132,[e,f,g])
a=Node(1,66,[b,c])

listOfNodes = [a,b,c,d,e,f,g,h,i,j,k]

print(d.getChildren())
# d.setChild(11)
# g.setChild(9)
# i.setParent(4)
# print(d.getChildren())

linkd=LinkedList()
linkd.buildLinkedList(a)

--Update-- The issue was pointed out by user Moxy, the fix was to remove the call to self within the buildLinkedList call.

As an aside, when I run the script, it creates a dictionary with the 'location' variable, in each iteration, however, whenever the location variable updates, it updates all instances of it in the dictionary.

To clarify, I make the dictionary recursively with the line "allNodeLocations[curNode] = location", with the curNode and location variables being updated on each recursive call, however when location changes, it immediately updates the dictionary to be the same value throughout each cell in the dictionary.

Is there some way to just get the value of the location variable instead the pointer (if thats even what the issue is)?

When you call buildLinkedList remove the self . It represents the instance of the class so you add it when you create the function inside your class but never when you call it. If you add the self , it creates an offset in the parameters of buildLinkedList .

Try this instead:

self.buildLinkedList(curNode, finish, between, location, allNodeLocations, i)

After fixing this, you will have a

ValueError: too many values to unpack (expected 3)

To fix it replace the last return of the buildLinkedList by something like:

return None, allNodeLocations, None

--Update--

This issue of update is because the list are passed by reference and not value, to fix it try:

allNodeLocations[curNode] = location by allNodeLocations[curNode] = list(location)

or

allNodeLocations[curNode] = location by allNodeLocations[curNode] = location[:]

Btw, I don't understand what you mean by getting the value of location instead of the pointer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM