Câu hỏi Làm thế nào để sao chép hoặc sao chép một danh sách?


Các tùy chọn sao chép hoặc sao chép danh sách bằng Python là gì?

Sử dụng new_list = my_list sau đó sửa đổi new_list mỗi lần my_list thay đổi.
Tại sao điều này?


1698
2018-04-10 08:49


gốc




Các câu trả lời:


Với new_list = my_list, bạn thực sự không có hai danh sách. Bài tập chỉ sao chép tham chiếu vào danh sách chứ không phải danh sách thực, vì vậy cả hai new_list và my_list tham khảo danh sách tương tự sau khi chuyển nhượng.

Để thực sự sao chép danh sách, bạn có nhiều khả năng khác nhau:

  • Bạn có thể sử dụng nội trang list.copy() phương thức (có sẵn từ python 3.3):

    new_list = old_list.copy()
    
  • Bạn có thể cắt nó:

    new_list = old_list[:]
    

    Alex Martelli ý kiến ​​(ít nhất là trở lại năm 2007) về điều này, nó là một cú pháp kỳ lạ và nó không có ý nghĩa để sử dụng nó. ;) (Theo ý kiến ​​của mình, người tiếp theo là dễ đọc hơn).

  • Bạn có thể sử dụng list() chức năng:

    new_list = list(old_list)
    
  • Bạn có thể sử dụng chung copy.copy():

    import copy
    new_list = copy.copy(old_list)
    

    Đây là một chút chậm hơn list() bởi vì nó phải tìm ra kiểu dữ liệu của old_list Đầu tiên.

  • Nếu danh sách chứa các đối tượng và bạn cũng muốn sao chép chúng, hãy sử dụng chung copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)
    

    Rõ ràng là phương pháp chậm nhất và cần nhiều bộ nhớ nhất, nhưng đôi khi không thể tránh khỏi.

Thí dụ:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return str(self.val)

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance 
a.append('baz')
foo.val = 5

print('original: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
      % (a, b, c, d, e, f))

Kết quả:

original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]

2326
2018-04-10 08:55



@ FelixKling: Sẽ có ý nghĩa khi chỉnh sửa câu trả lời này để đề cập đến list.copy phương thức (có sẵn từ Python 3.3)? Nếu bạn không cần tương thích Python 2, nó thực sự là một cách rõ ràng để làm điều đó. - Mark Dickinson
@ FelixKling I 100% đồng ý. Để có câu trả lời cho một câu hỏi Python quan trọng, câu hỏi này hơi phân tán và lỗi thời. - Jiminion
Nếu tôi không lầm: newlist = [*mylist] cũng là một khả năng trong Python 3. newlist = list(mylist) có lẽ là rõ ràng hơn mặc dù. - Stéphane
một possiblity khác là new_list = old_list * 1 - aris


Felix đã cung cấp một câu trả lời tuyệt vời, nhưng tôi nghĩ tôi sẽ làm một so sánh tốc độ của các phương pháp khác nhau:

  1. 10,59 giây (105,9us / itn) - copy.deepcopy(old_list)
  2. 10.16 giây (101.6us / itn) - python tinh khiết Copy() phương pháp sao chép các lớp học với deepcopy
  3. 1.488 giây (14,88us / itn) - python tinh khiết Copy() phương pháp không sao chép các lớp (chỉ có dicts / lists / tuples)
  4. 0,25 giây (3,25us / itn) - for item in old_list: new_list.append(item)
  5. 0,17 giây (2,17us / itn) - [i for i in old_list] (a danh sách hiểu)
  6. 0,18 giây (1,86us / itn) - copy.copy(old_list)
  7. 0,075 giây (0,75us / itn) - list(old_list)
  8. 0,053 giây (0,53us / itn) - new_list = []; new_list.extend(old_list)
  9. 0,039 giây (0,39us / itn) - old_list[:] (danh sách cắt)

Vì vậy, nhanh nhất là danh sách cắt. Nhưng hãy nhận biết rằng copy.copy(), list[:] và list(list), không giống copy.deepcopy() và phiên bản python không sao chép bất kỳ danh sách, từ điển và thể hiện lớp nào trong danh sách, vì vậy nếu bản gốc thay đổi, chúng cũng sẽ thay đổi trong danh sách được sao chép và ngược lại.

(Đây là kịch bản nếu có ai quan tâm hoặc muốn nêu ra bất kỳ vấn đề nào :)

from copy import deepcopy

class old_class:
    def __init__(self):
        self.blah = 'blah'

class new_class(object):
    def __init__(self):
        self.blah = 'blah'

