Coding: Variable and Property and Lists

I just now found out that…

When you assign a variable from a prop and change the variable, the prop doesnt change. (knew that)


import bge

cont = bge.logic.getCurrentController()
owner= cont.owner

myvar = owner["someprop"] #owner["someprop"] = 2

myvar += 2 #owner["someprop"] = 2


BUT:


import bge

cont = bge.logic.getCurrentController()
owner= cont.owner

myvar = owner["someprop"] #owner["someprop"] = ["a"]

myvar.append("b") #owner["someprop"] = ["a", "b"]


when the property is a list, somehow this isnt the same… (didnt know that), and the property changes with the variable

is there a reason for this, and is it possible to switch it off/circumvent it?


#this is what i use currently to avoid it

myvar = []
for item in owner["someprop"]:
     myvar.append(item)

but it seems to me to be a bit weird.

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.

ah ok thanks!

To be more clear:


container[key] = value

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:


storage = {}

def storeOwner(controller):
    storage["stored object"] = controller.owner

i hope this helps

referring to your second point:


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

@Leralasss

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

More info (and deepcopy): https://docs.python.org/3.1/library/copy.html

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.

Yep, it’s just that knowing what a pointer is helps to understand things better.

Tuples, integers, strings, floats, … are immutable. Hence, += on an immutable object doesn’t change the original.

“they actually have a name that I don’t remember”, oh that is the name :wink:

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”)).

I hope this helps

ok thanks, i think i understand it now :smiley: