简体   繁体   中英

How can I get the reference of a List element?

Is it possible to get the reference of one element out of a list?

I know how to reference a complete list to another list:

a = [1,2,3]
b = []

b = a

a[1] = 3

print (b) #b is now [1, 3, 3]

But how can I get the reference to one element? For example:

a = [1,2,3]
b = []

b = a[1] 

a[1] = 3

print (b) #b is now 2, but I want the value 3 like it is in a[1]

Or is there another solution for this problem in python?

It's not possible, because integers are immutable, while list are mutable.

In b = a[1] you are actually assigning a new value to b

Demo:

>>> a = 2
>>> id(a)
38666560
>>> a += 2
>>> id(a)
38666512

You can like this,

>>> a = [1,2,3]
>>> b = a
>>> a[1] = 3
>>> b
[1, 3, 3]
>>> a
[1, 3, 3]
>>> id(a)
140554771954576
>>> id(b)
140554771954576

You can read this document.

As jonrsharpe said, you can do what you want by using mutable elements in your list, eg make the list elements lists themselves.

For example

a = [[i] for i in xrange(5)]
print a

b = a[3]
print b[0]


a[3][0] = 42

print a
print b[0]


b[:] = [23]

print a
print b

output

[[0], [1], [2], [3], [4]]
3
[[0], [1], [2], [42], [4]]
42
[[0], [1], [2], [23], [4]]
[23]

Python is an object oriented language with first class functions. It doesn't deal with pointers in the way C does. List entries are not objects in themselves, so you'd need to hold both an item index and a reference to the container to do the sort of slot handling you describe. There are packages that do this, for instance buffers , memory views , or numpy ndarrays , but it requires a layer of indirection because an assignment of the form foo = something will bind the name foo, not modify what foo used to refer to.

Here is one example of such an indirection class:

class Indirect(object):
    def __init__(self, container, item):
        self.container = container
        self.item = item
    @property
    def value(self):
        return self.container[self.item]
    @value.setter
    def value(self, value):
        self.container[self.item] = value

l = [1,2,3]
ref = Indirect(l, 1)
ref.value = 5
print(l)

The first class function support means you can also create a function on the fly for the specific task you need:

l = [1,2,3]
def callwith5(func):    # doesn't know what func does
    return func(5)
setanitem = lambda i: l[1]=i    # creates a new function
callwith5(setanitem)
print(l)

Another way to express this difference is that Python doesn't really have lvalue in C terminology; we just have a few syntactic extensions that translate assignment statements into varying calls:

a = 1      # if a is global:  setitem(globals(), 'a', 1)
a = 1      # otherwise:       setitem(locals(), 'a', 1)
foo.a = 1  # setattr(foo, 'a', 1)  => foo.__setattr__('a', 1)
l[1] = 5   # setitem(l, 1, 5)      => l.__setitem__(1, 5)

Many of them are optimized such that there is no lookup of setitem, setattr etc, and those in turn have a set of rules that refer to specific methods like __setitem__ and __setattr__ , but in the end the rule is that everything is accessed via some object, with the roots being the module dictionaries.

In Python, everything is an object, including numbers. Numbers are immutable objects, and they exist only once. In other words in the above example, a does not hold the value two, it holds the reference to the Number object that represents the integer 2. This can be verified easily:

a = 2 # reference to the object that represents integer 2
print type(2), type(a), isinstance(2, Number), isinstance(a, Number)
=> <type 'int'> <type 'int'> True True

Similarly this is shown by looking at the identify of the object, which is returned by the id function:

a = 2
print id(2), id(a)
=> 4301263008 4301263008

We can now answer your question:

But how can I get the reference to one element?

You are already getting the reference to the element:

a = [1,2,3]
b = []
b = a[1] 
a[1] = 3
print (b) #b is now 2, but I want the value 3 like it is in a[1]
=> 2
print id(a[1]), id(b), id(2)
=> 4301262984 4301263008 4301263008

A Python list always stores object references. By setting a[1] = 3 you are in effect changing what the second list element references from id(2) to id(3). b continues to (correctly) hold the reference to object id(2), which is what you asked it to do.

