Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why variable = object doesn't work like variable = number

Tags:

These variable assignments work as I expect:

>>> a = 3 >>> b = a >>> print(a, b) (3, 3) >>> b=4 >>> print(a, b) (3, 4) 

However, these assignments behave differently:

>>> class number(): ...     def __init__(self, name, number): ...         self.name = name ...         self.number = number ...  >>> c = number("one", 1) >>> d = c >>> print(c.number, d.number) (1, 1) >>> d.number = 2 >>> print(c.number, d.number) (2, 2) 

Why is c is same as d, unlike in (a, b) example? How can I do something like in (a, b) in (c, d) classes example? That is, copy the object and then change one part of it (that won't affect the object that I borrowed properties from)?

like image 567
Gunnm Avatar asked Apr 28 '15 17:04

Gunnm


People also ask

Can numbers be variables?

A letter or symbol that represents any member of a collection of two or more numbers is called a variable. A letter or symbol that represents one specific number, known or unknown, is called a constant.

Can variable start with number in Python?

Rules for Python variables: A variable name must start with a letter or the underscore character. A variable name cannot start with a number. A variable name can only contain alpha-numeric characters and underscores (A-z, 0-9, and _ )

Does Python pass objects by reference?

The two most widely known and easy to understand approaches to parameter passing amongst programming languages are pass-by-reference and pass-by-value. Unfortunately, Python is “pass-by-object-reference”, of which it is often said: “Object references are passed by value.”

How do you set a variable to equal nothing?

To set the value of a variable is it's equal to null , use the nullish coalescing operator, e.g. myVar = myVar ?? 'new value' .


2 Answers

These lines:

c = number("one", 1) d = c 

...are effectively:

  • Create a new instance of number and assign it to c
  • Assign the existing reference called c to a new variable d

You haven't changed or modified anything about c; d is another name that points to the same instance.

Without cloning the instance or creating a new instance, you can't do anything similar to how the primitive int is behaving.


To correct a bit of information, the explanation above is rather simplified and a bit incomplete in its nature, although it mostly describes what's going on at 10,000 feet.

For a closer look, we have to realize a few things about Python's variables, or "names", and how they interact with this program.

As mentioned above, you have the notion of "names" and "bindings", which are pretty straightforward to reason at:

a = 3 b = a 

In this context, a is a name, and b is a binding to a. We haven't modified or changed anything about a.

As noted before, there are two types of data in Python: mutable and immutable. A name that points to immutable data, such as primitives and tuples, can be reassigned without any ill effect to any other bindings present on it, because no state is changing with respect to the binding.

This is why this reassignment does what we would expect it to:

print(a, b) b = 4 print(a, b) 

The result of b = 4 is that b is now pointing at a new copy of an integer, the value 4.

Recall that I did mention tuples as immutable data. You can't change the binding of a particular entity in your tuple...

t = ('foo', 'bar') t[0] = 'baz' # illegal 

...but you can have mutable data structures as part of those bindings.

t = ([1, 2, 3], 'bar') t[0].append([4, 5, 6]) # ([1, 2, 3, [4, 5, 6]], 'bar') 

So where does that leave our example?

c = number("one", 1) d = c 

number is a mutable type which is named as c, and its values can be changed at will between multiple different bindings to c.

Effectively, we've got a name and a binding to a name:

  • We have a new instance of number and refer to it by the name c.
  • Bind the reference c to another name d.

Again, nothing's changed about c, but it can be referenced through another name.

Unlike with the immutable data, when we reassign the value of d.number, we're reassigning the same binding that c is aware of:

>>> id(d.number) 36696408 >>> id(c.number) 36696408 

This is why you require either a new instance or a copy. You have to refer to a different instance of number. With this simple binding, you're not going to accomplish that.

from copy import copy c = number("one", 1) d = copy(c) id(c) # 140539175695784 id(d) # 140539175695856 
like image 92
Makoto Avatar answered Nov 02 '22 21:11

Makoto


I didn't see that anyone provided details on how to make these two cases work the same by copying the object instead of just assigning a new reference to the same object.

import copy c = number("one", 1) d = c e = copy.copy(c)  print(c.number, d.number, e.number) d.number = 2 e.number = 5 print(c.number, d.number, e.number) 

This will give you:

1 1 1 2 2 5 
like image 40
Brad Budlong Avatar answered Nov 02 '22 21:11

Brad Budlong