3
first = 30
second = first
first = 20
print("first: ", first, "second: ", second)
# first:  20 second:  30

In the above example, it seems the variable second points directly to the memory address for 30, and when first is reassigned it therefore has no effect on second.

class Circle: 
    pi = 3.1419

circle1 = Circle()
circle2 = Circle()
circle2.pi = 10
print(Circle.pi, circle1.pi, circle2.pi)
# 3.1419 3.1419 10

Circle.pi = 40
print(Circle.pi, circle1.pi, circle2.pi)
# 40 40 10

In the above example however, when I assign 40 to Circle.pi, it has an affect on circle1.pi because circle1.pi was pointing to Circle.pi instead of the memory address for 3.1419.

7
  • 4
    Python's language semantics are not defined in terms of pointers. Pointers are involved in the implementation, but you should not think of variables as pointers. Commented Apr 22, 2020 at 1:20
  • 1
    You especially shouldn't think of attributes as pointers, because attribute access has way too many fallbacks and customization hooks for that. Commented Apr 22, 2020 at 1:30
  • Just edited. Is that better? Commented Apr 22, 2020 at 1:33
  • 1
    A quibble: circle1.pi doesn't point to Circle.pi, it is Circle.pi. As written, circle1 has no attribute pi, so when you attempt to access circle1.pi, the lookup defers to the class object. If you write e.g. circle1.pi = 7 you will add an attribute to circle1, but you will not mutate Circle itself. Commented Apr 22, 2020 at 1:40
  • 1
    @ScottyBlades They are "one in the same" in the sense that writing circle1.pi when circle1 has no attribute pi (i.e. circle1.__dict__ doesn't contain an entry for pi) is translated to a lookup on the class object (i.e. Circle.pi). Essentially, writing circle1.pi results in circle1.__dict__ being checked, and if that fails, circle1.__class__.__dict__ is checked. If neither contain pi, an AttributeError is raised. Commented Apr 22, 2020 at 2:02

2 Answers 2

3

Python's semantics in this area can be surprising to people coming from languages with explicit pointers, but the rules are not complicated.

I highly recommend reading Ned Batchelder's Python Names and Values.

In short, Python associates names with values. An immutable value (like the integer in your first example) can't be changed, so multiple names pointing to it can't affect each other. However:

If a mutable value has more than one name, and the value changes, then all names see the change.

That's what you see in your class-based example. It can also be seen with lists:

>>> nums = [1, 2, 3]
>>> a = nums
>>> b = nums
>>> a.append(4)
>>> print(b)
[1, 2, 3, 4]
Sign up to request clarification or add additional context in comments.

Comments

0

EDITED:
From your class example:

  1. pi is a class attribute of Circle class.
  2. class attributes can be access from the object as you did circle2.pi
  3. However, class attribute, pi is not being modified from your code, circle2.pi = 10, it CREATES an instance attribute with the same name, pi as the class attribute.

2 Comments

Theres NO NEED to SHOUT. Also, it's a little misleading to say that "class attribute cannot be modified from the object". The type object for any given object can always be accessed (and mutated) from circle1.__class__. Likewise, if properties, metaclasses, or other niceties are at play, circle1.pi = 40 could very well mutate the class object itself. I realize these are pedantic points, but especially for questions such as this, its worth being precise.
@Brian I agree that the phrase can be misleading. Anyhow, none of those thing you mentioned are at play at OP's example. I don't know why you would get offended for bold italic styles, but if you did, I apologize. I'll remove those.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.