dignore = {str: None, unicode: None, int: None, type(None): None}

def Copy(obj, use_deepcopy=True):
    t = type(obj)

    if t in (list, tuple):
        if t == tuple:
            # Convert to a list if a tuple to 
            # allow assigning to when copying
            is_tuple = True
            obj = list(obj)
        else: 
            # Otherwise just do a quick slice copy
            obj = obj[:]
            is_tuple = False

        # Copy each item recursively
        for x in xrange(len(obj)):
            if type(obj[x]) in dignore:
                continue
            obj[x] = Copy(obj[x], use_deepcopy)

        if is_tuple: 
            # Convert back into a tuple again
            obj = tuple(obj)

    elif t == dict: 
        # Use the fast shallow dict copy() method and copy any 
        # values which aren't immutable (like lists, dicts etc)
        obj = obj.copy()
        for k in obj:
            if type(obj[k]) in dignore:
                continue
            obj[k] = Copy(obj[k], use_deepcopy)

    elif t in dignore: 
        # Numeric or string/unicode? 
        # It's immutable, so ignore it!
        pass 

    elif use_deepcopy: 
        obj = deepcopy(obj)
    return obj

if __name__ == '__main__':
    import copy
    from time import time

    num_times = 100000
    L = [None, 'blah', 1, 543.4532, 
         ['foo'], ('bar',), {'blah': 'blah'},
         old_class(), new_class()]

    t = time()
    for i in xrange(num_times):
        Copy(L)
    print 'Custom Copy:', time()-t

    t = time()
    for i in xrange(num_times):
        Copy(L, use_deepcopy=False)
    print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t

    t = time()
    for i in xrange(num_times):
        copy.copy(L)
    print 'copy.copy:', time()-t

    t = time()
    for i in xrange(num_times):
        copy.deepcopy(L)
    print 'copy.deepcopy:', time()-t

    t = time()
    for i in xrange(num_times):
        L[:]
    print 'list slicing [:]:', time()-t

    t = time()
    for i in xrange(num_times):
        list(L)
    print 'list(L):', time()-t

    t = time()
    for i in xrange(num_times):
        [i for i in L]
    print 'list expression(L):', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(L)
    print 'list extend:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        for y in L:
            a.append(y)
    print 'list append:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(i for i in L)
    print 'generator expression extend:', time()-t

CHỈNH SỬA: Thêm các lớp và kiểu cũ theo phong cách mới và dicts vào các tiêu chí chuẩn, và làm cho phiên bản python nhanh hơn nhiều và thêm một số phương thức khác bao gồm các biểu thức danh sách và extend().


449
2018-04-10 10:16



Vì bạn là điểm chuẩn, nên có thể bao gồm một điểm tham chiếu. Những con số này vẫn chính xác trong năm 2017 sử dụng Python 3.6 với mã được biên dịch hoàn toàn chưa? Tôi đang lưu ý câu trả lời dưới đây (stackoverflow.com/a/17810305/26219) đã đặt câu hỏi cho câu trả lời này. - Mark Edington
sử dụng timeit mô-đun. Ngoài ra, bạn không thể kết luận nhiều từ các tiêu chuẩn vi mô tùy ý như thế này. - Corey Goldberg


tôi có được kể Python 3.3+ bổ sung list.copy() phương pháp, nên nhanh như cắt:

newlist = old_list.copy()


116
2017-07-23 12:32





Các tùy chọn sao chép hoặc sao chép danh sách bằng Python là gì?

Trong Python 3, một bản sao nông có thể được thực hiện với:

a_copy = a_list.copy()

Trong Python 2 và 3, bạn có thể lấy một bản sao nông với một phần đầy đủ của bản gốc:

a_copy = a_list[:]

Giải trình

Có hai cách ngữ nghĩa để sao chép một danh sách. Một bản sao nông tạo ra một danh sách mới của các đối tượng tương tự, một bản sao sâu tạo ra một danh sách mới chứa các đối tượng tương đương mới.

Bản sao danh sách nông

Bản sao nông chỉ sao chép chính danh sách, đó là một vùng chứa tham chiếu đến các đối tượng trong danh sách. Nếu các đối tượng chứa chính chúng có thể thay đổi và một đối tượng được thay đổi, thay đổi sẽ được phản ánh trong cả hai danh sách.

Có nhiều cách khác nhau để làm điều này trong Python 2 và 3. Cách Python 2 cũng sẽ hoạt động trong Python 3.

Python 2

Trong Python 2, cách thành ngữ tạo một bản sao nông của một danh sách là với một lát hoàn chỉnh của bản gốc:

