2016-06-11 1 views
4

Например:Что делает CPython на самом деле, когда «=» выполняется для переменных примитивного типа?

a = some_process_that_generates_integer_result() 
b = a 

Кто-то сказал мне, что б и будет указывать на тот же кусок целого объекта, таким образом, б бы изменить счетчик ссылок объекта. Код выполняется в функции PyObject* ast2obj_expr(void* _o) в Python-ast.c:

static PyObject* ast2obj_object(void *o) 
{ 
    if (!o) 
     o = Py_None; 
    Py_INCREF((PyObject*)o); 
    return (PyObject*)o; 
} 

...... 

case Num_kind: 
    result = PyType_GenericNew(Num_type, NULL, NULL); 
    if (!result) goto failed; 
    value = ast2obj_object(o->v.Num.n); 
    if (!value) goto failed; 
    if (PyObject_SetAttrString(result, "n", value) == -1) 
      goto failed; 
    Py_DECREF(value); 
    break; 

Однако, я думаю, что изменения счетчика ссылок без изменения собственности действительно бесполезно. Я ожидаю, что каждая переменная, содержащая примитивные значения (float, integers и т. Д.), Всегда имеет собственное значение, а не ссылается на один и тот же объект.

И в исполнении моего простого тестового кода, я нашел точку останова в указанной выше Num_kind ветви никогда не достигается:

def some_function(x, y): 
    return (x+y)*(x-y) 

a = some_function(666666,66666) 
print a 

b = a 
print a 
print b 

b = a + 999999 
print a 
print b 

b = a 
print a 
print b 

Я использую программу python2.7-Dbg предоставленный Debian. Я уверен, что программа и исходный код совпадают, потому что многие другие точки останова работают правильно.

Итак, что на самом деле делает CPython для объектов примитивного типа?

+0

Я не уверен, что вы просите здесь. О какой точке разрыва вы обращаетесь? Какое поведение вы ожидали от этого кода и как оно не соответствовало вашим ожиданиям? – poke

+0

@poke Добавлено отсутствующее описание. Точка останова находится в ветке Num_kind в вышеприведенном C-коде, так как кто-то сказал мне, что часть кода относится к числовому копированию с числовым копированием во время выполнения. – jiandingzhe

+1

"каждая переменная, содержащая примитивные значения". Python фактически не имеет переменных _have_, которые содержат значения. Вместо этого он имеет строго типизированные объекты, которые могут быть привязаны к именам. Это сильно отличается от модели данных в стиле C, и попытка понять модель данных Python в терминах C редко бывает плодотворной, если только вы не взаимодействуете с кодом C. Вы можете найти эту статью полезной: [Факты и мифы о именах и значениях Python] (http://nedbatchelder.com/text/names.html), который был написан ветеран SO Нед Батчелдер. –

ответ

7

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

a = some_process_that_generates_integer_result() 
b = a 

В Python присвоения всегда эталонные копии. Так что независимо от функции возвращается, ее ссылка скопирована в переменную a. А затем во второй строке, ссылка снова скопирована в переменную b. Таким образом, обе переменные будут ссылаться на один и тот же объект.

Вы можете легко проверить это с помощью функции id() которая сообщит вам идентификатор объекта:

print id(a) 
print id(b) 

Это будет печатать один и тот же идентификационный номер дважды. Обратите внимание, что, если вы сделаете именно это, вы скопировали ссылку еще два раза: это не переменные, которые передали функциям, но копиям ссылок.

Это отличается от других языков, где вы часто дифференцируют между «вызов по значению» и «вызов по ссылке». Первое означает, что вы создаете копию значения и передаете его функции, а это означает, что для этого значения выделяется новая память; последнее означает, что фактическая ссылка передается и изменения этой ссылки также влияют на исходную переменную.

Что такое Python часто называют «вызов по назначению»: каждый вызов функции, в котором вы передаете аргументы, по существу является назначением в новые переменные (которые затем доступны для функции). И присваивание копирует ссылку.

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

То же самое происходит и с другими неизменяемыми объектами, например с строками. Каждая операция, которая дает измененную строку, дает вам другой строковый объект.

Однако задания с изменяемыми объектами одинаковы. Просто изменение стоимости этих объектов возможно, поэтому они выглядят разными. Рассмотрим следующий пример:

a = [1] # creates a new list object 
b = a # copies the reference to that same list object 
c = [2] # creates a new list object 
b = a + C# concats the two lists and creates a new list object 
d = b 
# at this point, we have *three* list objects 
d.append(3) # mutates the list object 
print(d) 
print(b) # same result since b and d reference the same list object 

Теперь, возвращаясь к вашему вопросу, и код C вы процитировать там, вы на самом деле, глядя на ту часть CPython, чтобы получить объяснение там. AST - это абстрактное синтаксическое дерево, созданное парсером при анализе файла. Он отражает структуру синтаксиса программы, но ничего не говорит о фактическом поведении во время выполнения.

Код, который вы указали для Num_kind, отвечает за создание объектов Num AST. Вы можете получить представление об этом при использовании ast module:

>>> import ast 
>>> doc = ast.parse('foo = 5') 

# the document contains an assignment 
>>> doc.body[0] 
<_ast.Assign object at 0x0000000002322278> 

# the target of that assignment has the id `foo` 
>>> doc.body[0].targets[0].id 
'foo' 

# and the value of that assignment is the `Num` object that was 
# created in that C code, with that `n` property containing the value 
>>> doc.body[0].value 
<_ast.Num object at 0x00000000023224E0> 
>>> doc.body[0].value.n 
5 

Если вы хотите, чтобы получить представление о реальной оценке кода на Python, вы должны сначала посмотреть на байт-код. Байт-код - это то, что выполняется во время выполнения виртуальной машиной. Вы можете использовать dis module увидеть байт-код для кода Python:

>>> def test(): 
     foo = 5 

>>> import dis 
>>> dis.dis(test) 
    2   0 LOAD_CONST    1 (5) 
       3 STORE_FAST    0 (foo) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 

Как вы можете видеть, есть две основные инструкции байт-код здесь: LOAD_CONST и STORE_FAST. LOAD_CONST просто загрузит постоянное значение в стек оценки. В этом примере мы просто загружаем постоянное число, но мы также можем загрузить значение из вызова функции (попробуйте сыграть с модулем dis, чтобы выяснить, как он работает).

Само назначение выполнено с использованием STORE_FAST. Интерпретатор байт-код делает the following для этой команды:

TARGET(STORE_FAST) 
{ 
    v = POP(); 
    SETLOCAL(oparg, v); 
    FAST_DISPATCH(); 
} 

Так что по сути получает значение (ссылку на целочисленном объект) из стека, а затем вызывает SETLOCAL, который по существу будет просто присвоить значение локальных переменным.

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

TARGET(LOAD_CONST) 
{ 
    x = GETITEM(consts, oparg); 
    Py_INCREF(x); 
    PUSH(x); 
    FAST_DISPATCH(); 
} 

Так ТЛ; др: Задания в Python всегда ссылаться на копии.Ссылки также копируются всякий раз, когда используется значение (но во многих других ситуациях скопированная ссылка существует только на короткое время). AST отвечает за создание объектного представления анализируемых программ (только синтаксис), в то время как интерпретатор байтового кода запускает ранее скомпилированный байт-код для выполнения фактических данных во время выполнения и обработки реальных объектов.

Смежные вопросы