Python - одна переменная равна другой переменной, когда она не должна - PullRequest
6 голосов
/ 05 ноября 2008

Вот мой пример кода. Предполагается, что это будет итерационная процедура для гаусса Зейделя (матричного решателя). По существу, когда ошибка достаточно мала, она выходит из цикла while.

i=1
while (i>0):
    x_past = x_present

    j=0
    while(j<3):
        value=0
        k=0
        while(k<3):
            if(k!=j):
                if(i==1):
                    if(k>j):
                        value=value+0
                    else:
                        value=value+x_present[k]*eqn[j][k]    
                else:
                    value=value+x_present[k]*eqn[j][k]
            else:
                value=value+eqn[j][k]
            k=k+1
        x_present[j:j+1]=[value]
        j=j+1
    print "X_PAST"
    print x_past
    print "X_PRESENT"
    print x_present    
    if(error(x_past, x_present)<10**-2):
        break;
    i=i+1

Я сократил код, чтобы он стал более управляемым. если вы не понимаете, что он делает, это не так важно для решения этой проблемы.

Вот проблема. Каждый раз

x_present[j:j+1]=[value]

выполняется, x_past сделан равным x_present. Я не знаю, почему это так, поскольку единственное место, где я установил x_past равным x_present, находится в верхней части цикла. Если я заберу

x_past=x_present

предложение, x_past никогда не делается равным x_present. Это заставляет меня думать, что именно эта комбинация двух утверждений вызывает проблему.

Это большая проблема, если x_past = x_present error = 0 каждый раз и цикл прерывается после первой итерации. Код работает, например, если я скажу, что код должен выполняться в течение 8 итераций, и перерыв дает мне ответ, который должен.

Я пытался понять это в течение последних 4 часов, и я полностью в замешательстве. Я не работал с Python долго, поэтому мои навыки устранения неполадок не очень хороши. Любая помощь будет принята с благодарностью !!

Ответы [ 6 ]

30 голосов
/ 05 ноября 2008

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

Вы ссылаетесь на список, поэтому при изменении списка любая ссылка на этот список будет отражать это изменение. Для демонстрации:

>>> x_present = [4,5,6]
>>>
>>> x_past = x_present
>>>
>>> x_past
[4, 5, 6]
>>>
>>> x_present.append(7)
>>>
>>> x_past
[4, 5, 6, 7]
>>>

Если вам нужна копия списка, вы должны сделать это, listcopy = mylist [:] .

>>> x_past = x_present[:]
>>> x_past
[4, 5, 6, 7]
>>>
>>> x_present.append(8)
>>>
>>> x_past
[4, 5, 6, 7]
4 голосов
/ 05 ноября 2008

Что такое x_past и x_present? Я не знаю много Python, но с точки зрения .NET / Java, если они ссылаются на какую-то структуру данных (карту или что-то еще), то создание ссылок на один и тот же объект (как вы делаете в начале) что любые изменения, сделанные через одну переменную, будут видны через другую. Похоже, вам нужно взять копию структуры данных вместо того, чтобы просто делать ссылки. Имеет ли структура данных, с которой вы работаете, какие-либо функции клонирования?

Как я уже сказал, я немного знаю Python, так что это может быть совершенно неправильно ...

3 голосов
/ 05 ноября 2008

Как указали другие, ответ должен заменить: x_past = x_present на x_past = x_present[:]. В общем, вы можете использовать модуль copy для копирования объекта в Python.

>>> import copy
>>> a = range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b = a
>>> a += 10, 11
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> c = copy.copy(a) # shallow copy
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> del a[3:]
>>> a
[0, 1, 2]
>>> b
[0, 1, 2]
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

Ваш код не пифоничен, если не сказать больше.

Его можно заменить на что-то вроде следующего кода:

import copy
# assert(len(x_present) >= len(eqn))

first = True
while True:
    x_past = copy.copy(x_present) # copy

    for j, eqj in enumerate(eqn):
        x_present[j] = sum(x_present[k] * eqj[k] 
                           for k in range(j if first else len(eqj)) 
                           if k != j)
        x_present[j] += eqj[j] 

    print "X_PAST\n%s\nX_PRESENT\n%s" % (x_past, x_present)
    if allequal(x_past, x_present, tolerance=10**-2):
        break
    first = False

Вот определение allequal() (используется абсолютная ошибка. В вашем случае это может быть или не быть хорошей идеей (вместо этого вы можете использовать относительную ошибку)):

def allequal(x, y, tolerance):
    return (len(x) == len(y) and 
            all(-tolerance < (xx - yy) < tolerance
                for xx, yy in zip(x, y)))
1 голос
/ 05 ноября 2008

В Python все является объектом. Таким образом, утверждение x_past = x_present указывает на одну и ту же ссылку.

0 голосов
/ 05 ноября 2008

попробуйте изменить строку x_past = x_present на x_past = [x for x in x_present] и посмотрите, поможет ли это.

сокращенная копия списка - моя любимая особенность Python, так как я могу сделать однострочники, которые невозможны на других языках:

greaterthan100 = [x for x in number if x > 100]

notinblacklist = [x for x in mylist if x not in blacklist]

firstchildofbigfamily = [x.child[0] for x in familylist if len(x.child) > 10]

0 голосов
/ 05 ноября 2008

Похоже, что x_present является списком. Я подозреваю, что это означает, что присвоение x_last = x_present превращает x_last в псевдоним, то есть они ссылаются на одну и ту же переменную. Может быть, это так?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...