That’s becouse of the data types you’re working with. Basic types (they actually have a name that I don’t remember), like numbers, booleans or strings are copied, whilist more complex objects are referenced. And no, you can’t change this behavior but you can deepcopy data.
This is something that Python does (and dynamic languages in general) internally to improve performance, don’t think it’s the same on every other language. In C/C++ for example you must specify you data type*, and that includes saying if it is a pointer.
*: Except if you use auto or similar, then the compiler decides for you.
Changes the container by assigning a new value into it. This is no modification to the value but the container.
Modifying the value will not change the container.
value = [] # new list
container[key] = value # this replaces the current property in container
value.append("I'm a String") # this modifies the value but not the container
You do not need to reassign a modified value.
Why you cant do that with strings and numbers?
Because you do not modify strings. You assign a new string.
container[key] = "a" # assign a value
container[key] = "b" # assign another value
This belongs to all objects that can keep references to python objects, including your own custom classes.
The cool think is you can use this when working with module variables. Usually you need to tell via keyword “global” that a method overrides a module variable:
storedObject = None
def storeOwner(controller):
global storedObject
storedObject = controller.owner
When you use a container you do not assign a new object to the module but to the container:
value = [] # new list
container[key] = value # this replaces the current property in container
value.append("I'm a String") # this modifies the value but not the container
This does change the container[key] (that was the point of my post)
tested it with:
value = []
player["mylist"] = value
print(player["mylist"], value, "before")
value.append("I'm a String")
print(player["mylist"], value, "after")
console prints:
[] [] before
[“I’m a String”] [“I’m a String”] after
so changing the value does change the container[key] (or player[“mylist”])
Interestingly, this also applies when doing it with 2 properties:
player["mylist"] = []
player["mylist2"] = player["mylist"]
print(player["mylist"], player["mylist2"], "before") #prints: [] [] before
player["mylist"].append("test123") #the same happens if: player["mylist2"].append()
print(player["mylist"], player["mylist2"], "after") #prints: ['test123'] ['test123'] after
also: when doing this with 3 properties, the same happens
Monster tried to explain it without pointers, since pointers are usually hard to understand, but I think that understanding this without pointers is even harder. Take a look at what a pointer is, then you only need to know that when doing:
value = []
Here value is a pointer (of a datastructure that also uses pointers), so when you do “something = value” something becomes a pointer to the same datastructure, therefore modifying the datastructure from any of the pointers will effect the other ones (since it’s the same data you’re modifying, you’re just accessing it from diferent pointers).
A workarround? Well just crate a pointer to a new datastructure “something = []”, or:
value = [] # new list
container[key] = [] #new list 2
value.append("I'm a String") # this modifies the value but not the container
If you want to copy a list, you have two options, shallow copy (copy only the first level), or deepcopy (copy recursively all levels). A second level would be a list within a list for example, a shallow copy will copy the pointer (so still the same data), a deepcopy will copy the content (creating new data of all levels).
In general a shallow copy is enough:
value = [] # new list
container[key] = value[:] #shallow copy of value
value.append("I'm a String") # this modifies the value but not the container
We should be careful here - Python doesn’t use Pointers. I know that elmeunick knows this (it does, in C, but that’s not relevant.
The basic gist is that Python has two types of data structure - mutable and immutable. More accurately, one would say that Python allows different data types to define what happens when they are added together, etc. The code for += for an integer returns a new “object”, different from the original. += for a list updates the same original list object.
Lists are mutable (can be modified)
Tuples, integers, strings, floats, … are immutable. Hence, += on an immutable object doesn’t change the original.
In Python it is called “Reference”. A reference is a more than a pointer (it deals with usages), but you can think of it like it is one.
Yes, it does. Have a look at the comments.
These are three different operations, manipulating different objects.
Check each single operation:
# create a list
value = []
# assign a value to container player = manipulates player (not value)
player["mylist"] = value
# adds a value to container value = manipulates value (not player!)
value.append("I'm a String")
When you manipulate objects inside a container object, you do not manipulate the container object.
When you manipulate container object, you do not manipulate the objects inside the container.
You printed out the incorrect object. You showed player[“mylist”] which is value. You did not show player. Btw. the print is not really a good indicator. For example print() on a list calls str() on the list entries. This means you will see changes on the entries regardless if the list changed or not.
example:
dictionary = {} # create a dictionary
valueAsList = [] # creates a list
print(dictionary) # results in {}
dictionary["key"] = valueAsList # manipulates the dictionary
print(dictionary) # results in {[]}
dictionary["key"].append("string") # manipulates the value, not the dictionary
print(dictionary) # results in {[]} # results in {[string]} but the dictionary did not change
another way that should show you the difference:
dictionary = {} # create a dictionary
valueAsList = [] # creates a list
print(dictionary) # results in {}
dictionary["key"] = valueAsList # manipulates the dictionary
print(dictionary) # results in {[]}
valueAsList.append("string") # manipulates the value, not the dictionary
print(dictionary) # results in {[]} # results in {[string]} but the dictionary did not change
I hope you see, that the difference is how you access the list.
In the first example the code extracts it from the dictionary (read-only operation: dictionary[“key”]) and operates on the return value (.append(“string”))
In the second example the codes does not even care the dictionary. It operates on the variable directly (valueAsList.append(“string”)).