#๐ "redundant" methods
70 messages ยท Page 1 of 1 (latest)
@waxen crow
Remember to:
- Ask your Python question, not if you can ask or if there's an expert who can help.
- Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
- Explain what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
extend is more efficient because it doesn't create a whole new list
things = [0] * 1_000_000
things.extend([1, 2, 3]) # adds items to an existing list
So extend is a replacement for multiple append calls, not for +
Yes. appending a list would add the entire list as a single element of the list it was added to. extend/+= add each element of the sublist as a separate element of the list they're added to.
Elements, not indexes
.copy() makes another list. list1 = list2 makes 2 variable refer to the same list.
a = b means "make a refer to the same object that b refers to". It will make the two names refer to the same object.
A shallow copy however copies the list, but not its elements. Changes to the shallow copied list structure will not effect the original. Mutations to the elements though will be shown in both places.
list_2 = list_2 isn't even a shallow copy, see above for more information
And if you have some concrete example code that your questions are stemming from, posting the code and your confusion would be beneficial.
Technically .append and .extend are redundant since you can make one from the other. But both are very common things to do with lists, so they both exist.
@waxen crow you also have
from copy import deepcopy
list_2 = deepcopy(list_1)
```if you want to do a proper deep copy
While that works, getting into concrete code is how you'll properly explore what you know and what you don't. Theory only gets you so far.
Learning how to use a REPL for testing was a turning point for me.
One advantage of example code is that you can then change the example to watch spewcific behaviour changes. And it lets the people you're talking to repeat what you're doing/reading to provide context or suggestiuons.
Well that wouldn't work with strings.
But suppose the list contained another list. Changing the contained list would be visible in both shallow copies because they both refer to the same "another list".
!eval @waxen crow
inner_obj = [1, 2, 3]
old_list = [inner_obj, "test"]
print(f"{old_list = }")
new_list = list(old_list)
new_list[1] = "2nd test"
print(f"{new_list = }")
inner_obj.append(5)
inner_obj.append(99)
print("After modifying inner list:")
print(f"{old_list = }")
print(f"{new_list = }")
:white_check_mark: Your 3.13 eval job has completed with return code 0.
001 | old_list = [[1, 2, 3], 'test']
002 | new_list = [[1, 2, 3], '2nd test']
003 | After modifying inner list:
004 | old_list = [[1, 2, 3, 5, 99], 'test']
005 | new_list = [[1, 2, 3, 5, 99], '2nd test']
strings are immutable, just like tuples
Another reason besides user convenience for redundant methods is that they can allow for internal optimisation in the object. For example, if you .extend and supply a list as the stuff to extend, the list class can premeasure the extehnding list and allocate space in one go. Where a lot of .append calls might make for multiple reallocations of intenal space to store the new items.
Things like that.
These are two sides of the same coin to me.
A mutation is when you change an object. An example is
x = []
x.append(1)
The x object itself was mutated. It was changed because it had a new element added to it. This is possible because lists are mutable.
'hello' + 'world' in contrast doesn't change either string. + returns a new string. You cannot change a string because strings are immutable. You can only create new strings that are essentially altered copies of other strings.
Both lists refer to some object A. If A is modified then listing the lists will show the change.
I think using ids can show this well
As you can see, although the new_list is different from old_list; They both share the same inner_obj.
Hence any changes to it, anywhere in the code, would be reflected in both โ instead of being limited to any specific container/list.
you can circumvent this by using .deepcopy() which copies/creates new instances of all objects, and their elements.
!e
a = ["abc", [1, 2, 3]]
b = a.copy()
for x in a:
print(id(x))
print()
for x in b:
print(id(x))
:white_check_mark: Your 3.13 eval job has completed with return code 0.
001 | 139883719467744
002 | 139883719220096
003 |
004 | 139883719467744
005 | 139883719220096
id in Cpython is the same as memory address
Though in other python impls it will be some other stable number.
it helps to explain it in cpython with the memory address I think
as you can see, despite copying, the values inside are at the same memory address
it's not about the object in the list being mutable or not, list's themselves are internally mutable, meaning you can add, remove or update an element to/from/in the list, if the object that element stores is mutable or immutable doesn't really matter in this context
but if you print the ids of a and b, they will be different
this is shallow copying. New list, referring to same values.
so, because mutable objects can be changed (without reassigning the memory address of the object itself), those changes are reflected elsewhere
but you cannot observe that the memory is the same in immutable objects (without using id or is), because you cannot change the objects themselves, and observe those effects
you are mutating the list itself, not an object inside of it
!e
letters = ['a', 'b', 'c']
same_letters = letters.copy()
print(id(letters))
print(id(same_letters))
print(letters is same_letters)
:white_check_mark: Your 3.13 eval job has completed with return code 0.
001 | 140170636860288
002 | 140170639166144
003 | False
or other mutable objects. You will learn about more later, and how to make your own even
think of a list as not a list of objects, but a list of memory addresses, which python follows to find the objects
It may seem like that, but there's an important thing to think about: lists don't actually hold objects. They hold the addresses of objects. If I stick a piece of paper with someone's house address in a box, and then I go to the house and rearrange their furniture, that didn't change the box. The box still just holds a piece of paper with the same street address on it.
@waxen crow as the lists are different after a shallow copy you can change the any of the two lists themselves without affecting the other, but all the elements that you had in the list when you copied it will be the exact same object, they are pointing to the same address in memory
yep
and recursively
so if you have a list in a list in a list, it does that all the way down
shallow copy means it only is a new list pointing to the same memory, and deepcopy means you are making new objects entirely, which are equal to the originals.
or a dict or other collection
Yes. It shallow copies the top object, but instead of refering to the same objects inside, it copies them also. All the way down. New objects all around.
think shallow water vs deep water.
in deep water, it goes down down down
having knowledge from C and pointers really help with visualizing the memory address thing
oh, by the way, a variable is more or less just a human readable name for a memory address, a label for that memory address if you will
yeah, but you get an exact copy as it was in memory at that point in the code at the time of the deep copy operation
and there isn't a .deepcopy()
deepcopy() is a function from the copy module in the standard library
yeah, so it's not a method of the list object, not even after you import the module
correct
Ya, it's useful for if you need to give a nested object to some other code, but the code you're giving it to might alter it. Deep copying can be expensive, but it also prevents a whole class of bugs.
In languages like Rust, you can actually be required to do deep copies at certain times to prevent two pieces of code from referring to the same memory.
Ya, that's why like 4 helpers and a mod decided to jump in
2 mods
Definitely
a director no less even
This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.