Синтаксис фрагмента списка Python используется без видимой причины - PullRequest
35 голосов
/ 27 ноября 2008

Я иногда вижу синтаксис списка фрагментов, используемый в коде Python, например:

newList = oldList[:]

Конечно, это так же, как:

newList = oldList

Или я что-то упустил?

Ответы [ 5 ]

52 голосов
/ 27 ноября 2008

[:] Полные копии списка, создавая копию структуры списка, содержащей ссылки на оригинальные элементы списка. Это означает, что операции с копией не влияют на структуру оригинала. Однако, если вы что-то делаете с участниками списка, оба списка по-прежнему ссылаются на них, поэтому обновления будут отображаться, если доступ к элементам осуществляется через оригинал.

A Deep Copy также будет копировать всех участников списка.

Приведенный ниже фрагмент кода показывает мелкую копию в действии.

# ================================================================
# === ShallowCopy.py =============================================
# ================================================================
#
class Foo:
    def __init__(self, data):
        self._data = data

aa = Foo ('aaa')
bb = Foo ('bbb')

# The initial list has two elements containing 'aaa' and 'bbb'
OldList = [aa,bb]
print OldList[0]._data

# The shallow copy makes a new list pointing to the old elements
NewList = OldList[:]
print NewList[0]._data

# Updating one of the elements through the new list sees the
# change reflected when you access that element through the
# old list.
NewList[0]._data = 'xxx'
print OldList[0]._data

# Updating the new list to point to something new is not reflected
# in the old list.
NewList[0] = Foo ('ccc')
print NewList[0]._data
print OldList[0]._data

Запуск его в оболочке Python дает следующую расшифровку. Мы можем увидеть список составляется с копиями старых предметов. Один из объектов может иметь его состояние обновляется по ссылке через старый список, и обновления могут быть видно, когда объект доступен через старый список. Наконец, изменение ссылка в новом списке может не отображаться в старом списке, так как новый список теперь ссылается на другой объект.

>>> # ================================================================
... # === ShallowCopy.py =============================================
... # ================================================================
... #
... class Foo:
...     def __init__(self, data):
...         self._data = data
...
>>> aa = Foo ('aaa')
>>> bb = Foo ('bbb')
>>>
>>> # The initial list has two elements containing 'aaa' and 'bbb'
... OldList = [aa,bb]
>>> print OldList[0]._data
aaa
>>>
>>> # The shallow copy makes a new list pointing to the old elements
... NewList = OldList[:]
>>> print NewList[0]._data
aaa
>>>
>>> # Updating one of the elements through the new list sees the
... # change reflected when you access that element through the
... # old list.
... NewList[0]._data = 'xxx'
>>> print OldList[0]._data
xxx
>>>
>>> # Updating the new list to point to something new is not reflected
... # in the old list.
... NewList[0] = Foo ('ccc')
>>> print NewList[0]._data
ccc
>>> print OldList[0]._data
xxx
48 голосов
/ 27 ноября 2008

Как сказал NXC, имена переменных Python фактически указывают на объект, а не на конкретное место в памяти.

newList = oldList создаст две разные переменные, которые указывают на один и тот же объект, поэтому изменение oldList также изменит newList.

Однако, когда вы делаете newList = oldList[:], он «разрезает» список и создает новый список. Значения по умолчанию для [:] - 0 и конец списка, поэтому он копирует все. Поэтому он создает новый список со всеми данными, содержащимися в первом, но оба они могут быть изменены без изменения другого.

11 голосов
/ 27 ноября 2008

Как уже было сказано, я просто добавлю простую демонстрацию:

>>> a = [1, 2, 3, 4]
>>> b = a
>>> c = a[:]
>>> b[2] = 10
>>> c[3] = 20
>>> a
[1, 2, 10, 4]
>>> b
[1, 2, 10, 4]
>>> c
[1, 2, 3, 20]
4 голосов
/ 27 ноября 2008

Никогда не думайте, что «a = b» в Python означает «скопировать b в a». Если с обеих сторон есть переменные, вы не можете этого знать. Вместо этого думайте об этом как «дайте b дополнительное имя a».

Если b является неизменным объектом (например, числом, кортежем или строкой), тогда да, вы получите копию. Но это потому, что когда вы имеете дело с неизменяемыми (которые, возможно, следовало бы назвать только для чтения , неизменяемым или WORM ), вы всегда получаете копия, по определению.

Если b является изменяемым, вы всегда должны делать что-то дополнительное, чтобы убедиться, что у вас есть истинная копия . Всегда . Со списками это так же просто, как срез: a = b [:].

Изменчивость также является причиной того, что это:

def myfunction(mylist=[]): 
    pass

... не совсем так, как вы думаете.

Если вы из C-фона: то, что осталось от '=', это всегда указатель. Все переменные являются указателями, всегда. Если вы помещаете переменные в список: a = [b, c], вы помещаете указатели на значения, на которые указывают b и c в списке, на который указывает a. Если затем вы установите a [0] = d, указатель в позиции 0 теперь указывает на то, на что указывает d.

См. Также копировальный модуль: http://docs.python.org/library/copy.html

0 голосов
/ 08 февраля 2010

Мелкое копирование: (копирует куски памяти из одного места в другое)

a = ['one','two','three']

b = a[:]

b[1] = 2

print id(a), a #Output: 1077248300 ['one', 'two', 'three']
print id(b), b #Output: 1077248908 ['one', 2, 'three']

Глубокая копия: (Копирует ссылку на объект)

a = ['one','two','three']

b = a

b[1] = 2


print id(a), a #Output: 1077248300 ['one', 2, 'three']
print id(b), b #Output: 1077248300 ['one', 2, 'three']
...