Python - копия по ссылке - PullRequest
       12

Python - копия по ссылке

5 голосов
/ 06 мая 2010

Есть ли возможность скопировать переменную по ссылке, независимо от того, является ли она экземпляром типа int или class?

Моя цель состоит в том, чтобы иметь два списка одинаковых объектов, и когда один из них меняется, изменение видимо через секунду.

Другими словами, мне нужны указатели: /


Я просто хочу int, float и другие стандартные типы, которые обычно копируются по значению, принудительно копировать по ссылке. Это сделает мой код более последовательным.

Если такой возможности нет, лучшим решением будет оболочка класса.

Ответы [ 5 ]

8 голосов
/ 07 мая 2010

Python всегда работает по ссылке, если вы явно не запросите копию (фрагмент встроенного списка считается «запросить копию», но фрагмент массива также работает по ссылке). Однако именно из-за этого alist=anotherlist; alist.sort() означает сортировку объектов одного списка (с двумя эквивалентными именами alist и anotherlist) - вы не можете поддерживать два разных порядка в одном и том же объекте списка одновременно.

Итак, в этом случае вы должны явно запросить копию (например, alist=list(anotherlist)) - и как только вы это сделаете, между двумя различными объектами списка больше не будет связи. У вас не может быть обоих способов: либо вы работаете по ссылке (и у вас есть один объект списка и, следовательно, один порядок!), Либо вы делаете копию (в этом случае вы получаете два отдельных объекта списка).

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

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

>>> alist = list([x] for x in 'ciao')
>>> blist = list(alist)
>>> blist.sort()
>>> alist
[['c'], ['i'], ['a'], ['o']]
>>> blist
[['a'], ['c'], ['i'], ['o']]
>>> blist[-1][0] = 'z'
>>> blist
[['a'], ['c'], ['i'], ['z']]
>>> alist
[['c'], ['i'], ['a'], ['z']]

Может ли эта концепция дополнительного уровня косвенности вообще помочь с тем, что вы точно пытаетесь сделать, только вы можете сказать, поскольку мы на самом деле не знаем , что вы 1033 * пытаются сделать; -).

8 голосов
/ 06 мая 2010

Вы можете обернуть свои неизменные объекты в классе:

class MutableWrapper(object):

    def __init__(self, value):
        self.value = value

a = MutableWrapper(10)
b = a
a.value = 20
assert b.value == 20
0 голосов
/ 07 мая 2010

(отредактировано, чтобы показать пример разыменования в той же области памяти)

Подход Лупера Руча очевиден при работе со списками смешанных типов. Просто оберните неизменяемые типы контейнерами.

Если вы действительно настаиваете на массивах в стиле C, где элементы ограничены одним типом (массив целых, массив символов и т. Д.), Вы можете использовать модуль ctypes. Это даст вам доступ к c типам данных и указателям в дополнение к FFI для использования DLL.

from ctypes import *
containerTYPE = POINTER( c_uint ) * 10 #Array of pointers to UINTs of size 10
containerA = containerTYPE()
containerB = containerTYPE()

for i in range( 10 ):
    val = c_uint( i )
    containerA[ i ] = pointer( val ) 
    containerB[ -1 - i ] = pointer( val ) 

print "A\tB"
for i in range( 10 ):
    print containerA[ i ].contents.value, "\t", containerB[ i ].contents.value

for i in range( 10 ): #affects both arrays
    derefed = containerA[ i ].contents
    derefed.value = i * 2

print
print "A\tB"
for i in range( 10 ):
    print containerA[ i ].contents.value, "\t", containerB[ i ].contents.value

Результат:

A       B
0       9
1       8
2       7
3       6
4       5
5       4
6       3
7       2
8       1
9       0

A       B
0       18
2       16
4       14
6       12
8       10
10      8
12      6
14      4
16      2
18      0
0 голосов
/ 06 мая 2010

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

Исходя из того, что вы уже дали, я бы создал подкласс встроенного типа списка и сохранил бы его альтернативную версию. Переопределите методы списка, чтобы оперировать самим собой, и альтернативной версией самого себя, где это имеет смысл. Там, где это не имеет смысла, как в функции sort(), определите вторую функцию для альтернативного списка.

Это действительно имеет смысл, только если сортировка неоправданно дорога; в противном случае я бы просто вел один список и сортировал по требованию.

class MyList(list):

    def __init__(self, li):
        super(MyList, self).__init__(li)
        self.altlist = list(li)

    def append(self, x):
        super(MyList, self).append(x)
        self.altlist.append(x)

    def sortalt(self):
        ...

    ...
0 голосов
/ 06 мая 2010

Я не уверен, какой API вы должны предоставить. Возможно, вы хотите что-то вроде

import bisect

class DualLists(object):
    def __init__(self, iterable=[]):
        self.insertion_order = list(iterable)
        self.sorted = sorted(self.insertion_order)

    def append(self, item):
        self.insertion_order.append(item)
        bisect.insort(self.sorted, item)

>>> d = DualLists()
>>> d.append(4)
>>> d.append(6)
>>> d.append(1)
>>> d.insertion_order
[4, 6, 1]
>>> d.sorted
[1, 4, 6]

Обратите внимание, что сторонний пакет blist обеспечивает более эффективный тип отсортированного списка, чем может обеспечить модуль bisect со встроенным типом list. Операция этого класса также может быть лучше обслужена при использовании базы данных, такой как база данных, к которой обращается встроенный модуль sqlite3.

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