a_copy = a_list[:]

Bạn cũng có thể thực hiện điều tương tự bằng cách chuyển danh sách thông qua hàm tạo danh sách,

a_copy = list(a_list)

nhưng việc sử dụng hàm tạo ít hiệu quả hơn:

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

Trong Python 3, danh sách có được list.copy phương pháp:

a_copy = a_list.copy()

Trong Python 3.5:

>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125

Tạo một con trỏ khác không phải tạo một bản sao

Sử dụng new_list = my_list sau đó sửa đổi new_list mỗi khi my_list thay đổi. Tại sao điều này?

my_list chỉ là một tên trỏ đến danh sách thực trong bộ nhớ. Khi bạn nói new_list = my_list bạn không tạo bản sao, bạn chỉ cần thêm một tên khác trỏ vào danh sách gốc đó trong bộ nhớ. Chúng tôi có thể có các vấn đề tương tự khi chúng tôi tạo bản sao danh sách.

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

Danh sách này chỉ là một mảng các con trỏ đến nội dung, do đó bản sao nông chỉ sao chép các con trỏ và vì vậy bạn có hai danh sách khác nhau, nhưng chúng có cùng nội dung. Để tạo bản sao nội dung, bạn cần bản sao sâu.

Bản sao sâu

Để thực hiện một bản sao sâu của một danh sách, trong Python 2 hoặc 3, sử dụng deepcopy bên trong copy mô-đun:

import copy
a_deep_copy = copy.deepcopy(a_list)

Để chứng minh điều này cho phép chúng tôi tạo danh sách phụ mới như thế nào:

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]

Và vì vậy chúng tôi thấy rằng danh sách được sao chép sâu là một danh sách hoàn toàn khác với danh sách gốc. Bạn có thể cuộn chức năng của riêng bạn - nhưng không. Bạn có thể tạo ra các lỗi mà bạn sẽ không có bằng cách sử dụng chức năng sâu của thư viện chuẩn.

Không sử dụng eval

Bạn có thể thấy điều này được sử dụng như một cách để làm sâu sắc, nhưng đừng làm điều đó:

problematic_deep_copy = eval(repr(a_list))
  1. Nó nguy hiểm, đặc biệt nếu bạn đang đánh giá thứ gì đó từ một nguồn mà bạn không tin tưởng.
  2. Nó không đáng tin cậy, nếu một subelement bạn đang sao chép không có một đại diện có thể được đánh giá để tái tạo một yếu tố tương đương.
  3. Nó cũng kém hiệu quả hơn.

Trong Python bit 64 bit:

>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206

trên Python 64 bit 3.5:

>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644

88
2017-10-25 12:13





Có rất nhiều câu trả lời đã cho bạn biết làm thế nào để tạo một bản sao thích hợp, nhưng không ai trong số họ nói lý do tại sao 'bản sao' ban đầu của bạn không thành công.

Python không lưu trữ các giá trị trong các biến; nó liên kết tên với các đối tượng. Nhiệm vụ ban đầu của bạn lấy đối tượng được nhắc đến bởi my_list và ràng buộc nó với new_list cũng. Bất kể tên bạn sử dụng ở đó vẫn chỉ có một danh sách, vì vậy các thay đổi được thực hiện khi đề cập đến nó my_list sẽ tồn tại khi đề cập đến nó như new_list. Mỗi câu trả lời khác cho câu hỏi này cung cấp cho bạn những cách khác nhau để tạo một đối tượng mới để liên kết với new_list.

Mỗi phần tử của một danh sách hoạt động như một tên, trong đó mỗi phần tử liên kết không độc quyền với một đối tượng. Một bản sao nông tạo ra một danh sách mới có các phần tử liên kết với các đối tượng giống như trước đây.

new_list = list(my_list)  # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]

Để lấy danh sách của bạn sao chép thêm một bước nữa, hãy sao chép từng đối tượng mà danh sách của bạn đề cập đến và liên kết các bản sao phần tử đó với một danh sách mới.

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]

Đây chưa phải là một bản sao sâu, bởi vì mỗi phần tử của một danh sách có thể ám chỉ đến các đối tượng khác, giống như danh sách được ràng buộc với các phần tử của nó. Sao chép đệ quy mọi phần tử trong danh sách và sau đó mỗi đối tượng khác được đề cập bởi từng phần tử, v.v.: thực hiện một bản sao sâu.

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)

Xem tài liệu để biết thêm thông tin về các trường hợp góc khi sao chép.


42
2017-11-23 16:45





new_list = list(old_list)


