Начал было присваивать значения новому "столбцу" с Item['adress'] = Item['ip'], а потом сформировал в цикле новые значения вида Item['adress'][i]=... И удивился, когда обнаружил, что поменялся и Item['ip'] Однако... все здесь объекты, а имена переменных - это только ссылки (точнее - указатели) на эти объекты (а не на метки областей памяти)...
Пока речь идет о переменных и функциях все почти очевидно... Но если в классе "не досмотрел или забыл...", и изменяемый объект изменился в другой функции, то никакой дебаггер не поможет... Я вовремя обнаружил, что создал разделяемую ссылку:
Image("C:\\Users\\kiss\\Pictures\\pythonR\\links1.png")
Для неизменяемых объектов (числа, строки) важна последовательность
Image("C:\\Users\\kiss\\Pictures\\pythonR\\links2.png")
#Что происходи при выполнении оператора
a='spam'
# Создается новый объект в памяти, а имени 'a' присваивается ссылка на этот новый объект
# Объект '3' не удаляется, поскольку на него ссылается имя 'b'
Переменные в языке Python, в отличие от других языков программирования, всегда являются указателями на объекты, а не метками областей памяти, доступных для изменения: запись нового значения в переменную не приводит к изменению первоначального объекта, но приводит к тому, что переменная начинает ссылаться на совершенно другой объект. В результате инструкция присваивания может воздействовать только на одну переменную. Однако когда в уравнении появляются изменяемые объекты и операции, их изменяющие, картина несколько меняется.
Потому далее последовательно законспектируем фрагменты из глав 6, 9, 18
Отсутствие инструкций объявления (p. 194)¶
Например, когда вводится инструкция a = 3 в интерактивной оболочке интерпретатора или в файле сценария, как интерпретатор Python узнает, что речь идет о целом числе? И вообще, как Python узнает, что есть что?
Модель динамической типизации существенно отличается от моделей типов в традиционных языках программирования. Динамическая типизация обычно проще поддается пониманию начинающих, особенно если они четко осознают разницу между именами и объектами.
В языкеPython все это решается весьма естественным способом, как описано ниже:
Создание переменной
Переменная (то есть имя), такая как 'a', создается автоматически, когда в программном коде ей впервые присваивается некоторое значение. Все последующие операции присваивания просто изменяют значение, ассоциированное с уже созданным именем. Строго говоря, интерпретатор Python определяет некоторые имена до запуска программного кода, но вполне допустимо думать, что переменные создаются первой операцией присваивания.
Типы переменных
Переменные не имеют никакой информации о типе или ограничениях, связанных с ним. Понятие типа присуще объектам, а не именам. Переменные универсальны по своей природе – они всегда являются всего лишь ссылками на конкретные объекты в конкретные моменты времени.
Использование переменной
Когда переменная участвует в выражении, ее имя замещается объектом, на который она в настоящий момент ссылается, независимо от того, что это за объект. Кроме того, прежде чем переменную можно будет использовать, ей должно быть присвоено значение – использование неинициализированной переменной приведет к ошибке.
Термин "переменная" - это имя ссылки на объект? Помним, что в Python все (даже числа) - объекты...¶
#Например, если ввести такую инструкцию:
>>> a = 3
#интерпретатор Python выполнит эту инструкцию в три этапа, по крайней мере, концептуально.
#Следующие этапы отражают все операции присваиванияв языке Python:
#1. Создается объект, представляющий число 3.
#2. Создается переменная a, если она еще отсутствует.
#3. В переменную a записывается ссылка на вновь созданный объект, представляющий число 3.
Так и есть, Эти ссылки на объекты в языке Python так и называются ссылки, то есть ссылки – это своего рода ассоциативная связь, реализованная в виде указателя на область памяти.
Когда бы ни использовалась переменная (то есть ссылка), интерпретатор Python автоматически переходит по ссылке от переменной к объ- екту. Все на самом деле гораздо проще, чем кажется.
В конкретных терминах: •• Переменные – это записи в системной таблице, где предусмотрено место для хранения ссылок на объекты. •• Объекты – это области памяти с объемом, достаточным для представления значений этих объектов. •• Ссылки – это автоматически разыменовываемые указатели на объекты.
Каждый объект имеет два стандартных поля: описатель типа, используемый для хранения информации о типе объекта, и счетчик ссылок, используемый для определения момента, когда память, занимаемая объектом, может быть освобождена.
>>> L = [1, 2, 3]
>>> M = L # M и L – ссылки на один и тот же объект
>>> L == M # Одно и то же значение
True
>>> L is M # Один и тот же объект
True
>>> L = [1, 2, 3]
>>> M = [1, 2, 3] # M и L ссылаются на разные объекты
>>> L == M # Одно и то же значение
#True
>>> L is M # Но разные объекты
#False
>>> X = 42
>>> Y = 42 # Должно получиться два разных объекта
>>> X == Y
#True
>>> X is Y # Тот же самый объект: кэширование в действии!
#True
В этом примере переменные X и Y должны быть равны (==, одно и то же значение), но не эквивалентны (is, один и тот же объект), потому что было выполнено два разных литеральных выражения. Однако из-за того, что любые целые числа и строки кэшируются и используются повторно ператор is сообщает, что переменные ссылаются на один и тот же объект
Как увидеть количество ссылок на объект?¶
>>> import sys
>>> sys.getrefcount(1) # 837 указателей на этот участок памяти
#837 - это количество ссылок на объект '1' (единица)
# а у меня этих ссылок и того больше - 1650
Ссылки и копии (p. 304 )¶
В главе 6 говорилось, что при выполнении операции присваивания всегда сохраняется ссылка на объект, а не копия самого объекта. В большинстве случаев именно это нам и требуется. Однако в процессе выполнения операций присваивания может быть создано несколько ссылок на один и тот же объект, поэтому очень важно понимать, что изменения, произведенные в изменяемом объекте с помощью одной ссылки, отразятся и на других ссылках, указывающих на этот же объект. Если такой порядок вещей является нежелательным, необходимо явно сообщить интерпретатору, чтобы он создал копию объекта.
>>> X = [1, 2, 3]
>>> L = ['a', X, 'b'] # Встроенная ссылка на объект X
>>> D = {'x':X, 'y':2}
from IPython.display import Image
Image("C:\\Users\\kiss\\Pictures\\pythonR\\links.png")
Так как списки относятся к категории изменяемых объектов, изменения в объекте списка, произведенные с помощью любой из трех ссылок, также отразятся на других двух ссылках:
X[1] = 'surprise' # Изменяет все три ссылки!
D
Ссылки – это более высокоуровневый аналог указателей в других языках программирования. И хотя вы не можете извлечь непосредственное значение самой ссылки, тем не менее одну и ту же ссылку можно сохранить более чем в одном месте (в переменных, в списках и так далее).
Это полезная особенность – вы можете передавать крупные объекты между компонентами программы без выполнения дорогостоящей операции копирования.
Однако, когда действительно необходимо создать копию объекта, вы можете запросить их:¶
•• Выражение извлечения среза с пустыми пределами (L[:]) создает копию последовательности.
•• Метод словарей и множеств copy создает копию словаря (D.copy()).
•• Некоторые встроенные функции, такие как list, создают копию списка (list(L)).
•• Модуль copy, входящий в состав стандартной библиотеки, создает полные копии объектов.
>>> L = [1,2,3]
>>> D = {'a':1, 'b':2}
Чтобы предотвратить возможность изменения объектов в других функциях, достаточно связать с другими переменными копии объектов вместо того, чтобы связать с ними сами объекты:
>>> A = L[:] # Вместо A = L (или list(L))
>>> B = D.copy() # Вместо B = D (то же относится и к множествам)
A,B
Cтандартный модуль copy¶
Одно замечание по поводу копий: выражение извлечения среза с пустыми значениями пределов и метод словаря copy создают поверхностные копии – тоесть они не копируют вложенные структуры данных, даже если таковые присутствуют. Если необходима полная копия структуры произвольной глубины вложенности, следует использовать стандартный модуль copy:
добавьте инструкцию import copy и вставьте выражение X = copy.deepcopy(Y), которое создаст полную копию объекта Y со сколь угодно большой глубиной вложенности.
Эта функция выполняет рекурсивный обход объектов и копирует все составные части. Однако такой подход используется намного реже (потому что для этого приходится писать дополнительный программный код). Обычно ссылки – это именно то, что нам требуется, в противном случае чаще всего применяются такие способы копирования, как операция извлечения среза и метод copy.
import copy
X = copy.copy(Y) # Создание “поверхностной” копии любого объекта Y
X = copy.deepcopy(Y) # Создание полной копии: копируются все вложенные части
Передача аргументов (p. 505)¶
Аргументы передаются через автоматическое присваивание объектов локальным переменным. Аргументы функции – ссылки на объекты, которые (возможно) используются совместно с вызывающей программой, – это всего лишь результат еще одной из разновидностей операции присваивания. Ссылки в языке Python реализованы в виде указателей, поэтому все аргументы фактически передаются по указателям. Объекты, которые передаются в виде аргументов, никогда не копируются автоматически.
Операция присваивания именам аргументов внутри функции не оказывает влияния на вызывающую программу. При вызове функции имена аргументов, указанные в ее заголовке, становятся новыми локальными именами в области видимости функции. Это не является совмещением имен между именами аргументов и именами в вызывающей программе.
Изменение внутри функции аргумента, который является изменяемым объектом, может оказывать влияние на вызывающую программу.
C другой стороны, так как аргументы – это всего лишь результат операции присваивания полученных объектов, функции могут воздействовать на полученные изменяемые объекты и тем самым оказывать влияние на вызывающую программу.
Изменяемые объекты могут рассматриваться функциями как средство ввода, так и вывода информации.
•• Неизменяемые объекты передаются «по значению». Такие объекты, как целые числа и строки, передаются в виде ссылок на объекты, а не в виде копий объектов, но так как неизменяемые объекты невозможно изменить непосредственно, передача таких объектов очень напоминает копирование.
•• Изменяемые объекты передаются «по указателю». Такие объекты, как списки и словари, также передаются в виде ссылок на объекты, что очень похоже на то, как в языке C передаются указатели на массивы, – изменяемые объекты допускают возможность непосредственного изменения внутри функции так же, как и массивы в языке C.
Как избежать воздействий на изменяемые аргументы (p. 508)¶
В языке Python аргументы по умолчанию передаются в функции по ссылкам (то есть по указателям), потому что в большинстве случаев именно это и требуется.
В действительности, как мы увидим в шестой части книги, модель классов в языке Python в значительной степени опирается на возможность изменения объектом собственного состояния с помощью аргумента «self».
Однако если нам требуется избежать влияния непосредственных изменений объектов, производимых в функциях, на вызывающую программу, мы можем просто явно копировать изменяемые объекты, как описывалось в главе 6 (ранее в этом посте).
Синтаксис Местоположение Интерпретация¶
func(value) Вызывающая программа Обычный аргумент: сопоставление производится по позиции
func(name=value)Вызывающая программа Именованный аргумент: сопоставление производится по указанному имени
func(*sequence) Вызывающая программа Все объекты в указанной последовательности передаются как отдельные позиционные аргументы
func(**dict) Вызывающая программа Все пары ключ/значение в указанном словаре передаются как отдельные именованные аргументы
def func(name) Функция Обычный аргумент: сопоставление производится по позиции или по имени
def func(name=value) Функция Значение аргумента по умолчанию, на случай, если аргумент не передается функции
def func(*name) Функция Определяет и объединяет все дополнительные аргументы в кортеж
def func(**name) Функция Определяет и объединяет все дополнительные именованные аргументы в словарь
def func(*args, name) Функция Аргументы, которые должны передаваться функции только по именам (3.0)
def func(*, name=value) Функция Аргументы, которые должны передаваться функции только по именам (3.0)
Действия, которые выполняет интерпретатор при сопоставлении аргументов перед присваиванием, грубо можно описать так:¶
1. Сопоставление неименованных аргументов по позициям.
2. Сопоставление именованных аргументов по именам.
3. Сопоставление дополнительных неименованных аргументов с кортежем *name.
4. Сопоставление дополнительных именованных аргументов со словарем **name.
5. Сопоставление значений по умолчанию с отсутствующими именованными аргументами.
После этого интерпретатор убеждается, что каждому аргументу соответствует только одно значение, – в противном случае возбуждается исключение. По окончании сопоставления всех аргументов интерпретатор связывает имена аргументов с полученными объектами.
Сбор аргументов в коллекцию¶
def f(*args): print(args)
f(1,2,3,4)
def fd(**args): print(args)
fd(a=1, b=2)
def fmix(a, *pargs, **kargs): print(a, pargs, kargs)
fmix(1, 2, 3, x=1, y=2)
Извлечение аргументов из коллекции¶
def func(a, b, c, d): print(a, b, c, d)
args = (1, 2)
>>> args += (3, 4)
>>> func(*args)
>>> argsd = {'a': 1, 'b': 2, 'c': 3}
>>> argsd['d'] = 4
>>> func(**argsd)
>>> argsd = {'a': 1, 'b': 2, 'c': 3}
# argsd['d'] = 4
>>> func(**argsd)
>>> func(*(1, 2), **{'d': 4, 'c':4})
Такого рода программный код удобно использовать, когда заранее невозможно предсказать число аргументов, которые могут быть переданы функции – вы можете собирать коллекцию аргументов во время исполнения и вызывать функцию таким способом.
Не путайте синтаксические конструкции /* в заголовках функций и в их вызовах – в заголовке они означают сбор всех лишних аргументов в коллекцию, а в вызове выполняют распаковку коллекций в отдельные аргументы
Как мы видели в главе 14, форма *pargs в вызовах функций является одной из разновидностей итерационного контекста, поэтому с технической точки зрения в таком виде допускается передавать не только кортежи или последовательности других типов, но и любые итерируемые объекты, как показано в этих примерах. Например, после символа * можно передать объект файла, и он будет распакован в отдельные аргументы (например:
func(*open(‘fname’))
Небольшой пример¶
def tracer(func, *pargs, **kargs): # Принимает произвольные аргументы
print('calling:', func.__name__)
return func(*pargs, **kargs) # Передает все полученные аргументы
def func(a, b, c, d):
return a + b + c + d
print(tracer(func, 1, 2, c=3, d=4))
Посты чуть ниже также могут вас заинтересовать
Комментариев нет:
Отправить комментарий