零碎知识 + 踩坑 - Python
1. Slicing makes shallow copy of a list#
I have talked this nature of slicing in previous post, currently I came across a weird behavior when improt a variable from another package.
messag.py
:
messages = []
def with_user(msg: str) -> None:
messages.append(msg)
def trim_last_two_messages() -> None:
global messages
print("in trim func, before trim, len of messages:" + str(len(messages)))
if len(messages) > 2:
messages = messages[:-2]
print("in trim func, after trim, len of messages:" + str(len(messages)))
if __name__ == "__main__":
pass
main.py
:
from message import messages
from message import with_user
from message import trim_last_two_messages
with_user('hello')
with_user('world')
with_user('foo')
print("main func, before trim:", messages)
trim_last_two_messages()
print("main func, after trim:", messages)
The output is:
main func, before trim: ['hello', 'world', 'foo']
in trim func, before trim, len of messages:3
in trim func, after trim, len of messages:1
main func, after trim: ['hello', 'world', 'foo']
But what I expected is:
main func, before trim: ['hello', 'world', 'foo']
in trim func, before trim, len of messages:3
in trim func, after trim, len of messages:1
main func, after trim: ['hello']
- Importing
messages
: When you importmessages
frommessage.py
intomain.py
, you are importing a reference to the same list object that exists inmessage.py
. - Appending Messages: When you call
with_user
, it appends new messages to themessages
list. Bothmain.py
andmessage.py
are still referring to the same list object. - Trimming Messages: In
trim_last_two_messages
, you are slicing themessages
list (messages = messages[:-2]
). This is where the key behavior occurs. Slicing a list in Python creates a new list object. So after this slicing operation,message.py
’smessages
now points to a new list object, butmain.py
’smessages
still points to the original list object.
Because of this, changes made to the list in message.py
(after the slicing operation) are not reflected in main.py
, as they are now two different list objects.
Correct way (change the function in message.py):
def trim_last_two_messages() -> None:
global messages
print("in trim func, before trim, len of messages:" + str(len(messages)))
if len(messages) > 2:
del messages[-2:]
print("in trim func, after trim, len of messages:" + str(len(messages)))
or
Change the code in main.py
:
import message
# use messages as this:
print(message.messages)
2. Performance of del
vs relicing
#
import timeit
# Test with a large list
large_list = list(range(1000000)) # a list with one million elements
def test_del_method():
temp_list = large_list.copy()
del temp_list[-2:]
def test_slice_method():
temp_list = large_list.copy()
temp_list = temp_list[:-2]
time_del = timeit.timeit(test_del_method, number=100)
time_slice = timeit.timeit(test_slice_method, number=100)
print(f"Time taken by del method: {time_del} seconds")
print(f"Time taken by slice method: {time_slice} seconds")
The output:
Time taken by del method: 0.312709417 seconds
Time taken by slice method: 0.683257708 seconds
查看其他文章