Python: Copy two dependent lists together with their dependence - python

I am stuck with some problem which I guess is not very difficult, but I could not find any answer to it.
I have two lists of objects, each of them containing lists of objects in the other. I would like to copy them both to do come tests and evaluate the results before repeating the process. In the end, I would keep the best result.
However, when copying each lists, the result is, unsurprisingly, not two dependent lists but two lists which do not interact anymore. How can I solve this? Is there some proper way to do it?
Given the two classes defined as follow.
import copy
class event:
def __init__(self, name):
self.name = name
self.list_of_persons = []
def invite_someone(self, person):
self.list_of_persons.append(person)
person.list_of_events.append(self)
class person:
def __init__(self, name):
self.name = name
self.list_of_events = []
I tried to write some simple example of the situation I am facing. The print function shows that the objects identifiers are different in the two lists.
# Create lists of the events and the persons
the_events = [event("a"), event("b")]
the_persons = [person("x"), person("y"), person("z")]
# Add some persons at the events
the_events[0].invite_someone(the_persons[0])
the_events[0].invite_someone(the_persons[1])
the_events[1].invite_someone(the_persons[1])
the_events[1].invite_someone(the_persons[2])
print("Original :", id(the_persons[1]), id(the_events[0].list_of_persons[1]), id(the_events[1].list_of_persons[0]))
# Save the original configuration
original_of_the_events = copy.deepcopy(the_events)
original_of_the_persons = copy.deepcopy(the_persons)
for i in range(10):
# QUESTION: How to make the following copies?
the_events = copy.deepcopy(original_of_the_events)
the_persons = copy.deepcopy(original_of_the_persons)
print(" i =", i, ":", id(the_persons[1]), id(the_events[0].list_of_persons[1]), id(the_events[1].list_of_persons[0]))
# Do some random stuff with the two lists
# Rate the resulting lists
# Record the best configuration
# Save the best result in a file
I thought about using some dictionary and make the list independent, but that would imply a lot of code revision which I would like to avoid.
Thank you in advance for any help! I am new both to Python and StackExchange.

Since deepcopy makes copies of all underlying objects of the thing being copied, doing two independent calls to deepcopy breaks your links between objects. If you create a new object with references to both of these things (like a dict) and copy that object, that will preserve the object references.
workspace = {'the_persons': the_persons, 'the_events': the_events}
cpw = copy.deepcopy(workspace)

Related

Error when appending to python object lists

I have defined a series of python objects of class dofiles which have a name and some empty lists.
class dofile:
name = ""
lines = []
inputs = []
outputs = []
intermediates = []
I am using the following code to loop through a list of these dofile objects called dofiles (after I have run code which fills up the lines list for each object which I know works correctly). The problematic code detects the phrase using and the word after it, then should append the word to the inputs list for each object.
for dofile in dofiles:
for line in dofile.lines:
# using
using_tups = re.findall(r'(using)\s([^\s,]+)', line)
for tuple in using_tups:
dofile.inputs.append(tuple[1])
However, I am ending up with the word appended to the inputs list of all of the dofile objects in dofiles. (ex: print(dofiles[0].inputs) and print(dofiles[1].inputs) return the same thing although they scan different files).
Am I missing something about how objects, lists, or loops work in Python?
I've tried (dofile.inputs).append(tuple[1]) but it doesn't seem to make a difference.
Thanks.
You're creating static vars, variables that are consistent across all dofile class instances. Essentially what is going on is that instead of having an "inputs" list for all dofile instances, you have one list that each one points to. To make them specific to a single instance, initialize them inside the __init__ class:
class dofile:
def __init__(self):
self.name = ""
self.lines = []
self.inputs = []
self.outputs = []
self.intermediates = []

Quick way to convert all instance variables in a class, to a list (Python)

I have created a class with around 100+ instance variables (as it will be used in a function to do something else).
Is there a way to translate all the instance variables; into an array list. Without manually appending each instance variable.
For instance:
class CreateHouse(object):
self.name = "Foobar"
self.title = "FooBarTest"
self.value = "FooBarValue"
# ...
# ...
# (100 more instance variables)
Is there a quicker way to append all these items to a list:
Quicker than:
theList = []
theList.append(self.name)
theList.append(self.title)
theList.append(self.value)
# ... (x100 elements)
The list would be used to perform another task, in another class/method.
The only solution (without totally rethinking your whole design - which FWIW might be an option to consider, cf my comments on your question) is to have a list of the attribute names (in the order you want them in the final list) and use getattr
class MonstruousGodClass(object):
_fields_list = ["name", "title", "value", ] #etc...
def as_list(self):
return [getattr(self, fieldname) for fieldname in self._fields_list]
Now since, as I mentionned in a comment, a list is NOT the right datatype here (from a semantical POV at least), you may want to use a dict instead - which makes the code much simpler:
import copy
def as_dict(self):
# we return a deepcopy to avoid unexpected side-effects
return copy.deepcopy(self.__dict__)

Python: Changing a single object within an array of objects changes all, even in a different array [duplicate]

