Module mode: main level code seems to be executed every logic tic. Why???

Hope this doesn’t need as much participation as my previous post.

So, I have this brief script:

from bge import logic
owner = logic.getCurrentController().owner
logic.globalDict['worldPosition'] = owner.worldPosition


def printPosition():
    print(logic.globalDict['worldPosition'])

Please note that owner is moving thanks to a Motion Actuator brick

if I run the script (worldPosition.py) in module mode (worldPosition.printPosition) with an Always sensor and True level triggering active, the printed position keeps updating every logic tic.
This makes no sense to me as the the main level code should get executed ONLY the first time the module is accessed.
Moreover, this does not happen if I expose other than owner.worldPosition, for example, owner.orientation.to_euler()…

Someone can explain this to me?

Please see attached if not clear

Attachments

problem_with_module_mode.blend (486 KB)

The mathutils types can support “owners” which represents data that is tied to some state. The worldPosition attribute of a KX_GameObject is tied to that object, unless you decide to copy it in some form. Hence, you appear to require a copy (either from vec.copy() or Vector(vec), or vec.xyz).
Good luck, Angus.

This is a good lesson for Python beginners. In Python, everything is an object, and objects are not copied when you make an assignment. Instead, whatever you are assigning the object to just gets a pointer to the object’s location in memory. This is very useful because it would take a lot of time and memory if Python made a full copy of an object every time you assigned it to a new variable.

For example, if you did the following:

A = [1, 2, 3]
B = A
A.append(4)

print(B)

the output would be:

[1, 2, 3, 4]

Both A and B “point” to the same list in memory. Therefore any modifications made to one affect the other.

In your case, owner.worldPosition is a Vector that the BGE’s logic loop updates every frame independent of your module. You’re able to see it changing because you’ve saved a pointer to it in the globalDict. On the other hand, calling the to_euler() method on the orientation Matrix actually creates a new object instead of getting a pointer to the same object. This new object isn’t used by the logic loop. You have the only pointer to it which is why it doesn’t change.

You should also note that with some “primitive” objects like integers, the above may seem to not hold true. For example:

A = 1
B = A
A += 2

print(B)

will still output 1. This is because “A += 2” doesn’t actually modify the object 1, it just assigns 3 to A. The same principle applies to other objects like booleans, strings, and floats.

It’s very important to know when you’re modifying an object and when you’re actually creating a new one. For example:

A = [1, 2, 3]
B = A
A = []
# print(B) will output [1, 2, 3] because A was assigned a NEW list

A = [1, 2, 3]
B = A
A.clear()
# print(B) will output [] because we have modified the list "in place"

Another example for Vectors. in the API you may have noticed that Vector has 2 methods that appear to do the same thing: normalize() and normalized(). However, normalize() normalizes the Vector IN PLACE while normalized() returns a NEW normalized copy.

If you’re ever unsure if two variables refer to the same object, you can always use the id() function which gives you the unique ID of an object.

A = 1
B = 1

print(id(A) == id(B))
# or you can use the is keyword
print(A is B)

It is mentioned above, but here in short:

since 2.5 the attributes worldPosition and localPosition are references to the concrete positional object (see call-by-reference).
before 2.50 these attributes where copies of the positional object from the moment of calling

The side effect is, that the stored position seams to change even without explicit update.

Solution: if you want to keep the coordinates of the position at the time of calling, create a copy of the position object (vector).


updatingPosition = object.worldPosition
positionAtCalling = object.worldPosition.copy()

I must say that after Angus reply all my little-burried-Python-knowledge came back to me, making me feel very stupid for asking…

Still, messing around with bge attributes and methods just to get sense of which ones create copies and which ones do not, I’ve run into this stalemate situation (see .blend attached)

why do identity and equality tests give me unexpected results?

I suspect this has something to do with me not being completely aware of mathutils tyes specification, or worst, maybe I still don’t get all the “Python objects have type, identity and value” thing (although I thought got that right!)

Please enlighten me

PS: explanations were awesome! Thanks to everyone!

Attachments

identity_vs_equality_check.blend (530 KB)

There certainly is some interesting behavior going on here. If you do the following:

A = owner.localLinearVelocity
print(id(owner.localLinearVelocity))
print(id(A))

you’ll notice that A gets assigned a separate Vector. Also, even though A and owner.localLinearVelocity are different objects, modifying A does indeed have an effect on the object’s velocity.

I suspect this has to do with the underlying implementation of the KX_GameObject. While you may have two separate Python objects, they may be utilizing the same data in C++. Someone more familiar with the code can probably explain it much better. You shouldn’t see weird effects like this when dealing with pure Python objects though.

