Взгляд на структуру языка программирования Python, «Все является объектом».

Введение

В неформальном смысле в Python мы делаем что-то с вещами. «Вещи» принимают форму таких операций, как сложение и конкатенация…, а «вещи» относятся к объектам, над которыми мы выполняем эти операции. Несколько более формально в Python данные принимают форму объектов, либо встроенных объектов, предоставляемых Python, либо объектов, которые мы создаем с помощью классов Python или инструментов внешнего языка, таких как библиотеки расширений C.

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

Идентификатор и тип

В Python все является объектом: каждое целое число, строка, список и функция.

Когда мы присваиваем значение переменной, мы фактически привязываем имя к объекту.

Одним из следствий этого является то, что несколько имен могут быть привязаны к одному объекту. Вот конкретный пример этого:

# <name> = <object>
>>> a = "Hello"
>>> b = "Hello"
>>> print(id(a))
1400...80
>>> print(id(b))
1400...80
>>> print(a is b)
True

Функция id возвращает фактическое место в памяти, где хранится переменная.

Начиная с id(a) = id(b), мы знаем, что a и b оба указывают на одну переменную, которая находится в одной ячейке памяти.

Такое поведение возникает из-за того, что строки являются неизменяемыми, и поэтому диспетчер памяти python оптимизирует ресурсы, создавая два имени, которые ссылаются на одно и то же строковое значение, ссылаясь на один и тот же объект. Это то, что мы подразумеваем под «несколько имен, связанных с одним объектом». В более формальном смысле это называется Общая ссылка, что означает, что на один объект могут ссылаться несколько переменных.

Если вы использовали языки более низкого уровня, такие как C или C++, вы знаете, что большая часть вашей работы сосредоточена на реализации объектов, также известных как структуры данных, для представления компонентов в предметной области вашего приложения. Вам нужно размещать структуры памяти, управлять распределением памяти, реализовывать процедуры поиска и доступа и так далее.

Эти рутинные действия настолько же утомительны и подвержены ошибкам, насколько могут показаться, и обычно они отвлекают от реальных целей вашей программы. В типичных программах на Python большая часть этой рутинной работы уходит. Потому что Python предоставляет мощные типы объектов как неотъемлемую часть языка.

print(type(4))
>> <class 'int'>
print(type(4.2))
>> <class 'float'>
print(type("3"))
>> <class 'str'>
print(type( [3, 4, 5] ))
>> <class 'list'>
print(type( (3, 4, 5) ))
>> <class 'tuple'>

Функция type либо возвращает тип объекта, либо возвращает объект нового типа на основе переданных аргументов.

неизменность

После создания и инициализации объекта его нельзя изменить.

Каждый объект в Python классифицируется как неизменяемый (неизменяемый) или нет. С точки зрения основного типа числа, строки, фиксированные множества и кортежи являются неизменяемыми. Этот тип объекта не поддерживает изменения на месте, единственные операции, разрешенные для неизменяемых объектов, — это вызов методов доступа «методы, используемые для получения информации об объекте»,копирование объектов или их передача вокруг. хотя мы всегда можем запускать выражения для создания новых объектов и присваивать их результаты переменным по мере необходимости.

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

>> S = 'Spam'
>> S[0] = 'z' # Immutable objects cannot be changed 
...error text omitted... 
TypeError: 'str' object does not support item assignment
>> S = 'z' + S[1:] # An expression to make new object 
>> S 'zpam'

Изменчивость

И наоборот, изменяемые типы (списки, словари, наборы, массивы байтов) всегда можно изменить на месте с помощью операций, которые не создают новых объектов.

Для объектов, которые поддерживают такие изменения на месте, вам нужно больше знать об общих ссылках, поскольку изменение одного имени может повлиять на другие. В противном случае может показаться, что ваши объекты меняются без видимой причины.

Чтобы проиллюстрировать это, давайте взглянем на список объектов.

>>> L1 = [2, 3, 4]
>>> L2 = L1

L1 здесь... — это список, содержащий объекты 2, 3 и 4.

Доступ к элементам внутри списка осуществляется по их позициям, поэтому L1[0] относится к объекту 2, который является первым элементом в списке L1.

После выполнения двух предыдущих назначений L1 и L2 ссылаются на один и тот же общий объект (так же, как a и b на изображении ниже).

Теперь давайте расширим это взаимодействие, чтобы сказать следующее:

>>> L1 = 24

Это присваивание просто устанавливает L1 для другого объекта, а L2 по-прежнему ссылается на исходный список. Но если мы немного изменим синтаксис этого оператора, эффект будет совершенно другим:

>> L1 = [2, 3, 4]  # A mutable object 
>> L2 = L1         # Make a reference to the same object 
>> L1[0] = 24      # in-place change 
>>> L1             # L1 is different [24, 3, 4] 
>> L2              # But so is L2! [24, 3, 4]

Здесь мы не изменили сам L1, мы изменили компонент объекта, на который ссылается L1. Такое изменение перезаписывает часть значения объекта списка на месте, потому что объект списка используется другими переменными. Вот почему вы должны знать, что когда вы вносите такие изменения, они могут повлиять на другие части вашей программы.

Как вы видите в этом примере, эффект проявляется и в L2, потому что он ссылается на тот же объект, что и L1.

Такое поведение происходит только для изменяемых объектов, которые поддерживают изменения на месте, и обычно это то, что вам нужно, но вы должны знать, как это работает, чтобы это ожидалось. Если вы не хотите такого поведения, вы можете попросить Python копировать объекты вместо создания ссылок.

Изменчивость против неизменности

Вы можете подумать, что изменчивость кажется безобидной темой, но при написании эффективной программы важно ее понимать. Например, следующий код представляет собой простое решение для объединения строк:

string_build = ""
for data in container:
    string_build += str(data)

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

Python обрабатывает изменяемые и неизменяемые объекты по-разному. Доступ к неизменяемым объектам быстрее, чем к изменяемым объектам. Кроме того, неизменяемые объекты принципиально дороги для «изменения», потому что это требует создания копии, а изменение изменяемых объектов обходится дешево.

Изменяемые объекты отлично подходят для эффективной передачи данных. Допустим, объекты X и Y имеют доступ к одному и тому же list object. X добавляет “something” в список, и Y автоматически получает доступ к этой информации. Но если бы оба использовали кортежи, то Х должен был бы скопировать записи своего кортежа, добавить новый элемент, создать новый кортеж, затем отправьте это Y. Даже если оба могут говорить напрямую, это много работы.

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

В общем, когда что-то может измениться, изменчивые объекты гораздо проще.

Механизм параметров Python

В C и других языках значения передаются функциям двумя широко известными концепциями «Передача по значению» и «Передача по ссылке». Но в Python ни одна из этих двух концепций не применима.

Python использует механизм создания параметров, известный как «Call-by-Object». Передача переменных, которые ссылаются на изменяемые/неизменяемые объекты, в функцию фактически передает ссылку на объект в качестве параметров.

  • Неизменяемые аргументы эффективно передаются «по значению». Такие объекты, как целые числа и строки, передаются по ссылке на объект вместо копирования, но поскольку вы никак не можете изменить неизменяемые объекты на месте, эффект очень похож на создание копии.
  • Изменяемые аргументы эффективно передаются «по указателю». Такие объекты, как списки и словари, также передаются по ссылке на объект, что похоже на то, как C передает массивы в качестве указателей.