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()))
Related
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()))
I know they practically do the same thing, but if you were to lets say do something like...
curpop = this_other_ndarray
i = 0;
while i<20:
curpop[:] = select(curpop, parameter, parameter1)
stuff
more stuff
curpop[:] = some_stuff_I_did
i += 1;
So the above code is just saying, before I enter a generational loop I am going to take an initial generation of populations from 'this other ndarray'.
Then I am planning on changing that array over and over and everytime I restart the loop I will only select some from itself but I will declare that as what it is equal to now. Is this okay to do in Python3?
Is the declaration of
'array[:] = some of it self'
versus
'array = some of itself'
different at all?
These are two totally different things.
The first is simple assignment.
foo = bar
This assignment statement merely says that the name on the left-hand side now refers to the same object as the name on the right-hand side. These statements do not modify either object.
Objects are neither created nor necessarily destroyed. If you lose the last name of an object, however, you will have lost the object. The CPython runtime uses reference counting as a memory management strategy, and will automatically reclaim objects that have a zero reference count.
In Python, variables act simply like object names that you can create, destroy, and change what they reference. Think of them like name-tags.
Now, a statement like:
foo[:] = bar
Is actually a method call. It can be translated to:
foo.__setitem__(slice(None, None, None), bar)
Observe:
>>> class Foo:
... def __setitem__(self, key, value):
... print("Key:", key, "Value:", value)
...
>>> class Bar: pass
...
>>> foo = Foo()
>>> bar = Bar()
>>> foo[:] = bar
Key: slice(None, None, None) Value: <__main__.Bar object at 0x104aa5c50>
So, really, the type of the objects control the ultimate effects of this statement. In the case of numpy.ndarray objects, slice-based assignment works similarly to list slice based assignment in that it mutates the array object in-place, with a few more caveats like broadcasting to throw into the mix. See the relevant docs:
https://docs.scipy.org/doc/numpy-1.13.0/user/basics.indexing.html#assigning-values-to-indexed-arrays
In many cases
curpop[:]= iterable_value_as_tuple_string_dictionary_and_list_etc
do the same thing as
curpop=iterable_value_as_tuple_string_dictionary_and_list_etc
of course assign a string first or at any step will remove the ability in the next steps to use [:] to assign something again
note that
curpop[:]= notiterable_value != curpop=notiterable_value
as the first assign notiterable_value to each element of curpop and the second assign the value notiterable_value to curpop itself
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()))
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.
When I write this code:
polly = "alive"
palin = ["parrot", polly]
print(palin)
polly = "dead"
print(palin)
I thought it would output this:
"['parrot', 'alive']"
"['parrot', 'dead']"
However, it doesn't. It does output:
['parrot', 'alive']
['parrot', 'alive']
How do I get it to output that (the former)?
Python variables hold references to values. Thus, when you define the palin list, you pass in the value referenced by polly, not the variable itself.
You should imagine values as balloons, with variables being threads tied to those balloons. "alive" is a balloon, polly is just a thread to that balloon, and the palin list has a different thread tied to that same balloon. In python, a list is simply a series of threads, all numbered starting at 0.
What you do next is tie the polly string to a new balloon "dead", but the list is still holding on to the old thread tied to the "alive" balloon.
You can replace that thread to "alive" held by the list by reassigning the list by index to refer to each thread; in your example that's thread 1:
>>> palin[1] = polly
>>> palin
['parrot', 'dead']
Here I simply tied the palin[1] thread to the same thing polly is tied to, whatever that might be.
Note that any collection in python, such as dict, set, tuple, etc. are simply collections of threads too. Some of these can have their threads swapped out for different threads, such as lists and dicts, and that's what makes something in python "mutable".
Strings on the other hand, are not mutable. Once you define a string like "dead" or "alive", it's one balloon. You can tie it down with a thread (a variable, a list, or whatever), but you cannot replace letters inside of it. You can only tie that thread to a completely new string.
Most things in python can act like balloons. Integers, strings, lists, functions, instances, classes, all can be tied down to a variable, or tied into a container.
You may want to read Ned Batchelder's treatise on Python names too.
Before your second print statement, store your new values into palin:
palin = ["parrot", polly]
When you put a string in a list, the list holds a copy of the string. It doesn't matter whether the string was originally a variable, a literal value, the result of a function call, or something else; by the time the list sees it, it's just a string value. Changing whatever generated the string later will never affect the list.
If you want to store a reference to a value that will notice when that value changes, the usual mechanism is to use a list containing the "referenced" value. Applying that to your example, you wind up with a nested list. Example:
polly = ["alive"]
palin = ["parrot", polly]
print(palin)
polly[0] = "dead"
print(palin)
The list will contain values only, not references to variables as you would like. You could however store a lambda in the list, and have the lambda look up the value of your variable.
>>> a = 'a'
>>> list = ['a',lambda: a]
>>> list[1]
<function <lambda> at 0x7feff71dc500>
>>> list[1]()
'a'
>>> a = 'b'
>>> list[1]()
'b'
You can't. Assignment to a bare name is Python always only rebinds the name, and you cannot customize or monitor this operation.
What you can do is make polly a mutable object instead of a string, and mutate its value instead of rebinding the name. A simple example:
>>> polly = ['alive']
>>> items = ['parrot', polly]
>>> items
['parrot', ['alive']]
>>> polly[0] = 'dead'
>>> items
['parrot', ['dead']]
The other answers have explained what's going on well.
This is one of the (several) problems that motivate the use of objects. For example, one might do this:
class Animal:
def __init__(self, aniType, name):
self.aniType = aniType
self.name = name
self.isAlive = True
def kill(self):
self.isAlive = False
def getName(self):
return self.name
def getType(self):
return self.aniType
def isLiving(self):
return self.isAlive
polly = Animal("parrot", "polly")
print(polly.getName()+' the '+polly.getType()+' is alive?')
print(polly.isLiving())
polly.kill()
print(polly.getName()+' the '+polly.getType()+' is alive?')
print(polly.isLiving())
It may look like a lot of code at first for a simple task, but objects are often the way to go for things like this, because they help keep everything organized.
Here's the output of that program:
polly the parrot is alive?
True
polly the parrot is alive?
False