4.15 Passing Arguments to Functions: A Deeper Look

Let’s take a closer look at how arguments are passed to functions. In many programming languages, there are two ways to pass arguments—pass-by-value and pass-by-reference (sometimes called call-by-value and call-by-reference, respectively):

  • With pass-by-value, the called function receives a copy of the argument’s value and works exclusively with that copy. Changes to the function’s copy do not affect the original variable’s value in the caller.

  • With pass-by-reference, the called function can access the argument’s value in the caller directly and modify the value if it’s mutable.

Python arguments are always passed by reference. Some people call this pass-by-object-reference, because “everything in Python is an object.”3 When a function call provides an argument, Python copies the argument object’s reference—not the object itself—into the corresponding parameter. This is important for performance. Functions often manipulate large objects—frequently copying them would consume large amounts of computer memory and significantly slow program performance.

Memory Addresses, References and “Pointers”

You interact with an object via a reference, which behind the scenes is that object’s address (or location) in the computer’s memory—sometimes called a “pointer” in other languages. After an assignment like

x = 7

the variable x does not actually contain the value 7. Rather, it contains a reference to an object containing 7 (and some other data we’ll discuss in later chapters) stored elsewhere in memory. You might say that x “points to” (that is, references) the object containing 7, as in the diagram below:

Diagram shows variable x with a dot in a square and an arrow points to an object which is a rectangle with a 7 in the rectangle.

Built-In Function id and Object Identities

Let’s consider how we pass arguments to functions. First, let’s create the integer variable x mentioned above—shortly we’ll use x as a function argument:

In [1]: x = 7

Now x refers to (or “points to”) the integer object containing 7. No two separate objects can reside at the same address in memory, so every object in memory has a unique address. Though we can’t see an object’s address, we can use the built-in id function to obtain a unique int value which identifies only that object while it remains in memory (you’ll likely get a different value when you run this on your computer):

In [2]: id(x)
Out[2]: 4350477840

The integer result of calling id is known as the object’s identity.4 No two objects in memory can have the same identity. We’ll use object identities to demonstrate that objects are passed by reference.

Passing an Object to a Function

Let’s define a cube function that displays its parameter’s identity, then returns the parameter’s value cubed:

In [3]: def cube(number):
   ...:     print('id(number):', id(number))
   ...:     return number ** 3
   ...:

Next, let’s call cube with the argument x, which refers to the integer object containing 7:

In [4]: cube(x)
id(number): 4350477840
Out[4]: 343

The identity displayed for cube’s parameter number4350477840—is the same as that displayed for x previously. Since every object has a unique identity, both the argument x and the parameter number refer to the same object while cube executes. So when function cube uses its parameter number in its calculation, it gets the value of number from the original object in the caller.

Testing Object Identities with the is Operator

You also can prove that the argument and the parameter refer to the same object with Python’s is operator, which returns True if its two operands have the same identity:

In [5]: def cube(number):
   ...:     print('number is x:', number is x) # x is a global variable
   ...:     return number ** 3
   ...:

In [6]: cube(x)
number is x: True
Out[6]: 343

Immutable Objects as Arguments

When a function receives as an argument a reference to an immutable (unmodifiable) object—such as an int, float, string or tuple—even though you have direct access to the original object in the caller, you cannot modify the original immutable object’s value. To prove this, first let’s have cube display id(number) before and after assigning a new object to the parameter number via an augmented assignment:

In [7]: def cube(number):
   ...:     print('id(number) before modifying number:', id(number))
   ...:     number **= 3
   ...:     print('id(number) after modifying number:', id(number))
   ...:     return number
   ...:
In [8]: cube(x)
id(number) before modifying number: 4350477840
id(number) after modifying number: 4396653744
Out[8]: 343

When we call cube(x), the first print statement shows that id(number) initially is the same as id(x) in snippet [2]. Numeric values are immutable, so the statement

number **= 3

actually creates a new object containing the cubed value, then assigns that object’s reference to parameter number. Recall that if there are no more references to the original object, it will be garbage collected. Function cube’s second print statement shows the new object’s identity. Object identities must be unique, so number must refer to a different object. To show that x was not modified, we display its value and identity again:

In [9]: print(f'x = {x}; id(x) = {id(x)}')
x = 7; id(x) = 4350477840

Mutable Objects as Arguments

In the next chapter, we’ll show that when a reference to a mutable object like a list is passed to a function, the function can modify the original object in the caller.

Self Check

  1. (Fill-In) The built-in function       returns an object’s unique identifier.
    Answer: id.

  2. (True/False) Attempts to modify mutable objects create new objects.
    Answer: False. This is true for immutable objects.

  3. (IPython Session) Create a variable width with the value 15.5, then show that modifying the variable creates a new object. Display width’s identity and value before and after modifying its value.
    Answer:

    In [1]: width = 15.5
    
    In [2]: print('id:', id(width), ' value:', width)
    id: 4397553776 value: 15.5
    
    In [3]: width = width * 3
    
    In [4]: print('id:', id(width), ' value:', width)
    id: 4397554208 value: 46.5
    
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.111.9