30
2018-04-10 09:03





Sử dụng thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 

27
2018-04-10 08:53





Thành ngữ của Python để làm điều này là newList = oldList[:]


26
2018-04-10 08:53





Tất cả những người đóng góp khác đã cho tuyệt quá câu trả lời, hoạt động khi bạn có một danh sách thứ nguyên (được đánh số), tuy nhiên các phương pháp được đề cập cho đến nay, chỉ copy.deepcopy() làm việc để sao chép / sao chép một danh sách và không có nó trỏ đến lồng nhau list các đối tượng khi bạn đang làm việc với các danh sách lồng nhau, nhiều chiều (danh sách các danh sách). Trong khi Felix Kling đề cập đến nó trong câu trả lời của mình, có một chút vấn đề hơn và có thể là một cách giải quyết khác bằng cách sử dụng các trình cài sẵn có thể chứng minh một giải pháp thay thế nhanh hơn deepcopy.

Trong khi new_list = old_list[:], copy.copy(old_list)' và cho Py3k old_list.copy() làm việc cho các danh sách một cấp, họ hoàn nguyên để chỉ vào list đối tượng lồng nhau trong old_list và new_listvà thay đổi một trong các list các đối tượng được duy trì ở cái kia.

Chỉnh sửa: Thông tin mới mang đến ánh sáng

Như đã được chỉ ra bởi cả hai Aaron Hall và PM 2Ring  sử dụng eval() không chỉ là một ý tưởng tồi, nó còn chậm hơn nhiều so với copy.deepcopy(). 

Điều này có nghĩa là đối với danh sách đa chiều, tùy chọn duy nhất là copy.deepcopy(). Với điều đó đang được nói, nó thực sự không phải là một lựa chọn như hiệu suất đi theo cách phía nam khi bạn cố gắng sử dụng nó trên một mảng đa chiều có kích thước vừa phải. Tôi đã cố gắng timeit sử dụng mảng 42x42, không phải là chưa từng nghe hoặc thậm chí là lớn đối với các ứng dụng tin sinh học và tôi đã từ bỏ việc chờ phản hồi và chỉ bắt đầu nhập chỉnh sửa của tôi vào bài đăng này.

Dường như tùy chọn duy nhất thực sự là khởi tạo nhiều danh sách và hoạt động độc lập với chúng. Nếu bất cứ ai có bất kỳ gợi ý nào khác, về cách xử lý sao chép danh sách nhiều chiều, nó sẽ được đánh giá cao.

Như những người khác đã nêu, có có thể  có ý nghĩa vấn đề hiệu suất bằng cách sử dụng copy module và copy.deepcopy  cho danh sách đa chiều.  Cố gắng tìm ra cách sao chép danh sách đa chiều khác mà không cần sử dụng deepcopy, (Tôi đã làm việc trên một vấn đề cho một khóa học chỉ cho phép 5 giây cho toàn bộ thuật toán chạy để nhận tín dụng), tôi đã đưa ra cách sử dụng các hàm dựng sẵn để tạo bản sao của danh sách lồng nhau mà không để họ chỉ vào nhau hoặc tại list các đối tượng được lồng trong chúng. Tôi đã sử dụng eval() và repr() trong nhiệm vụ để sao chép danh sách cũ vào danh sách mới mà không cần tạo liên kết đến danh sách cũ. Nó có dạng:

new_list = eval(repr(old_list))

Về cơ bản những gì điều này làm là làm cho một đại diện của old_list như một chuỗi và sau đó đánh giá chuỗi như thể đó là đối tượng mà chuỗi đại diện. Bằng cách này, không có liên kết đến bản gốc list đối tượng được thực hiện. Mới list đối tượng được tạo và mỗi biến trỏ đến đối tượng độc lập của riêng nó. Đây là một ví dụ sử dụng danh sách lồng nhau 2 chiều.

old_list = [[0 for j in range(y)] for i in range(x)] # initialize (x,y) nested list

# assign a copy of old_list to new list without them pointing to the same list object
new_list = eval(repr(old_list)) 

# make a change to new_list 
for j in range(y):
    for i in range(x):
    new_list[i][j] += 1

Nếu sau đó bạn kiểm tra nội dung của từng danh sách, ví dụ: danh sách từ 4 đến 3, Python sẽ trả về

>>> new_list

[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]

>>> old_list