This question already has an answer here:
Why does this code for initializing a list of lists apparently link the lists together? [duplicate]
(1 answer)
Closed 6 years ago.
I've got a one type of object, data_entry, that has a 2-dimensional array of other objects, time_entry.
Initialization of time_entries the array within data_entry looks like this:
[([time_entry()] * 12) for i in range(5)]
and initialization of the data_entry looks like this:
thing = data_entry()
Now, I have a list of "things", each which contains it's own 2d array of time_entrys.
Each time_entry has a list as one of it's attributes, initialized like so:
attributes = []
I modify attributes by extending it using .extend().
However, the problem I run into when I do this is EVERY single time_entry object in EVERY single data_entry object gets extended.
I know problems like this can arise from improper initialization of objects, so I'm wondering if perhaps my object creations are poor or there is another python quirk I am unaware of.
If you are performing the initialization on the class, it will affect all instances of the class. If that’s the case, this is not a result of it being in a list, but of it being on the class. For example:
#!/usr/bin/python
class BedrockDenizen():
attributes = []
wilma = BedrockDenizen()
fred = BedrockDenizen()
wilma.attributes.extend(['thin', 'smart'])
fred.attributes.extend(['fat', 'stupid'])
print 'Wilma:', wilma.attributes
print 'Fred:', fred.attributes
You will see that both Fred and Wilma are thin, smart, fat, and stupid.
Wilma: ['thin', 'smart', 'fat', 'stupid']
Fred: ['thin', 'smart', 'fat', 'stupid']
One way to fix this is to put the attribute creation into the init method, so that the attribute is per-instance:
class BedrockDenizen():
def __init__(self):
self.attributes = []
With that change, only Wilma is thin and smart, and only Fred is fat and stupid.
Wilma: ['thin', 'smart']
Fred: ['fat', 'stupid']
You may also need to show us more code. #Bakuriu notes that the problem may be that you are only creating one instance, and he may be right. For example, if this is closer to your code:
class BedrockDenizen():
def __init__(self):
self.attributes = []
neighborhood = [([BedrockDenizen()] * 2) for i in range(2)]
flintstones, rubbles = neighborhood
fred, wilma = flintstones
wilma.attributes.extend(['thin', 'smart'])
fred.attributes.extend(['fat', 'stupid'])
print 'Wilma:', wilma.attributes
print 'Fred:', fred.attributes
Then Fred and Wilma will continue to have the same attributes, because they aren’t really separate people. You may wish to use code more like this:
class BedrockDenizen():
def __init__(self):
self.attributes = []
neighborhood = [[BedrockDenizen() for n in range(2)] for i in range(2)]
flintstones, rubbles = neighborhood
fred, wilma = flintstones
wilma.attributes.extend(['thin', 'smart'])
fred.attributes.extend(['fat', 'stupid'])
print 'Wilma:', wilma.attributes
print 'Fred:', fred.attributes
That depends on what your needs are, though, as it seems like an odd way of doing things without more info.
This sounds like your attributes all point to the same list object inside. Then you call extend on the same object every time and modify it.
This is a common issue and discussed at
https://docs.python.org/3/faq/programming.html#how-do-i-create-a-multidimensional-list and Python list append behavior

Why does .append() not work on this list?

I have an object scene which is an instance of class Scene and has a list children which returns:
[<pythreejs.pythreejs.Mesh object at 0x000000002E836A90>, <pythreejs.pythreejs.SurfaceGrid object at 0x000000002DBF9F60>, <pythreejs.pythreejs.Mesh object at 0x000000002E8362E8>, <pythreejs.pythreejs.AmbientLight object at 0x000000002E8366D8>, <pythreejs.pythreejs.DirectionalLight object at 0x000000002E836630>]
If i want to update this list with a point which has type:
<class 'pythreejs.pythreejs.Mesh'>
I need to execute:
scene.children = list(scene.children) + [point]
Usually, I would execute:
scene.children.append(point)
However, while these two approaches both append point, only the first actually updates the list and produce the expected output (that is; voxels on a grid). Why?
The full code can be found here.
I am guessing your issue is due to children being a property (or other descriptor) rather than a simple attribute of the Scene instance you're interacting with. You can get a list of the children, or assign a new list of children to the attribute, but the lists you're dealing with are not really how the class keeps track of its children internally. If you modify the list you get from scene.children, the modifications are not reflected in the class.
One way to test this would be to save the list from scene.children several times in different variables and see if they are all the same list or not. Try:
a = scene.children
b = scene.children
c = scene.children
print(id(a), id(b), id(c))
I suspect you'll get different ids for each list.
Here's a class that demonstrates the same issue you are seeing:
class Test(object):
def __init__(self, values=()):
self._values = list(values)
#property
def values(self):
return list(self._values)
#values.setter
def values(self, new_values):
self._values = list(new_values)
Each time you check the values property, you'll get a new (copied) list.
I don't think there's a fix that is fundamentally different than what you've found to work. You might streamline things a little by by using:
scene.children += [point]
Because of how the += operator in Python works, this extends the list and then reassigns it back to scene.children (a += b is equivalent to a = a.__iadd__(b) if the __iadd__ method exists).
Per this issue, it turns out this is a traitlets issue. Modifying elements of self.children does not trigger an event notification unless a new list is defined.

Generate Class Instance In Python

I have been having trouble getting python to generate a (non-predetermined) number of class instances. Basically have classes be able to reproduce themselves.
class foo:
def __init__(self, name):
self.name = name
while True:
newinstance(foo) #what would the code be for this?
#or maybe
foo.newinstance #just something that could update itself
Basically generate a new instance any number of times. Thanks ahead of time.
This will do what you're asking for, but you'll want to hold onto the values somehow:
while True:
foo(some_name)
This will loop forever, so a more realistic option might be:
names = ["Ned", "Felix", "Guy"]
fooses = [foo(name) for name in names]
Use a list comprehension:
instances_of_foo = [foo("bar") for i in range(number_of_instances)]
Also, if you would like to pass different arguments to each instance, you can create of list of args instead of using range().
list_of_args = [args_for_instance_one, args_for_instance_two,...]
instances_of_foo = [foo(arg) for arg in list_of_args]

Categories

Resources