Newbie: question regarding references and class relationships

R

Rui Maciel

Dave said:
So why do you also have an instance attribute of the same name?

Thanks to this thread, and after a bit of reading, I've finally managed to
discover that in Python there are class attributes and instance attributes,
the former working similarly to C++'s static member variables and the latter
being more like proper member variables.

And there was light.

Python.org's tutorial could cover this issue a bit better than it does.


Thanks for the help,
Rui Maciel
 
T

Terry Jan Reedy

I don't doubt that there might good reasons for that, but it is always
preferable to get the rationale behind a decision to be able to understand
how things work and how to do things properly.

I agree actually. But sometimes is it hard to articulate 'good reasons'
for a principle based on the integration of over a decade of experience.
I was really trying to point to the difference between

'I will not accept the experience-based advice until enough good reasons
are presented.' and

'I will provisionally accept the advice but I would still like to know why.'

Another principle similar to 'Don't add extraneous code' is 'Don't
rebind builtins*'. I have a separate post for that.

Terry
 
G

Grant Edwards

Another principle similar to 'Don't add extraneous code' is 'Don't
rebind builtins'.

OK, we've all done it by accident (especially when starting out), but
are there people that rebind builtins intentionally?
 
C

Chris Angelico

OK, we've all done it by accident (especially when starting out), but
are there people that rebind builtins intentionally?

There are times when you don't care what you shadow, like using id for
a database ID.

ChrisA
 
T

Tim Chase

There are times when you don't care what you shadow, like using id
for a database ID.

While that's certainly the most common, there are a lot of items in
__builtins__ that are there for convenience that I wouldn't think
twice about rebinding, especially in a local scope (I might take more
care at a module scope, but still wouldn't care much). E.g.

apply
bin
buffer
coerce
filter
format
input

Some are deprecated, some are easily replaced by (what I consider
more readible) list comprehensions, and some have alternate meanings
that might be the right word-choice inside a function and wouldn't be
missed (I'd expect to see a bin-sort function use variables called
"bin" and not needing to convert from integer-to-string)

And some strings that are harmless outside the interactive interpreter

copyright
credits
exit
license
quit

-tkc
 
D

Dave Angel

There are times when you don't care what you shadow, like using id for
a database ID.

ChrisA

And times where you're deliberately replacing a built-in

try:
input = raw_input
except ....
 
J

Jason Swails

And times where you're deliberately replacing a built-in

try:
input = raw_input
except ....


Yes but this is a hack to coerce Python2/3 compatibility. You're no doubt
correct that it's intentional rebinding with a definite aim, but if the
PyArchitects had their way, this would be unnecessary (and discouraged) as
well.

The first time I remember rebinding a builtin was completely accidental
(and at the very beginning of me learning and using Python).

# beginner's crappy code
range = [0, 20]

# bunches of code

for i in range(len(data)):
if data > range[0] and data < range[1]:
do_something

TypeError: 'list' object is not callable... # what the heck does this mean??


That one drove me nuts. Took me hours to find. I still avoid rebinding
builtins just from the memory of the experience :)

--Jason
 
R

Rick Johnson

[...]

<code>
class Point:
position = []
def __init__(self, x, y, z = 0):
self.position = [x, y, z]

Firstly. Why would you define a Point object that holds it's x,y,z values in a list attribute? Why not store them as self.x, self.y and self.z? But ifyou are going to store them as a list, then why not extend a python list? I hate to see a "listy" object that only holds a list. It begs the question: Why not make it a *real* list? (Although i believe the former approach ismore desirable for this problem)

Secondly, why would store the position of the Point as a class attribute? Do you realize that EVERY instance of the Point object will share the same x,y, and z values if you do it that way? (considering you query the correct variable *evil grin*)

Actually, you have a legitimate excuse: you were fooled because in your "object definition" you declared a "class level variable" named "position". THEN in your "__init__" method you declared an "instance level variable" named "position".

Then, when you assigned the x,y,z values to "self.position" you thought youwhere assigning them to the "class level variable" named "position", HaHa,but then again you had NO idea that "class level variables" and "instance level variables" even existed! (or, at least, how to properly declare them)

Q: Yes Rick but how do i solve this issue?

By changing the names we can inspect the Point object and understand how class level and instance level variables work in Python. Observe:

## START SESSION ##
py> class Foo(object):
.... classVar = []
.... def __init__(self, arg):
.... self.instanceVar = arg
....
py> Foo.classVar
[]
py> Foo.classVar = "apple"
py> Foo.classVar
apple
py> foo1 = Foo("pear")
py> foo1.classVar
apple
py> foo1.instanceVar
pear
py> foo2 = Foo("peach")
py> foo2.classVar
apple
py> foo2.instanceVar
peach
## END SESSION ##

As you can see the "class level variable" is known to all instances of the object, and a change in one is equal to a change in all. Whereas the "instance level variables" are unique to each instance.
class Line:
points = ()
def __init__(self, p_i, p_f):
self.points = (p_i, p_f)

