Определение ссылок в Python - PullRequest
2 голосов
/ 25 января 2012

Я только что потратил очень много времени на отладку проблемы в python с использованием фреймворка web.py, и у меня возник вопрос о том, как проверить подобные вещи в будущем.

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

Чтобы решить эту проблему, мне пришлось создать еще один словарь и явно преобразовать все значения в объекте хранения web.py в другой тип данных (я просто использовал строки, чтобы заставить его работать), чтобы он был вынужден создать новый объект.

В коде это то, что происходило:

>>> result = db.query(query_params)
>>> for row in result:
>>>     print row
...     result_list.append(row)
{"result" : "from", "row" : "one"}
{"result" : "from", "row" : "two"}
>>> print result_list
[{"result":"from", "row" : "two"}, {"result" : "from", "row" : "two}]

Это то, что подсказало мне тот факт, что это была какая-то справочная проблема. В списке хранилось местоположение «строки», а не ее копия.

Первое, что я попробовал, было что-то вроде:

 copy = {}
 for row in result:
    for value in row:
        copy[value] = row[value]
    result_list.append(copy)

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

            copy[value] = str(row[value])

Итак, мой вопрос двоякий, действительно:

  1. Есть ли простой способ узнать, как что-то хранится и / или передается?
  2. Есть ли способ явно запросить копию вместо ссылки?

Ответы [ 5 ]

3 голосов
/ 25 января 2012

Добро пожаловать в работу Python. Легко определить, какие объекты передаются по ссылке в Python: все они. Просто многие (целые числа, строки, кортежи) являются неизменяемыми, поэтому вы не замечаете, когда более одного имени указывает на них, так как вы не можете их изменить.

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

newlist = list(oldlist)
newdict = dict(olddict)

Списки также могут делать это со срезом, который вы часто видите:

newlist = oldlist[:]

В словарях есть метод copy():

newdict = olddict.copy()

Это «мелкие» копии: сделан новый контейнер, но предметы внутри по-прежнему являются исходными ссылками. Это может укусить вас, например, со списками списков. Существует модуль под названием copy, который содержит функции для копирования практически любого объекта и функцию deepcopy, которая также будет копировать содержащиеся объекты на любую глубину.

3 голосов
/ 25 января 2012

Использование copy. Это позволяет просто мелкое и глубокое копирование объектов в python:

import copy    
result_list.append(copy.copy(row))
1 голос
/ 25 января 2012

Я ничего не знаю о web.py, но, скорее всего, result_set.append(dict(row)) - это то, что вы искали.

См. Также: copy.copy() и copy.deepcopy()

0 голосов
/ 26 января 2012

Любой из двух должен работать:

result = db.query(query_params).list()
result = list(db.query(query_params))

db.query и db.select возвращают итератор, его необходимо преобразовать в список.

0 голосов
/ 25 января 2012

Что ж, если вам интересно узнать личность, вы всегда можете проверить, используя оператор is:

>>> help('is')

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

>>> mydict = {}
>>> for x in xrange(3):
...     d = mydict
...     print d is mydict
...
...
True
True
True

Независимо от того, сколько раз вы добавляете вещи в d или mydict, mydict всегда будет одним и тем же объектом.

Другие отметили, что вы должны использовать copy, но им не удалось решить вашу основную проблему - вы, похоже, не понимаете ссылочные типы.

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

>>> x = 3
>>> y = x
>>> x is y
True

Теперь x и y ссылаются на один и тот же объект (не только имеют одно и то же значение).

>>> x += 4

Поскольку 3 является целым числом и является неизменным, эта операция не изменяет значение, которое хранится в x, фактически она добавляет 3 и 4 и обнаруживает, что это приводит к значению 7, поэтому теперь x«указывает» на 7.

>>> y
3
>>> x
7
>>> x is y
False
>>> y = x
>>> x is y
True

С изменяемыми объектами вы можете изменить их на месте:

>>> mylist = [1,2,3]
>>> mylist[0] = 3
>>> mylist
[3, 2, 3]

Вам будет намного легче, если вы перестанете думать о переменных вPython хранит значения в переменных и вместо этого думает о них как о тегах имен - вы знаете, те, которые говорят «Здравствуйте, меня зовут»?

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

Вот пример:

>>> def spam_ref():
...     thing = []
...     count = 0
...     while count < 10:
...         count += 1
...         thing.append(count)
...         yield thing
...
>>> for a in spam_ref():
...  print a
...
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> spam = []
>>> for a in spam_ref():
...      spam.append(a)
...

Итак, если вы посмотрите на вывод цикла, вы бы подумали, что spam теперь содержит

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Ноэто не так:

>>> for a in spam:
...  print a
...
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

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

Для дальнейшего чтения, ознакомьтесь с Python Objects и вызов по объекту через effbot.

...