I think your situation is due to the fact that despite A and owner.localLinearVelocity are separate objects (different IDs), their single components point to the same data in memory. For this reason if you change any component in place (for example A.z=1) this affects the other object too.


Not sure how to know, a priori, which operations will create new objects and which will not. Mostly it looks like exposing attributes creates new references to already existing data, while getSomething() methods create new objects (copies). Sometimes this is not as easy as it seems, as in your case…

On the other side still no clue on my situation. It’s like identity and equality operators work upside down! :eek:

My guess is that using localLinearVelocity with is still returns a new Python object while merely passing it to the id() function does not.

EDIT:
If the == comparison is what you’re confused about, I think you’ve just made a simple error. You’re comparing owner.loacalLinearVelocity to itself, not sphere.localLinearVelocity.

In terms of the implementation, the physics system is not written in Python, but C++. The Python API is a wrapper around this C++ code. The API does not have to return the same object if it does not wish to, but it does promise to provide a consistent interface to the underlying data it represents. Accessing the worldPosition returns a new Python object, simply because we don’t bother to cache and return the same one, which adds unnecessary boilerplate / creates issues when one might wish to return a new object each time.

== is an equality comparator. The is operator will return True if the identity of two objects (id()) is the same.

EDIT:
If the == comparison is what you’re confused about, I think you’ve just made a simple error. You’re comparing owner.loacalLinearVelocity to itself, not sphere.localLinearVelocity.

One mystery solved! :smiley:

My guess is that using localLinearVelocity with is still returns a new Python object while merely passing it to the id() function does not.[/I]

cube.localLinearVelocity and sphere.localLinearVelocity share the same id, but have different values. This makes no sense to me, if the memory pointer is the same, shouldn’t the value be the same to? Moreover, why do they share the same id in first place? It’s not like I set sphere.localLinearVelocity = cube.localLinearVelocity

Attachments

identity_vs_equality_check.blend (530 KB)

== is an equality comparator. The is operator will return True if the identity of two objects (id()) is the same.

As far as I know == (aka. equality operator) compares the objects value, not the id().
Identity comparison (id()) is performed instead by the is operator (aka. identity operator)

Still, the real question is: how could it be that cube.localLinearVelocity and sphere.localLinearVelocity share the same id, but have different values? And: Why do they share the same id in first place?

That’s incorrect. It is true that ‘==’ will by default compare the id() of the objects, but if the object defines it’s own eq method, that will be used instead.

Anothing thing I probably should have explained earlier. From the Python documentation:

id(object)
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.


Since you don’t hold onto a reference to the object you get, Python is immediately free to reuse that memory address. If you hold onto a reference like this:

A = sphere.localLinearVelocity
print(id(A), id(owner.localLinearVelocity))

you’ll see that the IDs are now different

That’s not incorrect. I state that there is a difference between the two operations. Perhaps you reply to Niv87?

Ah, I miss read. I though you were saying the ‘==’ operator compares the id().

The problem in presenting the operator is is the missing highlight.
It is easier to read if the operator 'is" is highlighted :wink:

I missread to, sorry

Anothing thing I probably should have explained earlier. From the Python documentation:

id(object)
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.


Since you don’t hold onto a reference to the object you get, Python is immediately free to reuse that memory address. If you hold onto a reference like this:
Code:
A = sphere.localLinearVelocity
print(id(A), id(owner.localLinearVelocity))
you’ll see that the IDs are now different

So, if I do:

print(id(sphere.localLinearVelocity) == id(cube.localLinearVelocity))

it returns True

The only way I can see this happening is the following:

  • Python creates a new sphere.localLinearVelocity object, with a certain id. The value this object itself is actually a pointer to the sphere localLinearVelocity vector in memory.
  • After collecting the sphere.localLinearVelocity id, Python immediately drops the object and frees the memory address.
  • Python re-assigns the previous id to a newly created cube.localLinearVelocity object, the value of this object itself is actually a pointer to the cube localLinearVelocity vector in memory.
  • Python collects the cube.localLinearVelocity id
  • Since the two collected ids share the same value, the equality comparison returns True

is this correct?

try this:


a = sphere ...
b = cube...
print(id(a) == id(b))

Yes, that is precisely what is happening. Don’t be fooled by the fact that that you have everything on the same line. Python is always counting references so it can manage memory effectively. As soon id() returns, Python sees that nothing is holding a reference to the object wrapper, so it’s immediately freed.

False :smiley:

Great! All mysteries solved…until next time! (which could be very soon :D)

Thank you all for the effort!