вызов Python по ссылке на примитивных типах - PullRequest
1 голос
/ 28 июня 2011

У меня есть опыт работы с императивными языками (C чаще всего), и, вероятно, поэтому я запутался в изучении Python.

Я пытаюсь реализовать простые побитовые функции установки флага. Так что в C это будет выглядеть так:

void set_state(int *state, int var, int set)
{
    (set)? *state |= var : *state &= ~var
}

int is_state(int *state, int var)
{
    return (*state & var > 0)
}

int * состояние относится к флагу состояния, который я отслеживаю, а var - это флаг, который я хотел бы установить или очистить (определяется с помощью int set).

Поэтому я попытался сделать то же самое с Python 3.2 и ...

def setState(nState, nVar, bSet):
    if bSet:
        nState |= nVar
    else:
        nState &= ~var
    print(nState)

Тогда я побежал ...

>>> state
1024
>>> nfirst
1
>>> nsecond
2
>>> setState(state, nfirst, True)
1025
>>> state
1024

отладчик говорит мне, что значение 'state' было скопировано в nState, а теперь nState мутировал сам (то есть он был изменен в своей локальной области видимости). Разве Python не все об объектах и ​​все-это-по-ссылке?

Если так, то почему я не вижу побочный эффект?

Я потерян. Может кто-нибудь объяснить, что здесь происходит.

Ответы [ 3 ]

7 голосов
/ 28 июня 2011

Python int s являются неизменяемыми, поэтому, пока передается ссылка, невозможно изменить значение объекта int. Как вы уже видели, локальная переменная является копией ссылки, поэтому они оба связаны с одним целым числом. Однако при изменении значения локальной переменной создается новый целочисленный объект, и локальная переменная возвращается к новому объекту.

например.

>>> def setState(nState, nVar, bSet):
...     print((nState, id(nState)))
...     if bSet:
...         nState |= nVar
...     else:
...         nState &= ~var
...     print((nState, id(nState)))
... 
>>> nState = 1024
>>> nState, id(nState)
(1024, 3077840368)
>>> setState(nState, 1, True)
(1024, 3077840368)          # local nState is bound to the same object
(1025, 3077840448)          # now the local nState is bound to the new `int` object

возможно, вы можете вернуть nState из функции и присвоить его обратно, например,

>>> def setState(nState, nVar, bSet):
...     if bSet:
...         nState |= nVar
...     else:
...         nState &= ~var
...     return nState
... 
>>> nState = 1024
>>> nState = setState(nState, 1, True)
>>> nState
1025
4 голосов
/ 28 июня 2011

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

1 голос
/ 28 июня 2011

http://mail.python.org/pipermail/tutor/2002-November/018828.html в значительной степени отвечает на ваш вопрос.

Мы можем рассматривать все "имена переменных" в Python как указатели на объекты.Фактически, это в значительной степени то, что происходит.
[...]
Ключевым моментом здесь является то, что арифметика в Python (или любая манипуляция с «неизменяемым» типом данных) не изменяет объект, хотя:он динамически создает новые объекты:

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

...