91

I'm trying to simplify one of my homework problems and make the code a little better. What I'm working with is a binary search tree. Right now I have a function in my Tree() class that finds all the elements and puts them into a list.

tree = Tree()
#insert a bunch of items into tree

then I use my makeList() function to take all the nodes from the tree and puts them in a list. To call the makeList() function, I do tree.makeList(tree.root). To me this seems a little repetitive. I'm already calling the tree object with tree.so the tree.root is just a waste of a little typing.

Right now the makeList function is:

    def makeList(self, aNode):
        if aNode is None:
            return []
        return [aNode.data] + self.makeList(aNode.lChild) + self.makeList(aNode.rChild)

I would like to make the aNode input a default parameter such as aNode = self.root (which does not work) that way I could run the function with this, tree.makeList().

First question is, why doesn't that work?
Second question is, is there a way that it can work? As you can see the makeList() function is recursive so I cannot define anything at the beginning of the function or I get an infinite loop.

EDIT Here is all the code as requested:

class Node(object):
    def __init__(self, data):
        self.data = data
        self.lChild = None
        self.rChild = None

class Tree(object):
    def __init__(self):
        self.root = None

    def __str__(self):
        current = self.root

    def isEmpty(self):
        if self.root == None:
            return True
        else:
            return False

    def insert (self, item):
        newNode = Node (item)
        current = self.root
        parent = self.root

        if self.root == None:
            self.root = newNode
        else:
            while current != None:
                parent = current
                if item < current.data:
                    current = current.lChild
                else:
                    current = current.rChild

            if item < parent.data:
                parent.lChild = newNode
            else:
                parent.rChild = newNode

    def inOrder(self, aNode):
        if aNode != None:
            self.inOrder(aNode.lChild)
            print aNode.data
            self.inOrder(aNode.rChild)

    def makeList(self, aNode):
        if aNode is None:
            return []
        return [aNode.data] + self.makeList(aNode.lChild) + self.makeList(aNode.rChild)


    def isSimilar(self, n, m):
        nList = self.makeList(n.root)
        mList = self.makeList(m.root) 
        print mList == nList 
Alec
  • 8,529
  • 8
  • 37
  • 63
chrisheinze
  • 1,129
  • 2
  • 9
  • 14
  • 1
    What do you want with 'self' within a module level method? This makes absolutely no seense. If makeList2() is a method of class then please provide correct code and not snippets without context. –  Apr 05 '11 at 16:53
  • makeList2() was suppose to be makeList(), I edited it – chrisheinze Apr 05 '11 at 18:06
  • How does this make no sense? I'm trying to use my makeList() function simpler by using a default parameter for the root of the tree instead of having to call it. – chrisheinze Apr 05 '11 at 18:40
  • 1
    I agree with @crh878 that this makes sense. I tried it myself, also for creating a binary search tree. No joke... – william_grisaitis Apr 24 '14 at 01:54

3 Answers3

65

larsmans answered your first question

For your second question, can you simply look before you leap to avoid recursion?

def makeList(self, aNode=None):
    if aNode is None:
        aNode = self.root
    treeaslist = [aNode.data]
    if aNode.lChild:
        treeaslist.extend(self.makeList(aNode.lChild))
    if aNode.rChild:
        treeaslist.extend(self.makeList(aNode.rChild))
    return treeaslist
Community
  • 1
  • 1
nofinator
  • 2,906
  • 21
  • 25
62

It doesn't work because default arguments are evaluated at function definition time, not at call time:

def f(lst = []):
    lst.append(1)
    return lst

print(f()) # prints [1]
print(f()) # prints [1, 1]

The common strategy is to use a None default parameter. If None is a valid value, use a singleton sentinel:

NOTHING = object()

def f(arg = NOTHING):
    if arg is NOTHING:
        # no argument
    # etc.
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 1
    You can omit the `Sentinel`. Just use `NOTHING = object()`. Guaranteed to yield a unique singleton you can check for via `is`. –  Apr 05 '11 at 17:02
  • I'd strongly advise against making `foo(1, None)` and `foo(1)` do different things. – Glenn Maynard Apr 05 '11 at 17:13
  • @Glenn: with a one-arg function that must deal with arbitrary objects, it can make sense sometimes. – Fred Foo Apr 05 '11 at 17:15
  • Ok, I understand why it doesn't work and also how your first code segment continually appends whatever value you add but I'm not fully following what's going on in the second code segment. – chrisheinze Apr 05 '11 at 18:43
  • @crh878: the point about using `None` is the most important. The second part constructs an object `NOTHING`, uses that as the default argument and checks whether that exact object was passed in. If you hide `NOTHING` (don't export it from your module), it cannot be passed in by client code. `is` compares objects by memory address. – Fred Foo Apr 05 '11 at 21:18
  • @larsmans ok I'm getting it. So if you something like a list, Python will continue to add on to that list even after it is done running that function, thus the function is useless if it is ran more than once? But if you assign the value to None and then change it, the function is reusable? – chrisheinze Apr 06 '11 at 15:21
  • @chr878: you've got it. But actually, the `list` version may be useful sometimes, if you remember this behavior and document it well; it's very like introducing a global variable. I've seen libraries do this. But the `None` version is far more common. – Fred Foo Apr 06 '11 at 20:56
  • Why we should use this instead of None? – Charlestone Feb 27 '17 at 08:20
  • @Charlestone in case you need to use None to mean something else. – Ben Farmer Aug 30 '22 at 23:15
2

If you want to treat None as a valid argument, you could use a **kwarg parameter.

def function(arg1, arg2, **kwargs):
    kwargs.setdefault('arg3', default)
    arg3 = kwargs['arg3']
    
    # Continue with function

function("amazing", "fantastic") # uses default
function("foo", "bar", arg3=None) # Not default, but None
function("hello", "world", arg3="!!!")

I have also seen ... or some other singleton be used like this.

def function(arg1, arg2=...):
    if arg2 is ...:
        arg2 = default
killjoy
  • 3,665
  • 1
  • 19
  • 16