I was experimenting with string slicing in Python, and this is the code I came up with:
s = 'a string'
print(id(s[3:]), id(s[5:]))
print(id(s[3:])==id(s[5:]))
print(s[3:] is s[5:])
print(s[3:] is s[3:])
The following are the outcomes of the code:
4396519216 4396519216
True
False
False
This website provides various instances, but I'm not sure what they mean. My question is how those string slices are stored in memory, why different slices of a string have the same id, and why do they yield 'False' when compared to using the 'is' keyword with the same id.
Solved! Go to Solution.
Run the same id check several times to see what it returns
# id
# Return the identity of an object.
# This is guaranteed to be unique among simultaneously existing objects.
#( CPython uses the object's memory address.)
s = 'a string'
id(s) # 2625510363760
id(s[:3]) # 2625512614384
id(s[:3]) # 2625512614000
id(s[:3]) # 2625512908528
#
id(s[:3]) is id(s[:3]) # False
#
id(id(s[:3]) == id(s[:3])) # 140711459289192
id(s[:3] == s[:3]) # 140711459289192
Now, wrap your head around the last two lines.
Run the same id check several times to see what it returns
# id
# Return the identity of an object.
# This is guaranteed to be unique among simultaneously existing objects.
#( CPython uses the object's memory address.)
s = 'a string'
id(s) # 2625510363760
id(s[:3]) # 2625512614384
id(s[:3]) # 2625512614000
id(s[:3]) # 2625512908528
#
id(s[:3]) is id(s[:3]) # False
#
id(id(s[:3]) == id(s[:3])) # 140711459289192
id(s[:3] == s[:3]) # 140711459289192
Now, wrap your head around the last two lines.
I've been an ArcGIS Python programmer for years and I've never really needed the id() function or the is operator to get real work done. In practice, I only really care about equality (==). Since you are just experimenting, I guess that's reason enough, but it's probably not a rabbit hole you need not delve in.
So here's what happening in the code above.
s = 'a string'
# 1) The print statement is called with a comma, which means each parameter
# will be evaluated, one at a time, from left to right.
# 2) s[3:] produces a new string with an id
# 3) That string is passed to the id function, which returns its memory address
# 4) That memory address goes into a new int object
# 5) The string is no longer needed, so it is freed
# 6) s[5:] produces a new string, reusing free memory
# 7) That string is passed to the id function, which returns its memory address
# 😎 That memory address goes into a new int object
# 9) The string is no longer needed, so it is freed
# Both objects had the same id at the time they were alive, so the
# same id is printed.
print(id(s[3:]), id(s[5:]))
# Similar to above, each parameter is evaluated left to right.
# This time, print only has one parameter, but that parameter
# is the result of an operator. The operator resolves its operands
# from left to right.
# Follow steps 1 through 9, paying attention to 4) and 8).
# Int objects are equal to each other if they have the same value
# so True is returned to the print statement.
print(id(s[3:])==id(s[5:]))
# 1) The print statement is called with one parameter, as evidenced by
# its lack of commas
# 2) The print statement's only parameter is the result of the is operator
# 3) The is operator evaluates its operands from left to right
# 4) s[3:] produces a new string with an id
# Note that this new string is still needed for the is operator, so it
# is not freed. It hangs out in memory
# 6) s[5:] produces a new string with an id
# This new string is also needed for the is operator, so it is not freed either
# 7) The is operator now compares the identity of both living strings,
# sees that they aren't equal, and returns False in both instances.
print(s[3:] is s[5:])
print(s[3:] is s[3:])
Now, to address @DanPatterson 's post:
s = 'a string'
# Since there are no print statements, I'm not sure how he got results
# that are in the comments. Perhaps, if run in a terminal, or at different
# times, the new strings did not reuse recently freed memory.
id(s) # 2625510363760
id(s[:3]) # 2625512614384
id(s[:3]) # 2625512614000
id(s[:3]) # 2625512908528
# 1) The is operator evaluates its operands from left to right
# 2) s[:3] creates a new string with an id
# 3) The id function is called, returning a new int object
# 4) The new string is freed, releasing its memory
# 5) s[:3] creates a new string with an id. This may or may
# not have been created in the same memory space as 1) was
# 6) The id function is called, returning a new int object
# 7) The is operator attempts to establish identity on the
# two ints that were returned, which have not been freed.
# The ints are different objects, so is returns False.
id(s[:3]) is id(s[:3]) # False
# This is actually the easiest to explain. Dan could have
# typed id(10>7), id("h" in "PYTHON".lower()), or id(5!=7)
# and still gotten the same memory address.
# What he is doing is grabbing the id of the result of the
# _equality_ operator. Those results are bools. The values
# True and False are global objects of type bool. Because
# they are objects, they each have an id. Comparator operators
# don't create new instances of type bool, but simply
# use the existing ones.
id(id(s[:3]) == id(s[:3])) # 140711459289192
id(s[:3] == s[:3]) # 140711459289192
like I said, with the self-learning explained 🤔
Here is another exercise:
# Here we create a tuple containing two new strings as the result
# of slice operations. Since both strings stick around in memory,
# neither are freed, and their id's will be different.
orange_slices = (s[:3], s[:5])
id(orange_slices[0])
1448227048496
id(orange_slices[1])
1448234245616