Почему изменение внутренней ссылки ведет себя непоследовательно изнутри наружу? - PullRequest
1 голос
/ 05 августа 2020
def foo(l):
   r.append(l)
   l[0], l[1] = l[1], l[0] 
   r.append(l)

r = []
l = [0, 1]
print(l);
foo(l)
print(l);
print(r);

распечатать:

[0, 1]
[1, 0]
[[1, 0], [1, 0]]

Насколько я знаю, python передает аргументы по присваиванию, в качестве новой ссылки на внешний [0, 1], внутренний l с таким свопом не создавать новый объект, а изменять объект, на который ссылается внутренний объект, так как внешний вывод l действует аналогично предполагаемому поведению.

Но пара r.append(l) кажется добавляет объект с тем же содержание каждый раз. Это из-за таких функций, как отложенная загрузка или что-то еще?

Конечно, это можно решить с помощью таких методов, как r.append(x for x in l), которые, на мой взгляд, добавляют новый объект списка в r. Но все же я хочу выяснить, каково поведение, определенное в python, когда мы добавляем элемент (то же самое ссылка на объект) в список, и что произошло, когда мы запрашиваем содержимое списка.

Ответы [ 2 ]

2 голосов
/ 05 августа 2020

Это хороший вопрос. Давайте разберемся, что происходит.

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

Давайте использовать id в вашем коде.

def foo(l):
   print("The ID of r in the function foo is {}".format(id(r)))
   print("The ID of l in the function foo is {}".format(id(l)))

   r.append(l)
   print("The ID of r in the function foo after the append is {}".format(id(r)))
   print("The ID of the the items in r: {}, after first append".format(id(r[0])))
   l[0], l[1] = l[1], l[0]
   print("The ID of l in the function foo after the swap is {}".format(id(l)))
 
   r.append(l)
   print("The ID of r in the function foo after second append is {}".format(id(r)))
   print("The ID of the the items in r: {}, {} after second append".format(id(r[0]), id(r[1])))


r = []
print("The ID of r in the beginning is {}".format(id(r)))
l = [0, 1]
print("The ID of l in the beginning is {}".format(id(l)))
print(l)
foo(l)
print(l)
print("The ID of l after the foo call is {}".format(id(l)))

print(r)
print("The ID of r after the foo call is {}".format(id(r)))
print("The ID of the the items in r: {}, {}".format(id(r[0]), id(r[1])))

Мой результат (ваш будет отличаться):

The ID of r in the beginning is 4374077736
The ID of l in the beginning is 4374122152
[0, 1]
The ID of r in the function foo is 4374077736
The ID of l in the function foo is 4374122152
The ID of r in the function foo after the append is 4374077736
The ID of the the items in r: 4374122152, after first append
The ID of l in the function foo after the swap is 4374122152
The ID of r in the function foo after second append is 4374077736
The ID of the the items in r: 4374122152, 4374122152 after second append
[1, 0]
The ID of l after the foo call is 4374122152
[[1, 0], [1, 0]]
The ID of r after the foo call is 4374077736
The ID of the the items in r: 4374122152, 4374122152

Это говорит нам о том, что здесь всегда есть только две переменные.

Даже внутри r. Вы добавляете один и тот же объект дважды. И этот объект также может быть изменен извне.

Таким образом, всякий раз, когда он изменяется, он также изменяется в вашем содержащем списке .

Каждый элемент в r указывает на то же l.

1 голос
/ 05 августа 2020

Для тех, кто не знаком с Python, я попытался написать понятный код на C ++:

#include <iostream>
#include <vector>

using namespace std;

class A {
public:
    void setValue(int value) {
        this->value = value;
    }

    int getValue() {
        return this->value;
    }
private:
    int value;
};

int main() {
    vector<A*> list;
    A* obj = new A();

    obj->setValue(1);
    list.push_back(obj);
    obj->setValue(2);
    list.push_back(obj);
    obj->setValue(3);
    list.push_back(obj);

    for (auto item : list) {
        cout << item->getValue() << endl;
    }
}

распечатывает:

3
3
3

Почти то же самое то, что было сделано с Python в вопросе.

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