Is there a way to achieve what you want, ie have b somehow point to the current value of a[1] ? Two options spring to mind:

  1. Store the index of the element, rather than its value:

     a = [1,2,3] b = [] b = a # reference the list b_i = 1 # reference the index a[1] = 3 # change value at index 1 print (b[b_i]) => 3 a[1] = 4 print (b[b_i]) => 4 
  2. In the list, store objects and change the value of those objects:

     class Element(int): def __init__(self, value): self.value = value def __str__(self): return "%s" % self.value def __repr__(self): return "%s" % self.value a = [Element(n) for n in [1,2,3]] print a => [1, 2, 3] b = a[1] a[1].value = 3 # don't change a[1]'s reference, change its value! print (b) => 3 print id(a[1]), id(b) # same object => 4372145648 4372145648 

You can't assign a reference from a variable to a list position. Unlike C++, in Python this is not possible. The closest you can go is to create an anonymous function to reference that single list index, and assign a new value to it:

>>> L = [1,2,3]
>>> def itemsetter(L, pos):
...     def inner(x):
...             L[pos] = x
...     return inner
... 
>>> f = itemsetter(L, 1)
>>> f
<function inner at 0x7f9e11fa35f0>
>>> f(9)
>>> L
[1, 9, 3]

This function resembles the way the stdlib function operator.attrgetter works.

The other closest alternative is to wrap each element on the list as a referenceable mutable object. For example:

>>> L = [1,2,3]
>>> L2 = [[x] for x in L]
>>> L2
[[1], [2], [3]]
>>> A = L2[1]
>>> A[0] = 9
>>> L2
[[1], [9], [3]]

Instead of a list, you may create your custom class:

>>> class wrap:
...     def __init__(self, value):
...             self.value = value
... 
>>> L3 = [wrap(x) for x in L]
>>> L3
[<__main__.wrap instance at 0x7feca2c92ab8>, <__main__.wrap instance at 0x7feca2c92b00>, <__main__.wrap instance at 0x7feca2c92b48>]
>>> A = L3[1]
>>> A.value = 9
>>> L3[1].value
9
>>> L3[0].value
1
>>> L3[2].value
3

a = b makes name a refering to object b - unlike in C / C++ it doesn't modify object to which a refered before .

>>> a = 2
>>> id(a)
505493912
>>> a = 3
>>> id(a)
505493928

Object to which a refered didn't changed; a started refering to different object.

Directly it isn't possible, but there are few workarounds, like this in the PM 2Ring's answer .

Yet another workaround:

def overload(*functions):
    return lambda *args, **kwargs: functions[len(args)](*args, **kwargs)

class Ref:
    def __init__(self, new_target):
        self.target=new_target
    def __repr__(self):
        return 'Ref(' + repr(self.target) + ')'
    __call__=overload(None, lambda self: self.target, __init__)

# Usage:

>>> a = [Ref(1), Ref(2), Ref(3)]
>>> b = a[1]
>>> print(b)
Ref(2)
>>> a[1](3)
>>> print(b)
Ref(3)
>>> print(b())
3

You can do what you want. Your original question involved integers, but your real problem doesn't. Asking questions about your real situation will get better answers.

You said,

I want to generate two lists of classes.

I think you mean objects. Then:

One "global" list with a user class and another list with an article class. Each article should have a "local" list of users, that are interested in this article. But this local list should only point to a subset of users of the "global" user list.

You can make a list of User objects:

class User(object):
    def __init__(self, name):
        self.name = name
        self.karma = 0

the_users = [User('Ned'), User('char0n'), User('Mithu')]

You can make a list of Article objects:

class Article(object):
    def __init__(self, title):
        self.title = title
        self.users = []

the_articles = [Article("How to cook"), Article("Latest News")]

You can associate users with articles:

the_articles[0].users.append(the_users[0])
the_articles[1].users.append(the_users[0])
the_articles[1].users.append(the_users[1])

Now the articles' users lists refer to the User objects in your user list. If you modify a user, it's visible through the article:

the_users[1].karma += 10
print sum(u.karma for u in the_articles[1].users)

it seems like you need just redefine b = a[1] when needed, or use a[1]. As said above, integers are immutable unlike lists-I won't reiterate what others said-and so will not work. I recommend just using a[1] where needed, or reassign b = a[1] for readability of a long line.

Answer to wrong questin below due to my misunderstanding:

You can use the * operator when defining b.

a = [1,2,3]
b = 1*a #my book says *a will work, but it creates an error in spyder when I tested it

b
>>> [1,2,3]

a[1] = 3

a
>>> [1,3,3]

b
>>> [1,2,3]

Source: my programming course

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM