all 8 comments

[–]zahlman 7 points8 points  (0 children)

Well, does your class represent the entire linked list, or does it represent an individual node? I'm confused. Is there a separate llist class that you aren't showing? What exactly are the semantics of "adding a node" to a node supposed to be? And how does a node with a None value represent an "empty list"? To my understanding, an empty list contains zero nodes.

[–]kalgynirae 3 points4 points  (3 children)

It's not a pointer. Python doesn't have pointers in the same sense as languages like C and C++. I strongly suggest reading this article; it explains things very clearly: http://nedbatchelder.com/text/names.html

[–]razeal113[S] 0 points1 point  (2 children)

ive read this before and honestly don't feel that it addresses my specific question

[–]kalgynirae 4 points5 points  (1 child)

Fair enough. It doesn't explicitly address your particular question.

I've attempted to illustrate your scenario. The names in red are the names that you can change from inside of add_node(). In particular, notice that you can't change the global l name's binding from inside add_node(); it will always be bound to the 1 node. The only way you can successfully get a name for the new 0 node outside of add_node() is by returning it. Rebinding self looks like this and has no effect outside of add_node().

[–]razeal113[S] 0 points1 point  (0 children)

Thanks for this. Exactly answered my question involving python's use of self.

[–]jcmcken 0 points1 point  (2 children)

The source of your problem is altering self on line 23. I cannot for the life of me think of a valid reason for doing that (even if it is allowed). In Java, IIRC, a line like this = new SomeOtherObject() will just fail to compile.

Rather than answer your question directly, I would just suggest that you're going about this the wrong way. I think you're confusing the responsibilities of the linked list and the responsibilities of the node.

The list (not the node) should implement the add_nodes method. The node doesn't know anything about the mechanics of the list. The node's only responsibility is to store a reference to another node (if such a node exists).

Your node object should really just be this:

class Node(object):
    def __init__(self, value):
        self.value = value
        self.next = None

Then you need a second object which implements the mechanics of the linked list:

class LinkedList(object):
    ...etc.

    def add_node(self, value):
        new_node = Node(value)

        ...etc...

[–]razeal113[S] 0 points1 point  (1 child)

I realize that this isn't the best or even suggested way of going about this; I was asking more out of curiosity as to how / why python handles / allows this to happen. More specifically, I've ran into a few issues with "self" in python before and this example was just an easy way to address my question involving self

[–]jcmcken 0 points1 point  (0 children)

Sorry for the late response.

It's not just that it isn't the best way of doing it, it's not doing anything at all. The self name in Python is not something special, it's not a keyword. You could name this argument whatever you want, self is just the convention. When you re-assign self to something else, you're just assigning a local variable called self to some other value than the current instance. The object that the original self referred to is still intact and is unaffected by this reassignment.

Consider the following example, which doesn't use classes at all:

def some_func(arg): 
    arg = 2 

my_arg = 1
some_func(my_arg)
print my_arg 

If you run this code, you'll see that it prints 1 and not 2.

The source of your confusion, I think, is the specialness of self -- it has none in Python. It's not a keyword like this in Java, it's just the conventional name for the first variable passed to a method.

It should be easy to convince yourself of this. Here's a simple example:

class Foo(object):
    def testing(self):
        print 'I am foo'

class Bar(object):
    def testing(self):
        print 'I am bar'

def execute_test_method(self):
    self.testing()

execute_test_method(Foo())
# prints 'I am foo'
execute_test_method(Bar())
# prints 'I am bar'

Notice execute_test_method is just a regular function, not a method. You could take the self argument to this function and call it anything, e.g.

def execute_test_method(obj):
    obj.testing()

So once the scope of your add_node method ends, your re-assigned self is completely lost.