[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

Trong khi điều này có lẽ không phải là cách chính xác hoặc cú pháp chính xác để làm điều đó, nó có vẻ hoạt động tốt. Tôi chưa thử nghiệm hiệu suất, nhưng tôi sẽ đoán eval() và rep() sẽ có ít chi phí để chạy hơn deepcopy sẽ.


18
2017-07-10 03:51



Điều này sẽ không luôn luôn hoạt động, vì không có gì đảm bảo rằng chuỗi được trả về bởi repr() là đủ để tái tạo đối tượng. Cũng thế, eval() là một công cụ cuối cùng; xem Đánh giá thực sự nguy hiểm bởi SO cựu chiến binh Ned Batchelder để biết chi tiết. Vì vậy, khi bạn ủng hộ việc sử dụng eval() bạn có thật không nên đề cập rằng nó có thể nguy hiểm. - PM 2Ring
Điểm công bằng. Mặc dù tôi nghĩ rằng điểm của Batchelder là có eval() chức năng trong Python nói chung là một rủi ro. Nó không phải là quá nhiều hay không bạn sử dụng các chức năng trong mã nhưng nó là một lỗ hổng bảo mật trong Python trong và của chính nó. Ví dụ của tôi không sử dụng nó với một hàm nhận đầu vào từ input(), sys.agrvhoặc thậm chí là tệp văn bản. Nó là nhiều hơn dọc theo dòng khởi tạo một danh sách đa chiều trống một lần, và sau đó chỉ có một cách để sao chép nó trong một vòng lặp thay vì reinitializing tại mỗi lần lặp của vòng lặp. - AMR
Như @AaronHall đã chỉ ra, có khả năng một vấn đề hiệu suất đáng kể khi sử dụng new_list = eval(repr(old_list)), bên cạnh đó là một ý tưởng tồi, nó có lẽ cũng là cách quá chậm để làm việc. - AMR


Thời gian Python 3.6.0

Dưới đây là kết quả tính thời gian sử dụng Python 3.6.0. Hãy ghi nhớ những lần này liên quan đến nhau, không tuyệt đối.

Tôi bị mắc kẹt để chỉ làm các bản sao nông và cũng đã thêm một số phương thức mới không thể thực hiện được trong Python2, chẳng hạn như list.copy() (Python3 lát tương đương) và danh sách giải nén (*new_list, = list):

METHOD                  TIME TAKEN
b = a[:]                6.468942025996512   #Python2 winner
b = a.copy()            6.986593422974693   #Python3 "slice equivalent"
b = []; b.extend(a)     7.309216841997113
b = a[0:len(a)]         10.916740721993847
*b, = a                 11.046738261007704
b = list(a)             11.761539687984623
b = [i for i in a]      24.66165203397395
b = copy.copy(a)        30.853400873980718
b = []
for item in a:
  b.append(item)        48.19176080400939

Chúng ta có thể thấy người chiến thắng cũ vẫn xuất hiện trên đầu trang, nhưng không thực sự bởi một số lượng lớn, xem xét khả năng đọc được của Python3 list.copy() tiếp cận.

Lưu ý rằng những phương pháp này làm không phải kết quả tương đương đầu ra cho bất kỳ đầu vào nào ngoài danh sách. Tất cả chúng đều hoạt động cho các đối tượng có thể cắt lát, một vài công việc cho bất kỳ lần lặp nào, nhưng chỉ copy.copy() làm việc cho bất kỳ đối tượng Python nào.


Đây là mã thử nghiệm cho các bên quan tâm (Mẫu từ đây):

import timeit

COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'

print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []\nfor item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a:  b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))

12
2018-04-05 01:01



Họ dường như đã tối ưu hóa list initializer trong Python 3.6.1. Tôi không có cài đặt Python 3.6.0, nhưng b = list(a) được 2.7 và b = a[:] được 3.1và b = a.copy() được 3.1 (Trên Windows và Linux của tôi với CPython 3.6.1), vì vậy list() nhanh hơn khoảng 10% - Artyer
@Artyer Tôi đã thử nghiệm lại với Python 3.6.3 (WSL 16.04) và về cơ bản theo cùng thứ tự - River


Không giống như các ngôn ngữ khác có biến và giá trị, Python đã tên và đối tượng.

Tuyên bố này:

a = [1,2,3]

có nghĩa là cung cấp cho danh sách (đối tượng) một cái tên a, và cái này:

b = a

chỉ cho cùng một đối tượng a một cái tên mới b, vì vậy bất cứ khi nào bạn làm điều gì đó với a, đối tượng thay đổi và do đó b thay đổi.

Cách duy nhất để tạo ra có thật không bản sao của a là tạo một đối tượng mới như những câu trả lời khác đã nói.

Bạn có thể xem thêm về điều này đây.


11
2018-03-23 12:32