开发者

Python - copy by reference

Is there any possibility to copy variable by reference no matter if its int or class instance?

My goal is to have two lists of the same objects and when one changes, change is visible in second.

In other words i need pointers:/


I simply want int, float and other standard types which are normally copied by value, force to copy by reference. This will make my code more consistent.

If there is 开发者_Python百科no such possibility, class wrapper is the best solution.


Python always works by reference, unless you explicitly ask for a copy (a slice of a built-in list is deemed to "ask for a copy" -- but a slice of a numpy array also works by reference). However, exactly because of that, alist=anotherlist; alist.sort() means the single list objects (with two equivalent names alist and anotherlist) gets sorted -- you can't maintain two different orderings at the same time on the same list object.

So, in this case, you must explicitly ask for a copy (e.g. alist=list(anotherlist)) -- and once you've done that there is no more connection between the two distinct list objects. You can't have it both ways: either you work by reference (and have a single list object and thus a single ordering!), or you make a copy (in which case you end up with two separate list objects).

You could take advantage of the fact that the copies discussed so far are shallow -- the objects (items) that the two lists refer to are the same... until and unless you perform removals, additions, or reassignments of items on either list (mutation of mutable items on the other hand don't alter this connection: it's a completely separate and drastically different situation from any of the above, since removals, additions and reassignments are operation on the list, while calling a mutating method on an item is an operation on the item -- items are oblivious to any operation on one or more lists referring to them, lists are oblivious to any operation on one or more of the items they refer to).

There's not much you can do about removals and additions, except keeping two lists wrapped and synced up in a single object as suggested in other answers; but for reassignments of items, if that's all you require, you could turn those into mutation of items by adding one level of indirection -- instead of having a list directly referring to the items, have it refer e.g. to one-item sublists. For example:

>>> 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']]

Whether this concept of an extra indirection level can help at all with what you're exactly trying to do, only you can tell, since we don't really know what it is that you are trying to do;-).


You can wrap you immutable objects in a class:

class MutableWrapper(object):

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

a = MutableWrapper(10)
b = a
a.value = 20
assert b.value == 20


(edited to show example of dereferencing to same memory location)

Luper Rouch's approach is spot-on when dealing with lists of mixed-types. Just wrap the immutable types with containers.

If you really insist on C-style arrays where the elements are constrained to being a single type (array of ints, array of chars, etc), you could use the ctypes module. It'll give you access to c data types and pointers in addition to an FFI to use DLLs.

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

Result:

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


I am not sure what API you must provide. It's possible you want something like

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]

Note that the third-party package blist provides a more efficient sorted list type than using the bisect module with the built-in list type can provide. The operation of this class might also be better served by using a database, such as the one accessed by the built-in sqlite3 module.


There is probably a more elegant, pythonic way to handle this than using pointers. Could you provide a little more context into what you are trying to do.

Based on what you've given so far, I would subclass the built-in list type and have it store an alternative version of itself. Override the list methods to operate on itself, and the alternative version of itself where it makes sense. Where it doesn't make sense, like in the sort() function, define a second function for the alternate list.

This only really makes sense if the sort is unreasonably expensive; otherwise, I would just maintain one list and sort on demand.

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