Уже есть много ответов, которые расскажут вам, как сделать правильную копию, но никто из них не говорит, почему ваша оригинальная «копия» не удалась.
Python не сохраняет значения в переменных; он связывает имена с объектами. Ваше исходное назначение взяло объект, на который ссылается my_list
, и связал его с new_list
. Независимо от того, какое имя вы используете, остается только один список, поэтому изменения, сделанные при обращении к нему как my_list
, будут сохраняться при обращении к нему как new_list
. Каждый из других ответов на этот вопрос дает вам различные способы создания нового объекта для привязки к new_list
.
Каждый элемент списка действует как имя, поскольку каждый элемент связывается не исключительно с объектом. Неглубокая копия создает новый список, элементы которого привязываются к тем же объектам, что и раньше.
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]
Чтобы сделать копию списка еще на один шаг, скопируйте каждый объект, на который ссылается ваш список, и привяжите эти копии элементов в новый список.
import copy
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]
Это еще не глубокая копия, потому что каждый элемент списка может ссылаться на другие объекты, точно так же, как список привязан к его элементам. Чтобы рекурсивно скопировать каждый элемент в списке, а затем каждый другой объект, на который ссылаются каждый элемент, и т. Д .: выполните глубокую копию.
import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
Для получения дополнительной информации о копировании в окне [gg] см. документацию .
Это подсказка для компилятора, что переменная будет сильно использоваться и что вы порекомендуете ее сохранить в регистре процессора, если это возможно.
Большинство современных компиляторов делают это автоматически и лучше их собирают чем мы, люди.
Он сообщает компилятору попытаться использовать регистр ЦП, а не ОЗУ, чтобы сохранить переменную. Регистры находятся в ЦП и намного быстрее доступны, чем оперативная память. Но это всего лишь предложение компилятору, и это может не произойти.
Register сообщит компилятору, что кодер полагает, что эта переменная будет написана / прочитана достаточно, чтобы оправдать ее хранение в одном из немногих регистров, доступных для использования переменной.
В настоящее время это не очень полезно, поскольку большинство оптимизаторов компиляторов лучше, чем вы, при определении того, должен ли регистр быть установленным, используется для этой переменной и как долго.
Я знаю, что этот вопрос касается C, но тот же вопрос для C ++ был закрыт как точный дубликат этого вопроса. Поэтому этот ответ может не применяться для C.
Последний проект стандарта C ++ 11 N3485 говорит об этом в 7.1.1 / 3:
Спецификатор
blockquote>register
- это намек на реализацию, которую объявленная переменная будет сильно использоваться. [ note: Подсказка может быть проигнорирована, и в большинстве реализаций она будет игнорироваться, если будет принят адрес переменной. Это использование устарело ... -end note ]В C ++ (но не в C) в стандарте не указано, что вы не можете принять адрес переменной, объявленной
register
; однако, поскольку переменная, хранящаяся в регистре CPU на протяжении всего жизненного цикла, не имеет связанного с ней местоположения памяти, попытка получить его адрес будет недействительной, а компилятор будет игнорировать ключевое словоregister
, чтобы разрешить прием адреса.
В поддерживаемых компиляторах C он пытается оптимизировать код, так что значение переменной сохраняется в реальном регистре процессора.
Я тестировал ключевое слово register под QNX 6.5.0, используя следующий код:
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/neutrino.h>
#include <sys/syspage.h>
int main(int argc, char *argv[]) {
uint64_t cps, cycle1, cycle2, ncycles;
double sec;
register int a=0, b = 1, c = 3, i;
cycle1 = ClockCycles();
for(i = 0; i < 100000000; i++)
a = ((a + b + c) * c) / 2;
cycle2 = ClockCycles();
ncycles = cycle2 - cycle1;
printf("%lld cycles elapsed\n", ncycles);
cps = SYSPAGE_ENTRY(qtime) -> cycles_per_sec;
printf("This system has %lld cycles per second\n", cps);
sec = (double)ncycles/cps;
printf("The cycles in seconds is %f\n", sec);
return EXIT_SUCCESS;
}
Я получил следующие результаты:
-> 807679611 истекших циклов
-> Эта система имеет 3300830000 циклов в секунду
-> Циклы в секундах ~ 0.244600
И теперь без регистра int:
int a=0, b = 1, c = 3, i;
Я получил:
-> 1421694077 пройденных циклов
-> Эта система имеет 3300830000 циклов в секунду
-> Циклы в секундах ~ 0.430700
Вы возитесь с сложным алгоритмом раскраски графа компилятора. Это используется для распределения регистров. Ну, в основном. Он действует как подсказка для компилятора - это правда. Но не игнорируется полностью, так как вам не разрешено принимать адрес переменной регистра (помните, что компилятор, теперь по вашей милости, будет пытаться действовать по-другому). Который в некотором смысле говорит вам не использовать его.
Ключевое слово было использовано long, long back. Когда было только так мало регистров, которые могли подсчитать их всех с помощью указательного пальца.
Но, как я уже сказал, устаревшее не означает, что вы не можете его использовать.
Регистр указывает компилятору оптимизировать этот код, сохраняя эту конкретную переменную в регистрах, а затем в памяти. это запрос компилятора, компилятор может или не может рассмотреть этот запрос. Вы можете использовать это средство в случае, если к какой-либо из вашей переменной обращались очень часто. Для ex: цикл.
Еще одна вещь: если вы объявляете переменную как регистр, вы не можете получить ее адрес, поскольку он не хранится в памяти. он получает свое распределение в регистре CPU.
Я прочитал, что он используется для оптимизации, но четко не определен ни в одном стандарте.
blockquote>Фактически это является , четко определенным C. Цитирование N1570 draft раздела 6.7.1, пункт 6 (другие версии имеют одинаковую формулировку):
Объявление идентификатора для объекта с спецификатором класса хранения
blockquote>register
предполагает, что доступ к объекту должен быть как можно быстрее. Степень, в которой такие предложения эффективны, определяется реализацией.Унарный оператор
&
не может применяться к объекту, определенному с помощьюregister
, аregister
может не быть используется в внешней декларации.Существует несколько других (довольно неясных) правил, специфичных для
register
-qualified objects:
- Определение объекта массива с помощью
register
имеет неопределенное поведение. Исправление: законно задавать объект массива с помощьюregister
, но вы не можете ничего использовать с таким объектом (индексирование в массив требует ввода адреса его начального элемента)._Alignas
(новый в C11) не может быть применен к такому объекту.- Если имя параметра, переданное макросу
va_start
, [register
-qualified, поведение не определено.Могут быть несколько других; Загрузите черновик стандарта и выполните поиск «register», если вам интересно.
Как следует из названия, значение оригинала для
register
должно было требовать от объекта храниться в регистре CPU. Но с улучшением оптимизации компиляторов это стало менее полезным. Современные версии стандарта C не относятся к регистрам CPU, потому что они больше не нуждаются (должны) предполагать, что есть такая вещь (существуют архитектуры, которые не используют регистры). Общая мудрость заключается в том, что применениеregister
к объявлению объекта с большей вероятностью ухудшает сгенерированный код, потому что это мешает распределению собственного регистра компилятора. Там может быть несколько случаев, когда это полезно (скажем, если вы действительно знаете, как часто переменная будет доступна, и ваши знания лучше, чем то, что может вычислить современный оптимизирующий компилятор).основным ощутимым эффектом
register
является то, что он предотвращает любую попытку принять адрес объекта. Это не особенно полезно в качестве подсказки оптимизации, поскольку его можно применять только к локальным переменным, а оптимизационный компилятор может убедиться в том, что адрес такого объекта не принят.
register
-qualified array, если это то, о чем вы думаете.
– Keith Thompson
19 July 2016 в 18:35
register
; см. обновленную первую маркерную точку в моем ответе. Юридически определить такой объект, но вы ничего не можете с ним поделать. Если вы добавите register
в определение s
в вашего примера , программа является незаконной (нарушение ограничений) в C. C ++ не устанавливает те же ограничения на register
, поэтому программа была бы действительной C ++ (но использование register
было бы бессмысленным).
– Keith Thompson
20 July 2016 в 15:43
register
могло бы служить полезной целью, если было бы право принимать адрес такой переменной, но только в тех случаях, когда семантика не была бы затронута путем копирования переменной во временное место, когда ее адрес был взят, и перезагрузка от временной в следующей точке последовательности. Это позволило бы компиляторам предположить, что переменная может быть безопасно сохранена в регистре во всех обращениях указателей при условии, что она покраснела в любом месте, где его адрес занят.
– supercat
20 July 2016 в 21:59
Компилятор Microsoft Visual C ++ игнорирует ключевое слово register
, когда включена глобальная оптимизация распределения регистров (флаг компилятора / Oe).
См. регистр ключевого слова в MSDN.
Storytime!
C, как язык, является абстракцией компьютера. Это позволяет вам делать то, что делает компьютер, манипулировать памятью, делать математику, печатать вещи и т. Д.
Но C - это только абстракция. И, в конечном счете, то, что он извлекает из вас , является языком ассемблера. Assembly - это язык, который читает процессор, и если вы его используете, вы делаете что-то с точки зрения процессора. Что делает процессор? В принципе, он читает из памяти, выполняет математику и записывает в память. CPU не просто выполняет математику по номерам в памяти. Во-первых, вам нужно переместить число из памяти в память внутри процессора, называемого регистром . Как только вы закончите делать все, что вам нужно сделать, чтобы это число, вы можете переместить его обратно в обычную системную память. Зачем использовать системную память? Регистры ограничены по количеству. В современных процессорах вы получаете около ста байтов, а более старые популярные процессоры были еще более фантастически ограничены (у 6502 было 3 8-битных регистра для вашего бесплатного использования). Итак, ваша средняя математическая операция выглядит так:
load first number from memory
load second number from memory
add the two
store answer into memory
Многое из того, что ... не математика. Эти операции загрузки и хранения могут занимать до половины времени обработки. C, являясь абстракцией компьютеров, освобождает программиста от беспокойства по поводу использования и жонглирования регистрами, а так как число и тип варьируются между компьютерами, C возлагает ответственность за распределение регистров исключительно на компилятор. С одним исключением.
Когда вы объявляете переменную register
, вы сообщаете компилятору «Yo, я намерен использовать эту переменную много и / или быть коротким. Если бы я был вами, Я постараюсь сохранить его в реестре. Когда в стандарте C говорится, что компиляторам вообще не нужно что-то делать, это потому, что стандарт C не знает, на каком компьютере вы компилируете, и это может быть как 6502 выше, где все 3 регистра необходимы только для работы , и нет запасного регистра, чтобы сохранить свой номер. Однако, когда говорится, что вы не можете принять адрес, это потому, что регистры не имеют адресов. Это руки процессора. Поскольку компилятор не должен указывать вам адрес, и поскольку он вообще не может иметь адрес, в настоящее время для компилятора открываются несколько оптимизаций. Он мог, скажем, всегда держать номер в регистре. Он не должен беспокоиться о том, где он хранится в памяти компьютера (за исключением необходимости вернуть его обратно). Это может даже записать его в другую переменную, передать его другому процессору, дать ему изменение местоположения и т. Д.
tl; dr: Краткоживущие переменные, которые выполняют много математики. Не объявляйте слишком много сразу.
Это не актуально в течение как минимум 15 лет, так как оптимизаторы принимают более правильные решения, чем вы можете. Даже когда это было актуально, это имело гораздо больший смысл в архитектуре процессора с большим количеством регистров, таких как SPARC или M68000, чем у Intel с небольшим количеством регистров, большинство из которых зарезервировано компилятором в своих целях.
Просто небольшое демо (без какой-либо реальной цели) для сравнения: при удалении ключевых слов register
перед каждой переменной этот фрагмент кода занимает 3,41 секунды на моем i7 (GCC), с register
тот же код завершается через 0,7 секунды.
#include <stdio.h>
int main(int argc, char** argv) {
register int numIterations = 20000;
register int i=0;
unsigned long val=0;
for (i; i<numIterations+1; i++)
{
register int j=0;
for (j;j<i;j++)
{
val=j+i;
}
}
printf("%d", val);
return 0;
}
На самом деле, регистр сообщает компилятору, что переменная не псевдонимы ничем другим в программе (даже не char).
Это может быть использовано современными компиляторами в самых разных ситуациях и может помочь компилятор совсем немного в сложном коде - в простом коде компиляторы могут понять это самостоятельно.
В противном случае это нецелесообразно и не используется для распределения регистров. Обычно это не связано с ухудшением производительности, если ваш компилятор достаточно современен.
register
запрещает брать адрес вообще, так как в противном случае было бы полезно сообщить компиляторам о случаях, когда компилятор сможет применять оптимизацию регистров , несмотря на i> передаваемый адрес переменной к внешней функции (переменная должна быть сброшена в память для этого конкретного вызова i>, но как только функция вернется, компилятор снова сможет рассматривать ее как переменную, адрес которой никогда не был выполнен).
– supercat
26 May 2016 в 23:11
bar
является переменной register
, компилятор может на отдыхе i> заменить foo(&bar);
на int temp=bar; foo(&temp); bar=temp;
, но с адресом bar
будет запрещено в большинство других контекстов не будет выглядеть слишком сложным правилом. Если переменную в противном случае можно было бы сохранить в регистре, замена заменила бы код меньше. Если в любом случае переменная должна быть сохранена в ОЗУ, замещение сделает код более крупным. Оставив вопрос о том, следует ли сделать подстановку до компилятора, в обоих случаях будет улучшен код.
– supercat
31 May 2016 в 14:28
register
для глобальных переменных, независимо от того, разрешает ли компилятор разрешить адрес, позволил бы сделать некоторые хорошие оптимизации в случаях, когда встроенная функция, использующая глобальную переменную, вызывается многократно в цикле. Я не могу придумать другого способа, чтобы эта переменная хранилась в регистре между итерациями цикла - не так ли?
– supercat
31 May 2016 в 14:30
Ключевое слово Register указывает компилятору, чтобы сохранить конкретную переменную в регистре CPU, чтобы она могла быть быстро доступной. С точки зрения программиста ключевое слово register используется для переменных, которые сильно используются в программе, поэтому компилятор может ускорить код. Хотя это зависит от компилятора, следует ли сохранять переменную в регистры процессора или в основной памяти.
Я удивлен, что никто не упоминал, что вы не можете взять адрес переменной регистра, даже если компилятор решает сохранить переменную в памяти, а не в регистре.
Таким образом, используя register
, вы ничего не выигрываете ( в любом случае компилятор сам решит, куда поставить переменную) и потерять оператор &
- нет причин для его использования.
register
полезен для этого, даже если компилятор не помещает его в регистр.
– Miles Rout
7 June 2014 в 06:33
const
также бесполезно, поскольку оно ничего не выигрывает, вы теряете способность изменять переменную. register
можно использовать, чтобы убедиться, что никто не примет адрес переменной в будущем, не подумав. У меня никогда не было причин использовать register
.
– Tor Klingberg
29 October 2015 в 14:11
В семидесятые годы, в самом начале языка C, было введено ключевое слово register, чтобы программист мог дать подсказки компилятору, указав, что переменная будет использоваться очень часто и что она должна разумно сохранить его в одном из внутренних регистров процессора.
В настоящее время оптимизаторы гораздо эффективнее, чем программисты, для определения переменных, которые с большей вероятностью будут храниться в регистрах, и оптимизатор не всегда принимает подсказка программиста.
Так много людей ошибочно рекомендуют не использовать ключевое слово register.
Давайте посмотрим, почему!
Ключевое слово register имеет связанную сторону Эффект: вы не можете ссылаться (получить адрес) на переменную типа регистра.
Люди, советующие другим не использовать регистры, ошибочно воспринимают это как дополнительный аргумент.
Однако простой факт, что вы не можете взять адрес переменной регистра, позволяет компилятор (и i ts optimizer), чтобы знать, что значение этой переменной не может быть изменено косвенно через указатель.
Когда в определенной точке потока команд переменная регистра имеет свое значение, назначенное в регистре процессора, и регистр не использовался, так как для получения значения другой переменной компилятор знает, что нет необходимости перегружать значение переменной в этом регистре. Это позволяет избежать дорогостоящего бесполезного доступа к памяти.
Сделайте свои собственные тесты, и вы получите значительные улучшения производительности в своих самых внутренних циклах.
register
; это only i> обязательный эффект ключевого словаregister
. Даже этого достаточно для улучшения оптимизации, потому что становится тривиальным сказать, что переменная может быть изменена только в этой функции. – Dale Hagglund 9 February 2010 в 19:26