attribute assignment effects all class instances

A

anon

I'm aware that you can assign a value to an attribute in all class
instances by assigning to <Class>.<attribute>, however, my case is
slightly different and bizarre.

In module node:

top, left, mid, right = range(4)

class Node:

def __init__(self, ntype = None, function = None, symbol = None,
meta = None, adjacent = [None, None, None, None]):
self.type = ntype
self.function = function
self.symbol = symbol
self.meta = meta
self.adjacent = adjacent


Here's the wierd thing, in another module I have:

node.adjacent
= y

where node is an instance of the Node class. This statement assigns
all Node instances the value y to attribute adjacent
. This seems
very wrong, unless python resolves names in a case-insensitive way.

When I try the alternate:

node.adjacent = [None, y, None, None]

I get the behavior I expect (ie. only the instsance node gets its
attribute set)

Can anyone shed any light on this?

Thanks.
--Greg​
 
J

Jeffrey Froman

anon said:
Here's the wierd thing, in another module I have:

node.adjacent
= y

where node is an instance of the Node class.  This statement assigns
all Node instances the value y to attribute adjacent
.  This seems
very wrong, unless python resolves names in a case-insensitive way.​


When you assign the default value for adjacent in the constructor definition
as you do, that list is created only one time, and the same list is passed
as the default value for each instance of Node.

Thus, when you alter the list, the change shows up in all instances, because
they are all using the same list.
When I try the alternate:

node.adjacent = [None, y, None, None]

I get the behavior I expect (ie. only the instance node gets its
attribute set)

In the second case you have not altered the existing list, but rather
reassigned your attribute to a new list. Hence, only the node you are
working on changes. At that point all of the nodes *except* the one you
just changed are using the same list for the "adjacent" attribute.


Hope that helps,
Jeffrey​
 
P

Peter Hansen

anon said:
I'm aware that you can assign a value to an attribute in all class
instances by assigning to <Class>.<attribute>, however, my case is
slightly different and bizarre.

Jeffrey Froman gave you a correct answer already, but I wanted
to point out a problem with the above, which might indicate
another area where you have a misunderstanding.

What you say above is technically wrong, so I hope it was just
a poor choice of wording. Assigning to Class.attribute as you
describe absolutely does NOT "assign a value to an attribute in
all class instances". What it actually does is it "assigns a
value to an attribute in the class", and that attribute just
happens to be *visible* to all instances. They do not actually
have that attribute themselves, however.

I put the "assigns a value" part in quotation marks as well,
because in Python it is more accurate to talk about "binding
names" than assigning values. Thus you should really say that
you can bind a name in the class and it will be visible to
all instances, but the instances themselves do not have that
name bound...

Hope that helps clarify things, or at least I hope it helps
you in asking subsequent questions. :)

-Peter
 
J

Jeff Shannon

And since others have already told you *why* you're having a problem,
I'll limit myself to showing you one possible way around that problem.

class Node:
def __init__(self, ... , adjacent = None):
# ....
if adjacent is None:
adjacent = [None, None, None, None]
self.adjacent = adjacent

This way, if you don't specify adjacent, you create a *new* list of Nones, which thus won't be shared by other instances of Node. If you *do* specify adjacent, then you get what you specified (and it's up to you to make sure that it's not shared with any other Nodes).

Jeff Shannon
Technician/Programmer
Credit International
 
D

Dennis Lee Bieber

In module node:

class Node:



Here's the wierd thing, in another module I have:

node.adjacent
= y

where node is an instance of the Node class. This statement assigns​


I'd recommend some name changes somewhere... Too many "nodes"
here...

Your "another module" had to have done an "import node" (since
that is the name you claim at the top). You then had to perform
something like: "node = node.Node()" to create the instance, but that
would also supersede the module name node...

--​
 
A

Andrew Durdin

And since others have already told you *why* you're having a problem,
I'll limit myself to showing you one possible way around that problem.

And for another way around: just change the line in __init__ from
self.adjacent = adjacent
To
self.adjacent = list(adjacent)

This will alter the overall behaviour -- the new node will always make
a copy of the list. An example is best to show the difference. Suppose
you have the following code:

adj = [node1, node2, node3, node4]
newnode1 = node(adjacent=adj)
newnode2 = node(adjacent=adj)
.... # Some other irrelevant code
newnode2.adjacent
= node5

With Jeff's solution (and your original), newnode1.adjacent will also
have changed, so will be [node1, node5, node3, node4]. With the
solution above which copies the list, newnode1 will not have changed.

The solution you should use depends on what behaviour you want for
this situation.​
 
A

anon

Dennis Lee said:
In module node:

class Node:



Here's the wierd thing, in another module I have:

node.adjacent
= y

where node is an instance of the Node class. This statement assigns​


I'd recommend some name changes somewhere... Too many "nodes"
here...

Your "another module" had to have done an "import node" (since
that is the name you claim at the top). You then had to perform
something like: "node = node.Node()" to create the instance, but that
would also supersede the module name node...​


Dennis--
I actually imported the module as "from node import *", so I haven't
run into a name conflict (apparently). Though I see your point for
naming things differently.

Peter--
Sorry for my miswording of assignment to shared class data.

Everyone--
Thank you very much for your assistance. I was unaware that the
default arguments were created only once and then reused for each
instance. The situation makes complete sense now.

--Greg​
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top