How does the append method work in python?

I am learning programming languages ​​and the question and solution for the quiz is:

def foo(x):
   x.append (3)
   x = [8]
   return x

x=[1, 5]
y= foo(x)
print x
print y

      

Why does it print like this:

[1 5 3 ]
[8]

      

Why doesn't x equal 8 ??

+3


source to share


5 answers


The other two answers are great. I suggest you try to id

get the address.

See next example

def foo(x):
   x.append (3)
   print "global",id(x)
   x = [8]
   print "local ",id(x)
   return x

x=[1, 5]
print "global",id(x)
y= foo(x)
print "global",id(x)
print x
print y

      



And the conclusion

global 140646798391920
global 140646798391920
local  140646798392928
global 140646798391920
[1, 5, 3]
[8]

      

As you can see, the address of the variable x

remains the same when you manipulate it, but changes with use =

. Moving a variable within a function makes the variable local to the function

+4


source


You have a lot going on. So let go step by step.

x = [1,5]

You assign a list from 1.5 to x

y=foo(x)

You call foo and pass x and assign whatever is returned from foo



inside foo

you call x.append(3)

which adds 3 to the list that was passed in.

then set x = [8]

, which is now a reference to the local variable x, which is then returned from foo, ultimately settingy = [8]

+3


source


Object references

The key to understanding this is that Python passes variables around using object references. They are similar to pointers in a language like C ++, but differ in very key ways.

When assignment is performed (using the assignment operator, =

):

x = [1, 5]

      

Actually 2 things were created. First, it's the object itself, which is a list [1, 5]

. This object is a separate object from the second object, which is a (global) object reference to the object.

Object(s)       Object Reference(s)
[1, 5]          x (global)                    <--- New object created

      

In python, objects are passed to functions by object reference; they are not passed "by reference" or "by value" as in C ++. This means that when x

passed to a function foo

, a new local object reference to the created object is created.

Object(s)       Object Reference(s)
[1, 5]          x (global), x (local to foo)  <--- Now two object references

      

Now, internally, foo

we call x.append(3)

that directly modifies the object (called the reference to foo-local x object itself):

Object(s)       Object Reference(s)
[1, 5, 3]       x (global), x (local to foo)  <--- Still two object references

      

Then we do something else. We assign the object reference local-foo x

(or reassign since the object reference already existed previously) in the NEW LIST .

Object(s)       Object Reference(s)
[1, 5, 3]       x (global)                    <--- One object reference remains
[8]             x (local to foo)              <--- New object created

      

Please note that the global object reference x

still exists! He was not influenced. We just reassigned the local-foo object reference to a x

NEW list.

Finally, it should be clear that when the function returns, we have this:

Object(s)       Object Reference(s)
[1, 5, 3]       x (global)                    <--- Unchanged
[8]             y (global), x (local to foo)  <--- New object reference created

      

Side panel

Note that the reference to the local-foo object x

still exists! This is an important behavior to understand, because if we do something like this:

def f(a = []):
    a.append(1)
    return a
f()
f()
f()

      

We will not receive:

[1]
[1]
[1]

      

Instead, we get:

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

      

The statement a = []

is only evaluated by ONCE by the interpreter when the program is first run, and this object reference is never removed (unless we remove this function).

As a result, when called f()

, local-f a

does not fall back to []

. It "remembers" its previous meaning; this is because this local object reference is still valid (i.e., it has not been removed) and therefore the object does not get garbage collected between function calls.

Contrast with pointers

One way that object references differ from pointers is through assignment. If Python used actual pointers, you would expect behavior like:

a = 1
b = a
b = 2
assert a == 2

      

However, this statement throws an error. The reason is that it b = 2

does NOT affect the object that the object reference points to, b. It creates a NEW object ( 2

) and reassigns to b

that object.

In other ways, object references differ from pointers in terms of deletion:

a = 1
b = a
del a
assert b is None

      

This statement also raises an error. The reason is the same as in the example above; del a

No effects on the object to which the link points to an object b

. He simply removes the reference to the object a

. The object reference b

and the object it points to 1

are not affected.

You might be asking, "Well, then how do you delete the actual object?" Answer: YOU CAN'T! You can remove all references to this object. As soon as there are no more references to the object, the object becomes garbage collection and it will be deleted for you (although you can force this to happen with a module gc

). This feature is known as memory management and it is one of the main advantages of Python, and it is also one of the reasons why Python uses object references in the first place.

Variability

Another thing to understand is that there are two types of objects: mutable and immutable. Mutable objects can be modified, and immutable objects cannot be modified.

A list

, such as [1, 5]

, is mutable. A tuple

or int

, on the other hand, is immutable.

Method append

Now that this is all clear, we should be able to answer the question "How does append

Python work?" The result of the method append()

is an operation on the most mutable list object. list

changed "in place" so to speak. Created a new list

one and then assigned foo-local x

. This is in contrast to the assignment operator =

, which creates a NEW object and assigns that object to an object reference.

+2


source


The function append

changes x

which was passed to the function, whereas assigning something new to x

changed the local cloud range value and returned it.

+1


source


The scope x

inside is foo

function specific and independent of the main calling context. x

inside foo

starts to refer to the same object as x

in the main context, because the parameter that was passed, but the moment you use the assignment operator to set it to [8]

, you have allocated a new object, which x

inside foo

indicates that now completely different from x

in the main context. To illustrate further, try changing foo

to this:

def foo(x):
   print("Inside foo(): id(x) = " + str(id(x)))
   x.append (3)
   print("After appending: id(x) = " + str(id(x)))
   x = [8]
   print("After setting x to [8], id(x) = " + str(id(x)))
   return x

      

When I executed I got this output:

Inside foo(): id(x) = 4418625336
After appending: id(x) = 4418625336
After setting x to [8], id(x) = 4418719896
[1, 5, 3]
[8]

      

(the ids you see will be different, but the point will still be clear, hopefully)

You can see that it is append

simply mutating an existing object - no need to allocate a new one. But as soon as the statement =

executes, the new object is selected (and eventually returned to the main context where it is assigned y

).

+1


source







All Articles