Same problem here with the class level/instance level thing.
It would be nice if, whenever a Point object was updated,
the Line objects which are associated with it could
reflect those updates directly in Line.p_i and Line.p_f.
What's the Python way of achieving the same effect? Thanks
in advance, Rui Maciel

If you construct your code properly this can be achieved. If each point is an object, and lines are merely holding references to two point objects that define the start and end position of an imaginary "line", then updates onthe points will be reflected in the Line object.

Observe:

## START SESSION ##
py> class Point3d(object):
.... def __init__(self, x, y, z):
.... self.x = x
.... self.y = y
.... self.z = z
.... def __str__(self):
.... _ = 'Point3d({}, {}, {})'
.... return _.format(self.x, self.y, self.z)
....
py> p1 = Point3d(1,2,3)
py> str(p1)
Point3d(1, 2, 3)
py> p2 = Point3d(3,4,5)
py> str(p2)
Point3d(3, 4, 5)
py> class Line3d(object):
.... def __init__(self, p1, p2):
.... self.p1 = p1
.... self.p2 = p2
.... def __str__(self):
.... _ = 'Line3d({}, {})'
.... return _.format(self.p1, self.p2)
....
py> line1 = Line3d(p1, p2)
py> str(line1)
Line3d(Point3d(1, 2, 3), Point3d(3, 4, 5))
py> p1.x = 100
py> str(p1)
Point3d(100, 2, 3)
py> str(line1)
Line3d(Point3d(100, 2, 3), Point3d(3, 4, 5))
## END SESSION ##

Easy peasy.
 
R

Rick Johnson

[...]
There are a couple of ways you might get this to work the way you
want. One is by adding as an extra layer a proxy object, which could
be as simple as:
class Proxy(object):
def __init__(self, ref):
self.ref = ref
Instead of adding points to the model, add instances of Proxy that
contain the points. When adding points to the lines, add the same
Proxy objects instead.

Oh gawd just stop here! Are you joking? Are you playing coy? Are you attempting to confuse the lad? This type of complexity you suggest is what renders code bases unreadable and gives sloppy programmers "job security".
Later, when you want to replace a point in the
model, leave the Proxy in place but change the Point it contains by
modifying the "ref" attribute. Since the Line only directly
references the Proxy, it must follow the same ref attribute to access
the Point, and so in that way it will "see" the change. The downsides
to this approach are that you have to then use the Proxy objects all
over the place and explicitly "dereference" them by using the ref
attribute all over the place.

Teacher: "What is the worse thing since memory management?"
Jimmy Says: "ooh! ohh! i know!
Teacher: "Yes jimmy?"
Jimmy: "Superfluous memory management?"
Teacher: "Oh no, I'm sorry Jimmy, you're so close but i was looking for: `Superfluous memory management by proxy`!"
 
R

Rui Maciel

Rick said:
[...]

<code>
class Point:
position = []
def __init__(self, x, y, z = 0):
self.position = [x, y, z]

Firstly. Why would you define a Point object that holds it's x,y,z values
in a list attribute? Why not store them as self.x, self.y and self.z?
<snip/>

The position in space is represented as a vector, which is then used in a
series of operations.

Currently I'm using numpy arrays to represent vectors.

Secondly, why would store the position of the Point as a class attribute?
<snip/>

I've answered this 3 days ago. I'm still learning Python, and python.org's
tutorial on classes didn't explicitly covered the differences between class
and instance attributes.

If you construct your code properly this can be achieved. If each point is
an object, and lines are merely holding references to two point objects
that define the start and end position of an imaginary "line", then
updates on the points will be reflected in the Line object.

This was already covered three days ago in another post in this thread.
I'll quote the post below

<quote>
I've tested the following:

<code>
model = Model()
model.points.append(Point(1,2))
model.points.append(Point(1,4))

line = Line( model.points[0], model.points[1])

# Case A: this works
model.points[0].position = [2,3,4]
line.points

# Case B: this doesn't work
test.model.points[0] = test.Point(5,4,7)
line.points
</code>


Is there a Python way of getting the same effect with Case B?
</quote>

Rui Maciel
 
C

Chris Angelico

<snip/>

The position in space is represented as a vector, which is then used in a
series of operations.

Currently I'm using numpy arrays to represent vectors.


<snip/>

I've answered this 3 days ago. I'm still learning Python, and python.org's
tutorial on classes didn't explicitly covered the differences between class
and instance attributes.

Just FYI, Rick Johnson (aka Ranting Rick) is a known troll. Don't let
him goad you :)

Follow other people's advice, and take Rick's posts with a grain of
salt. Sometimes he has a good point to make (more often when he's
talking about tkinter, which is his area of expertise), but frequently
he spouts rubbish.

ChrisA
 
R

Rui Maciel

Chris said:
Just FYI, Rick Johnson (aka Ranting Rick) is a known troll. Don't let
him goad you :)

Follow other people's advice, and take Rick's posts with a grain of
salt. Sometimes he has a good point to make (more often when he's
talking about tkinter, which is his area of expertise), but frequently
he spouts rubbish.

I had no idea.


Thanks for the headsup.
Rui Maciel
 

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
474,137
Messages
2,570,795
Members
47,342
Latest member
eixataze

Latest Threads

Top