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

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.

Related

Why are ids of different elements of array.array identical? [duplicate]

Why does CPython (no clue about other Python implementations) have the following behavior?
tuple1 = ()
tuple2 = ()
dict1 = {}
dict2 = {}
list1 = []
list2 = []
# makes sense, tuples are immutable
assert(id(tuple1) == id(tuple2))
# also makes sense dicts are mutable
assert(id(dict1) != id(dict2))
# lists are mutable too
assert(id(list1) != id(list2))
assert(id(()) == id(()))
# why no assertion error on this?
assert(id({}) == id({}))
# or this?
assert(id([]) == id([]))
I have a few ideas why it may, but can't find a concrete reason why.
EDIT
To further prove Glenn's and Thomas' point:
[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440
When you call id({}), Python creates a dict and passes it to the id function. The id function takes its id (its memory location), and throws away the dict. The dict is destroyed. When you do it twice in quick succession (without any other dicts being created in the mean time), the dict Python creates the second time happens to use the same block of memory as the first time. (CPython's memory allocator makes that a lot more likely than it sounds.) Since (in CPython) id uses the memory location as the object id, the id of the two objects is the same. This obviously doesn't happen if you assign the dict to a variable and then get its id(), because the dicts are alive at the same time, so their id has to be different.
Mutability does not directly come into play, but code objects caching tuples and strings do. In the same code object (function or class body or module body) the same literals (integers, strings and certain tuples) will be re-used. Mutable objects can never be re-used, they're always created at runtime.
In short, an object's id is only unique for the lifetime of the object. After the object is destroyed, or before it is created, something else can have the same id.
CPython is garbage collecting objects as soon as they go out of scope, so the second [] is created after the first [] is collected. So, most of the time it ends up in the same memory location.
This shows what's happening very clearly (the output is likely to be different in other implementations of Python):
class A:
def __init__(self): print("a")
def __del__(self): print("b")
# a a b b False
print(A() is A())
# a b a b True
print(id(A()) == id(A()))

Assigning an (OOP) object to another

I was trying to assign a Python object to another in-place using a member function such as replace_object() below. However, as you can see, object_A remains unchanged and the only way to copy object_B is to create an entirely new object object_C, which defeats the purpose of in-place assignment.
What is going on here and how can I make the assignment in-place?
class some_class():
def __init__(self, attribute):
self.attribute = attribute
def replace_object(self, new_object):
self = new_object
# Does this line even have any effect?
self.attribute = new_object.attribute
self.new_attribute = 'triangle'
return self
object_A = some_class('yellow')
print(object_A.attribute) # yellow
object_B = some_class('green')
object_C = object_A.replace_object(object_B)
print(object_A.attribute) # yellow
print(object_C.attribute) # green
#print(object_A.new_attribute) # AttributeError!
print(object_B.new_attribute) # triangle
print(object_C.new_attribute) # triangle
I also tried to play around with deep copies using copy.copy(), but to no avail.
An interesting twist to this is that if I replace
object_C = object_A.replace_object(object_B)
with
object_A = object_A.replace_object(object_B)
then I get what I want. But why can't the same result be achieved by the statement self = new_object statement within replace_object()?
PS: I have a very good reason to do this in-place assignment, so although it may not be best practice in general, just go along with me here.
You can't 'assign an object to another'. You can assign new and existing objects to new and existing names.
self = new_object only says 'from now on the name self will refer to new_object', and does nothing to the old object. (Note self is just a variable name like any other and only by convention refers to an object within a class definition.)
The subsequent command self.attribute = new_object.attribute has no effect because self has already become a duplicate label for the new_object.
You could copy all the properties of a new object to the old object. You would end up with two distinct objects with different names and identical properties. A test of equality (a == b) would return false unless you overrode the equality operator for these objects.
To copy all the properties inline you could do something like this:
def replace_object(self, new_object):
self.__dict__ = new_object.__dict__.copy() # just a shallow copy of the attributes
There are very likely better ways to do whatever it is you want to do.

default value for arg in Python function [duplicate]

Why does CPython (no clue about other Python implementations) have the following behavior?
tuple1 = ()
tuple2 = ()
dict1 = {}
dict2 = {}
list1 = []
list2 = []
# makes sense, tuples are immutable
assert(id(tuple1) == id(tuple2))
# also makes sense dicts are mutable
assert(id(dict1) != id(dict2))
# lists are mutable too
assert(id(list1) != id(list2))
assert(id(()) == id(()))
# why no assertion error on this?
assert(id({}) == id({}))
# or this?
assert(id([]) == id([]))
I have a few ideas why it may, but can't find a concrete reason why.
EDIT
To further prove Glenn's and Thomas' point:
[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440
When you call id({}), Python creates a dict and passes it to the id function. The id function takes its id (its memory location), and throws away the dict. The dict is destroyed. When you do it twice in quick succession (without any other dicts being created in the mean time), the dict Python creates the second time happens to use the same block of memory as the first time. (CPython's memory allocator makes that a lot more likely than it sounds.) Since (in CPython) id uses the memory location as the object id, the id of the two objects is the same. This obviously doesn't happen if you assign the dict to a variable and then get its id(), because the dicts are alive at the same time, so their id has to be different.
Mutability does not directly come into play, but code objects caching tuples and strings do. In the same code object (function or class body or module body) the same literals (integers, strings and certain tuples) will be re-used. Mutable objects can never be re-used, they're always created at runtime.
In short, an object's id is only unique for the lifetime of the object. After the object is destroyed, or before it is created, something else can have the same id.
CPython is garbage collecting objects as soon as they go out of scope, so the second [] is created after the first [] is collected. So, most of the time it ends up in the same memory location.
This shows what's happening very clearly (the output is likely to be different in other implementations of Python):
class A:
def __init__(self): print("a")
def __del__(self): print("b")
# a a b b False
print(A() is A())
# a b a b True
print(id(A()) == id(A()))

sort list of class instances according to master list

I have a list of class instances like so:
classes = [A(someval), B(someval), C(someval)]
I would like to sort this list using a "master" list.
master_list = [B, A]
This would sort the list with B and A first if they exist and always in the order specified in master_list. The classes are field validators so I expect there never to be more than one of each class, but one can never know for sure.
Any other class instances can just come after in any order they appear.
I would like to be able to put some instances at the end of the list according to another master list, but I suspect the best this to do here is just reverse the list and do the same thing again.
But how do I do it?
Edit: Subclasses are not important as these validator classes only have a single callable method and are generally pretty simple beasts. I have yet to encounter a subclassed validator and I have tried to imagine a scenario where it would be beneficial, but I cannot.
You can use the index function of the list class. Here's a simple example:
class A: pass
class B: pass
class C: pass
classes = [A(), B(), C()]
master_list = [B, A]
def sorter(instance):
# get the type of the object
c = instance.__class__
# if it is in the master_list, use its index, otherwise put it at the end
return master_list.index(c) if c in master_list else len(master_list)
final_list = sorted(classes, key=sorter)
final_list is now
[<__main__.B instance at 0x023348C8>, <__main__.A instance at 0x01EE64B8>, <__main__.C instance at 0x023348F0>]
def class_order(master_list):
master_dict = { cls:i for (i, cls) in enumerate(master_list) }
return lambda i: master_dict.get(i.__class__, len(master_list))
# I renamed ‘classes’ to ‘instances’ because it makes more sense.
sorted(instances, key=class_order(master_list))
This assumes that only direct instances of classes in master_list appear in the original list. If something derived from A or B appears, they won't sort as As or Bs, which they should.

Creating a list of objects in Python

I'm trying to create a Python script that opens several databases and compares their contents. In the process of creating that script, I've run into a problem in creating a list whose contents are objects that I've created.
I've simplified the program to its bare bones for this posting. First I create a new class, create a new instance of it, assign it an attribute and then write it to a list. Then I assign a new value to the instance and again write it to a list... and again and again...
Problem is, it's always the same object so I'm really just changing the base object. When I read the list, I get a repeat of the same object over and over.
So how do you write objects to a list within a loop?
Here's my simplified code
class SimpleClass(object):
pass
x = SimpleClass
# Then create an empty list
simpleList = []
#Then loop through from 0 to 3 adding an attribute to the instance 'x' of SimpleClass
for count in range(0,4):
# each iteration creates a slightly different attribute value, and then prints it to
# prove that step is working
# but the problem is, I'm always updating a reference to 'x' and what I want to add to
# simplelist is a new instance of x that contains the updated attribute
x.attr1= '*Bob* '* count
print "Loop Count: %s Attribute Value %s" % (count, x.attr1)
simpleList.append(x)
print '-'*20
# And here I print out each instance of the object stored in the list 'simpleList'
# and the problem surfaces. Every element of 'simpleList' contains the same attribute value
y = SimpleClass
print "Reading the attributes from the objects in the list"
for count in range(0,4):
y = simpleList[count]
print y.attr1
So how do I (append, extend, copy or whatever) the elements of simpleList so that each entry contains a different instance of the object instead of all pointing to the same one?
You demonstrate a fundamental misunderstanding.
You never created an instance of SimpleClass at all, because you didn't call it.
for count in xrange(4):
x = SimpleClass()
x.attr = count
simplelist.append(x)
Or, if you let the class take parameters, instead, you can use a list comprehension.
simplelist = [SimpleClass(count) for count in xrange(4)]
A list comprehension can be used to fill a list with separate instances of a class, like so:
instancelist = [MyClass() for i in range(29)]
This avoids the problem with multiplying a list of one element with *, which re-uses the same object.
It shouldn't be necessary to recreate the SimpleClass object each time, as some are suggesting, if you're simply using it to output data based on its attributes. However, you're not actually creating an instance of the class; you're simply creating a reference to the class object itself. Therefore, you're adding a reference to the same class attribute to the list (instead of instance attribute), over and over.
Instead of:
x = SimpleClass
you need:
x = SimpleClass()
Create a new instance each time, where each new instance has the correct state, rather than continually modifying the state of the same instance.
Alternately, store an explicitly-made copy of the object (using the hint at this page) at each step, rather than the original.
If I understand correctly your question, you ask a way to execute a deep copy of an object.
What about using copy.deepcopy?
import copy
x = SimpleClass()
for count in range(0,4):
y = copy.deepcopy(x)
(...)
y.attr1= '*Bob* '* count
A deepcopy is a recursive copy of the entire object. For more reference, you can have a look at the python documentation: https://docs.python.org/2/library/copy.html
I think this simply demonstrates what you are trying to achieve:
# coding: utf-8
class Class():
count = 0
names = []
def __init__(self,name):
self.number = Class.count
self.name = name
Class.count += 1
Class.names.append(name)
l=[]
l.append(Class("uno"))
l.append(Class("duo"))
print l
print l[0].number, l[0].name
print l[1].number, l[1].name
print Class.count, Class.names
Run the code above and you get:-
[<__main__.Class instance at 0x6311b2c>,
<__main__.Class instance at 0x63117ec>]
0 uno
1 duo
2 ['uno', 'duo']

Categories

Resources