But how could I use this with class ? The idea is to avoid making copies for each new object
I think youâre assuming that thereâs something wrong with the class. Thereâs not. The class is working fine. However this:
p2 = Person("Hector", 13, graph)
p1 = Person("John", 36, graph)
Is what is resulting in the two sharing the list. Effectively you need to change it to:
p2 = Person("Hector", 13, graph.copy())
p1 = Person("John", 36, graph.copy())
Iâd prefer copying explicity outside the class (as per example above) rather than inside the class (ie using the solution TheDave suggested. This is because there may be cases where you want them to share graphs or where the graph is too expensive to copy.
Itâs worth noting that Rust (a different programming language) would have not compiled the code above because it would detect that both instances have mutable references to the same data, which can result in odd behaviours like this. To resolve it youâd either have to give them read only access to the graph, clone the graph, or pack it in a runtime-borrow-checked-object and handle when the data can mutate yourself. While you do end up fighting the rust compiler a fair bit, it does make it clear where this sort of problem is happening.
For me, it was obvious that i soon as i made a : " listB = listA " itâs because i need a copy of it.
Python is a high level language and tries to hide thungs from you. As was hinted at before, bools/ints/strings and lists/objects/dictionaries are different:
a = 1
b = a
b = 3
print(a) #1 -- b is a copy of a
a = [1]
b = a
b[0] = 3
print(a) #[3] -- b is a reference to a
Python could make copies of lists each time, but:
- Performance. If you have 3Gb of data in a list,
b = a
would stall the computer for ages as it copied, and youâd run out of ram in short order.
- itâs hard to tell where to stop copying. If you have a dictionary of lists of integers, do you duplicate the whole lot? Or do you copy only the dictionary and reference the lists?
- Often actually want shared references in OOP languages! Imagine if objects were copied each time:
objects_in_scene = scene.objects
player = objects_in_scene['player']
player.worldPosition.x += 1
Line 1 would create a copy of all the objects in the scene
Line 2 would create another copy of the player
Line 3 would move the copy of the copy of the player
And BGE would not know about these copies of the objects, so they wouldnât be drawn at all!
For referencing game objects from code, there are two patterns I follow depending on which I feel fits better:
- RAII: create the object inside the initilizer
- Composition: pass the object in
RAII example:
class Robot:
def __init__(self, scene):
self.obj = scene.addObject('Robot')
def __del__(self):
self.obj.endObject()
When you create a robot in python, it creates a new one in the scene! When you delete the object (and donât have reference cycles) it deletes the one in the scene!
Composition Example:
class Robot:
def __init__(self, obj):
self.obj = obj
#elsewhere
obj = scene.addObject('Robot')
#or
obj = scene.objects['Robot']
my_robot = Robot(obj)
RAII is nice for thing like bullets which tend to be spawned from in python. This is because you can just call Bullet(scene, position)
and it creates a bullet for you. Composition is nice for multi-object entites such as players or cars that are constructed externally in blender because you can go:
class Wheel:
def __init__(self, obj):
self.obj = obj
class car:
def __init__(self, car_body):
self.body = car_body
self.wheels = [Wheel(o) for o in car_body.children if 'Wheel' in o]
Often you can use a combination, for example:
class SomeCar:
def __init__(self, scene):
self.body = scene.addObject('SomeCar')
self.wheels = [Wheel(o) for o in self.body if 'Wheel' in o]
def __del__(self):
self.body.endObject
Here calling `Car(scene) will create a car (RAII style), but the car âcomposesâ itâs wheels from existing objects.