Почему этот назначенный объект использует то же пространство памяти, что и исходный объект?

В python я столкнулся с этим странным явлением, работая с модулем itertools groupby.

В python назначение переменной означает назначение новой переменной ее собственной памяти вместо указателя на исходную память (насколько я понимаю, если это неверно, сообщите мне):

y = 7
x = y    
y = 9

x will still be 7

Тем не менее, когда я работал с модулем groupby, я использовал этот модуль для группировки элементов с одинаковым ключом в одну группу. Я хотел две группы, так как повторение исходной группы было бесполезным, поскольку память уже была изменена. Пример:

for key, group in groupby(rows, lambda x: x[0]):

    data = [thing[1] for thing in group] #accesses 1st attribute of element
    data2 = [thing[2] for thing in group] # would yield [] as group is empty

Поэтому я попробовал это вместо этого:

for key, group in groupby(rows, lambda x: x[0]):
    #create a copy of group to reiterate over
    toup = group

    print toup #<itertools._grouper object at 0x1039a8850>
    print group #<itertools._grouper object at 0x1039a8850>

    data = [thing[1] for thing in group] #accesses 1st attribute of element
    data2 = [thing[2] for thing in toup]

data2 должен получить доступ ко второму элементу, но дает [], поскольку они оба используют одну и ту же память

Мой вопрос: почему это происходит? Разве назначение группы toup не означает, что у toup будет копия памяти групп по другому шестнадцатеричному адресу?

Кроме того, что я могу сделать, чтобы обойти эту проблему, чтобы мне не приходилось писать две итерации groupby?


person user2117728    schedule 04.09.2014    source источник
comment
Это зависит от типа переменной. Примитивы, такие как целые числа и строки, копируются, если вы присваиваете их другой переменной, объекты экземпляра - нет - вместо этого переменная станет ссылкой на объект экземпляра. Попробуйте a= []; б = а; print(a is b) - это напечатает True.   -  person Aran-Fey    schedule 04.09.2014
comment
присвоение переменной означает присвоение новой переменной ее собственной памяти вместо указателя на исходную память - неверно, это не то, как работают имена Python (см., например, nedbatchelder.com/text/names.html). Используйте toup = group[:] для создания копии.   -  person jonrsharpe    schedule 04.09.2014
comment
@Rawing, это неверно (для начала, в Python на самом деле нет примитивов, и, например, целые числа являются экземплярами); разница в том, что, например. целые числа неизменяемы, тогда как, например. списки изменяемы. a = 1; b = a; a is b будет по-прежнему равно True, просто b += 1 не повлияет на a (поскольку целые числа неизменяемы).   -  person jonrsharpe    schedule 04.09.2014
comment
@jonrsharpe: Понятно, спасибо за исправление.   -  person Aran-Fey    schedule 04.09.2014


Ответы (2)


Вы заявляете:

В python назначение переменной означает назначение новой переменной ее собственной памяти вместо указателя на исходную память (насколько я понимаю, если это неверно, сообщите мне):

Это неправильно. Имена Python могут иметь аспекты, которые (иногда) похожи на переменные C, а также могут иметь аспекты, которые (иногда) похожи на указатели C. Попытаться сказать, что они похожи на одно или другое, просто сбить с толку. Не. Считайте их уникальными и идиоматическими для Python.

«Переменные» Python следует рассматривать как имена. Более одного может ссылаться на одно и то же место в памяти, даже если вы этого не планировали.

Пример:

>>> y=7
>>> x=7
>>> x is y
True
>>> id(x)
140316099265400
>>> id(y)
140316099265400

И (из-за интернирования следующее может быть правдой. См. PEP 237 относительно интернирования коротких целых чисел, но это детали реализации) :

>>> x=9
>>> y=5+4
>>> x is y
True

Оператор Python is возвращает True, если два объекта являются одними и теми же, сравнивая их адрес в памяти. Функция id возвращает этот адрес.

Рассмотрим в качестве последнего примера:

>>> li1=[1,2,3]
>>> li2=[1,2,3]
>>> li1==li2
True
>>> li1 is li2
False

Несмотря на то, что li1 == li2, они должны быть отдельными списками, иначе оба изменятся, если вы измените один, как в этом примере:

>>> li1=[1,2,3]
>>> li2=li1
>>> li1.append(4)
>>> li2
[1, 2, 3, 4]
>>> li1==li2
True
>>> li1 is li2
True

(Обязательно поймите другую классическую ошибку, которую рано или поздно совершат все программисты Python. Это вызвано множественными ссылками на один изменяемый объект, а затем ожидает, что одна ссылка будет действовать как один объект.)

Как отметил Джонршарп в комментариях, прочтите отличные факты и мифы об именах и значениях Python Неда Бэтчелдерса. или Как думать как питонист для более подробного обзора.

person dawg    schedule 04.09.2014
comment
Вы действительно должны упомянуть стажировку во втором примере - этого не произойдет с 300 is 60*5 (или во всех реализациях Python - это деталь CPython). - person jonrsharpe; 04.09.2014
comment
Имена Python могут иметь аспекты, которые (иногда) похожи на переменные C, а также могут иметь аспекты, которые (иногда) похожи на указатели C. Они всегда похожи на указатели C. Не существует универсального способа скопировать переменную по значению. - person Paul Draper; 11.09.2015
comment
@PaulDraper: я, вероятно, не преуменьшаю ваш комментарий, но a=[1,2,3] и b=a логически похожи в абстракции в Python, но совершенно разные внутри. - person dawg; 11.09.2015
comment
@dawg, в C a = b скопирует значение b в a. В Python это сделать невозможно; единственное, что вы можете скопировать, это указатели. (И все переменные являются указателями.) - person Paul Draper; 11.09.2015
comment
вопрос к вам: так почему числа и логические значения, назначенные разным именам, имеют один и тот же адрес памяти (ваш первый пример), но кортежи, которые также неизменны, будут назначены разным адресам, например. x = 1, y = 1, id(x) != id(y) (разрешается как истина). Кроме того, при первом запуске интерпретатора я заметил, что попытка его со списками фактически дала один и тот же общий адрес... это оптимизация, зависящая от текущего состояния памяти? - person trad; 01.11.2017
comment
Стажировка полностью зависит от реализации. Если вы хотите узнать «почему» во многих из этих вещей, обратитесь к соответствующему источнику. Могут быть существенные изменения от версии к версии и от платформы к платформе. В общем, это, вероятно, большая отдача от усилий для интернирования строк и целых чисел, а не для кортежей. Обратите внимание, что внутренние объекты кортежей часто интернируются. Попробуйте t=(1,2,3); t1=(1,2,3); id(t[0])==id(t1[0]), и это, скорее всего, True, даже если id(t)==id(t1) нет. - person dawg; 01.11.2017

В python назначение переменной означает назначение новой переменной ее собственной памяти вместо указателя на исходную память.

В Python есть изменяемые (например, списки, итераторы, почти все) и неизменяемые объекты (например, целые числа и строки). Присваивание не копирует объект в любом случае. С неизменяемыми объектами все операции с ними приводят к созданию нового экземпляра, поэтому вы не столкнетесь с проблемой «изменения» целого числа или строки, как с изменяемыми типами.

Мой вопрос: почему это происходит? Разве назначение группы toup не означает, что у toup будет копия памяти групп по другому шестнадцатеричному адресу?

Обе переменные будут указывать на один и тот же объект. Когда вы перебираете одну и исчерпаете итератор, перебор второй переменной даст вам пустую последовательность.

person Blender    schedule 04.09.2014