Simone's Creative Cooking Club • If You Haven't Got a Clue What "Pass by Value" or "Pass by Reference" Mean, Read On…
…and Python functions don't use either of them. Say hello to "Pass by Assignment"
Simone decided to start a creative cooking club. She didn't want members to memorise recipes but to learn how to adapt and personalise them–perhaps even create their own masterpieces in due course.
She designed a recipe
for the first session: Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta. She made several photocopies. When attendees arrived for the club, they could get_the_recipe()
on their way in. So, get_the_recipe(recipe)
results in the attendees getting a photocopy of the recipe.
Note: this is a generic function, not a Python function. I'll clarify why I'm saying this later.
Keith was eager to get started. He liked spicy food, so he got his pen out and added chillies to his version of the recipe. Marta didn't like the idea of guacamole without garlic, so she added it to her version.
Midway through the session, you could see each attendee's recipe strewn with blue or red markings as they personalised it. All was going well.
In an alternate universe, Simone's counterpart was also starting a creative cooking club. She wrote her recipe
on a Google Doc to share with the attendees.
In this universe, get_the_recipe(recipe)
gives the attendees access to the shared Google Doc with the recipe.
This universe's Keith also likes hot food and added chillies to the recipe. And this Marta also likes garlic. Go figure! It seems our counterparts in alternate universes aren't that different from us, after all!
Midway through the session, Simone had a look at the recipe
on her computer. This was proving to be one hell of a concoction. Everyone's changes had merged into one recipe. All attendees were sharing the same document. But you'd never guess–the final dish was a success, somehow!
Pass by Value and Pass by Reference
Let's leave Python to one side for now and talk about programming in general. Functions don't work in the same way across different languages.
What happens when you pass an argument to a function? The answer depends on what programming language you're working with.
Pass by Value
Some languages pass the value of the argument expression, for example, the value of a variable (whatever a variable is—more on this later). Often, this means the function creates a copy of the argument's value, which is stored in a new memory location.
This approach ensures that the function doesn't affect the variable in the scope calling the function, such as a program's global scope.
Let me translate the last sentence. Suppose there's a variable in the main program, and you pass it to a function in a 'pass by value' language. Since the function doesn't have access to the original data, it can't change the value of that variable.
The Simone in the primary universe created a recipe
. But when the club's attendees "called" get_the_recipe(recipe)
, they got a photocopy. This conceptual function passes the value of the recipe, a photocopy containing an exact replica of the original one.
But when Keith and Marta make changes to the recipe, they only affect their own copy. Simone's original version is unaffected.
Pass by Reference
Another approach is when a programming language passes a reference to data stored in memory, such as the reference to an object in languages that have objects.
A function's argument is the address to data that belongs to the scope where the function is called.
Let's rephrase. The main program has a variable that represents data stored in memory. When you pass this variable to a function in a 'pass by reference' language, you pass the address of the data's memory location. So, the function has access to the original data, and if it makes changes to the data, these changes will occur everywhere in the program.
In the alternate universe, Simone's recipe
is the same as in the primary universe. But get_the_recipe(recipe)
gives attendees access to the same recipe, not a copy–they all have a link to the same document.
When Keith adds chillies, he adds them to everyone's recipe because there's only one version. And Marta's garlic also makes its way to everyone's frying pans once they start cooking!
Back to Python Now • Identity, Type, and Value of an Object
So, you may be wondering whether Python functions are 'pass by value' or 'pass by reference'. You'll get to this shortly. (Spoiler alert: neither.)
First, let's talk about Python objects. I wrote about objects and their names in What's In A Name?
But in this article, I'll focus on one aspect of this topic:
Every object in Python has an identity, a type, and a value.
Let's use a dummy example to illustrate this:
The BlankObject
class only has a .__new__()
special method. This is not a special method you define often in custom classes. And don't worry if you're not familiar with some of the code in this method.
But even when you don't define this special method, it's always there, and it's always the first special method the program calls when creating an instance of any class. The last line in this code creates an instance of BlankObject
. Here's the output from this program:
Creating instance
This output confirms that the program calls .__new__()
when creating an instance of this class.
This object doesn't have anything yet. Well, that's strictly not true since there are some dunder attributes that every object has:
In Python, all objects are subclasses of the object
class, and they all inherit the following attributes:
__class__
__delattr__
__dict__
__dir__
__doc__
__eq__
__format__
__ge__
__getattribute__
__gt__
__hash__
__init__
__init_subclass__
__le__
__lt__
__module__
__ne__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
__weakref__
But let's focus on what really matters for this article. The program creates a new object and stores it somewhere in memory. You can create several more instances of this class. They'll all be identical blank objects, but they won't be the same object. Imagine you spot two cars of the same make, model, colour, and specifications manufactured at the same time. They're identical, but they're not the same car!
Each object has a unique identity, which you can display using Python's built-in id()
:
The output shows the memory address for this specific object (in CPython, which is the most common Python implementation):
4341341232
You'll get a different number when you try this on your computer. In fact, you'll get a different number each time you try it out.
Every object has a unique identity, and this identity doesn't change for the duration of the program.
In my native country, Malta, there's an identity card system. I was given an identity number at birth, and that number has remained the same my entire life.
But if you create a second instance of the class, it will have a different identity:
The two values returned by id()
are different, confirming the objects are distinct:
Creating instance
Creating instance
4310801456 4310801744
Every object also has a type:
The name says it all. The type
tells you what type of object you're dealing with:
<class '__main__.BlankObject'>
This object is of type BlankObject
. Even the most basic object has a type:
No prizes for guessing the output:
<class 'object'>
Every Python class inherits from this class.
Finally, there's the value. Let's get back to the test
object you created from the BlankObject
class. This object doesn't really have any value yet. Often, an object's value is defined in its .__init__()
special method, which initialises the object, or through other methods that assign or change values.
For example, numbers = [42, 3.142, 99]
creates an object of type list
that has a unique identity, which you can find using id()
. The object's value is the list with the numbers 42
, 3.142
, and 99
.
In general, an object's value is defined by the contents of its data attributes.
Right, you're ready to go back to functions and their arguments.
If you enjoy my style of communication, you may also enjoy the video courses at The Python Coding Place. New: there’s now a monthly subscription option of $15 / month. Or you can still pay a one-time fee for lifetime access if you prefer. Plus, you get access to the members’ forum.
Python is Pass by ...??
Let's replicate Simone's cooking club using Python code. We'll start with the Simone in the primary universe. I'll include only the recipe's name in this example to keep it simple and to ensure you don't steal Simone's recipe–go to her cooking club if you want the full recipe!
You won't be surprised to find out that the value of the string original_recipe
in the global scope is the same as the value of the argument within the function:
The original recipe is:
Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
The recipe within the function is:
Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
It would be worrying if they weren't the same! But this is not sufficient to claim that Python uses 'pass by value'.
Let's look at the identity instead:
You fetch the identity using id()
, and you discover they're the same:
The original recipe's identity is: 4421109168
The recipe's identity within the function is: 4421109168
Therefore, when you pass original_recipe
to the function, you pass a reference to the object, since the object within the function is the same object that exists outside the function. These aren't equal objects. They're the same object.
Does this mean that Python uses 'pass by reference'? No, not quite.
Let's look at what happens when Keith calls this function. Note that I'm cutting some corners to demonstrate the relevant points since, in a "proper" program, Keith's changes would occur in a separate function!
Here's the output showing the printouts within and outside the function:
The original recipe is:
Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
The original recipe's identity is: 4480533936
The recipe within the function is:
Snaking Spicy Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
The recipe's identity within the function is: 4480534192
Keith changed the recipe's name. But the identity of the object within the function is now different. It's no longer the same object as the recipe outside the function.
You can also confirm that Keith's changes only affect his version and not Simone's original recipe:
Here's the output from this code:
Keith's recipe is:
Snaking Spicy Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
Simone's original recipe is:
Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
There's no "Spicy" in Simone's "Snaking Chicken".
Pass by Assignment (or Pass by Sharing)
So, why was the identity of the object within the function the same as the object in the global scope in one of the calls to get_the_recipe()
but different in the other?
Python doesn't use 'pass by reference' or 'pass by value'. Instead, the argument passed to a function is assigned to a new local variable within the function. This local variable is the parameter name.
In the program above, you create the string with Simone's original recipe name in the main scope of the program. This object has a specific identity. You also assign it to the variable name original_recipe
. A variable name in Python is a reference to an object. The name original_recipe
refers to the object that has a specific identity and is stored in a particular location in memory.
So, what happens when you call get_the_recipe(original_recipe)
? You may be tempted to think that you're passing the variable name original_recipe
to the function. After all, that's what you write in the parentheses! This would be 'pass by reference'. However, Python deals with arguments in a more nuanced manner.
When you call get_the_recipe(original_recipe)
, Python passes the object to which the variable name original_recipe
refers. This object is assigned to the local variable the_recipe
within the function.
Therefore, at the start of the function, both original_recipe
and the_recipe
refer to the same object. And this is why id()
returns the same value in the version when Keith doesn't make any changes. Here's that version again:
This shows that original_recipe
and the_recipe
refer to the same object since they have the same identity value:
The original recipe's identity is: 4444603824
The recipe's identity within the function is: 4444603824
However, when Keith changes the recipe, he uses the string method .replace()
, which creates a new string object with the replaced text. Strings are immutable, and therefore, the only way to make a change is to create a new string object that's similar to the original one except for the required changes. Here's the function showing Keith's change again:
In the first line within the function's definition, the local variable the_recipe
is reassigned to a new object–the new string object that .replace()
returns. Therefore, from this point onwards, the_recipe
no longer refers to the original object.
This style of dealing with function arguments is sometimes called 'pass by sharing'. However, a more commonly used term in Python is 'pass by assignment' since the object is assigned to a local variable—the parameter name—when passed to the function. But that same local variable can be reassigned to another object.
But, but, in this function, Keith had to work with a string, which is immutable. That's why he had to create a new string object and reassign it to the same variable name.
Let's have a look at what happens when you pass mutable objects to a function.
Passing Mutable Objects as Arguments • The Alternate Universe Cooking Club
Let's look at code that could represent the alternate universe scenario. Recall that this is the case when all attendees shared the same Google Doc, and their changes affected everyone else's recipe:
The recipe is stored in a Python dictionary this time. And a Python dictionary is mutable. The global variable name original_recipe
and the function's local variable the_recipe
both refer to the same object:
The original recipe's identity is: 4302408640
The recipe's identity within the function is: 4302408640
So far, this situation is similar to the case in the primary universe, where original_recipe
was an immutable string. The global variable and the function's local variable also referred to the same object in the version where Keith didn't change the recipe.
But let's include Keith's changes now:
Keith changes the name and adds chillies to the list of ingredients. The output shows Keith's new recipe:
The original recipe's identity is: 4521331648
The recipe's identity within the function is: 4521331648
Keith's recipe is called
Snaking Spicy Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
and includes:
Chicken, GIL-free Guacamole, Bruschetta, Chillies
However, look at the identity of the original recipe and Keith's version, which is within the function. They're the same.
Keith was able to change the values within the original recipe since dictionaries are mutable. It's possible to change values in a mutable object without creating a new object.
But, if this is the same object as Simone's original recipe, what does Simone's recipe look like now?
Hmmm!
Simone's original recipe is called
Snaking Spicy Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
and includes:
Chicken, GIL-free Guacamole, Bruschetta, Chillies
Simone's "original" recipe now includes the word "Spicy" in the name and has chillies as one of the ingredients. This is hardly Simone's original version!
This behaviour is similar to what you'd expect in a 'pass by reference' programming language. However, in Python, this only happens when you try to mutate a mutable object within a function. Every function call will access and modify the same object.
But recall that Python uses "pass by assignment". Let's review what this means with this example:
You create the original dictionary object and assign it to the variable name
original_recipe
.You pass
original_recipe
toget_the_recipe()
. But in Python, this doesn't pass the variable name (reference). Instead, Python passes the object thatoriginal_recipe
refers to and assigns it to the local variablethe_recipe
, which is the parameter name.Both
original_recipe
andthe_recipe
refer to the same object.Since the object is a dictionary, which is mutable, the function changes some of its values (name and ingredients).
Both
original_recipe
andthe_recipe
still refer to the same object, which has now changed.
You can use sys.getrefcount()
to see the additional reference created when you pass an object to a function:
I truncated the dictionary's contents in this version for brevity. Here's the output from this code for Python 3.12. (You may get a different result on older Python versions due to optimisations on how references are treated in recent updates to the language):
The original recipe's identity is: 4308191424
Number of references to object in global scope: 2
The recipe's identity within the function is: 4308191424
Number of references to object within function's local scope: 3
In the global scope, before calling the function, sys.getrefcount()
returns 2
. Now, in reality, there's only one reference to the dictionary at this point: the variable name original_recipe
. You'll understand why sys.getrefcount()
returns an extra reference by the end of this section!
When you call sys.getrefcount()
within the function, the output shows there are now three references to the object. That's one more than outside the function. The same object now also has the local variable the_recipe
referring to it.
And what if you check again in the global scope but after calling the function? What do you think the reference count will be, two or three?
This is a trick question as the answer in this version is still 3
:
The original recipe's identity is: 4373956800
Number of references to object in global scope: 2
The recipe's identity within the function is: 4373956800
Number of references to object within function's local scope: 3
Number of references to object in global scope after function call: 3
The local variable the_recipe
only exists for as long as the function is running. This reference is removed when the function ends. So why is the final reference count still 3
?
The function returns the dictionary, which you assign to a new variable name, keith_s_recipe
. This is the third reference. Suppose you remove this assignment and simply call the function without assigning its return value. In that case, the reference count should return to the original 2
:
And here's confirmation that the function's local variable no longer counts as a reference to the dictionary object once the function terminates:
The original recipe's identity is: 4301949120
Number of references to object in global scope: 2
The recipe's identity within the function is: 4301949120
Number of references to object within function's local scope: 3
Number of references to object in global scope after function call: 2
The extra reference to the object Python creates when calling the function get_the_recipe()
is due to the function assigning the argument to the parameter name, and it only exists while the function is running.
And this explains the mystery of why the reference count returned by sys.getrefcount()
always shows one more reference than you might expect. After all, sys.getrefcount()
is itself a function. Therefore, you pass the object as an argument to sys.getrefcount()
, which adds another reference to the object! Whenever you use sys.getrefcount()
, always subtract one from the final answer to get the "real" reference count.
Reassigning a mutable object within a function
Here's the function with Keith's changes again:
Since the local variable the_recipe
refers to the same mutable object as original_recipe
, the function mutates the object everywhere, not just in the function.
Simone's original recipe is no longer available. It's replaced by Keith's:
The original recipe's identity is: 4345891008
The recipe's identity within the function is: 4345891008
Keith's recipe is called
Snaking Spicy Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
and includes:
Chicken, GIL-free Guacamole, Bruschetta, Chillies
Simone's original recipe is called
Snaking Spicy Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
and includes:
Chicken, GIL-free Guacamole, Bruschetta, Chillies
What if you don't want this behaviour? If you were using an immutable data type, you'd need to create a copy to make changes. But you can still create a copy of a mutable type:
The local variable the_recipe
now refers to a new object. This new object is initially a copy of the original one. When Keith makes changes to make the recipe spicy, he only affects his copy:
The original recipe's identity is: 4297870208
The recipe's identity within the function is: 4297883520
Keith's recipe is called
Snaking Spicy Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
and includes:
Chicken, GIL-free Guacamole, Bruschetta, Chillies
Simone's original recipe is called
Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta
and includes:
Chicken, GIL-free Guacamole, Bruschetta
The original recipe and the version at the end of the function now have different identities since the function works on a copy, which is a new object. And Simone's recipe still has the original name and ingredients.
Did you notice that I used the copy
module from Python's standard library and the function deepcopy()
from this module? Dictionaries have their own .copy()
method. Can you spot why you can't use this dictionary .copy()
method in this case? Try it out and look carefully at Simone's ingredients in the program's output when you do.
You can read more about this topic in Shallow and Deep Copy in Python and How to Use '__copy__()'.
What's Monty Got to Say About This?
If you're an avid The Python Coding Stack reader or you read The Python Coding Book (what do you mean you haven't?! Grab your copy here!), you'll know about Monty and my favourite programming analogy.
I'll summarise the bits I need for this article, but I won't repeat the whole analogy. To get the full picture, read the three-part series Monty and The White Room Analogy.
In this analogy, Monty works in a room that represents the resources needed for a program. A function defined in a script is analogous to a room adjacent to the main room, with a door leading from one room to the other. The function room door has a label with the function's name.
In this analogy, variables are boxes. The object goes inside the box, and the variable name is a label on the box's exterior. Monty uses these labels–the variable names–to find the box he needs and fetch its contents.
Where does 'pass by assignment' fit into this?
In the Monty and The White Room analogy, when you define a function and create a new function room, you place boxes at the function room's entrance, one for each parameter. These boxes are pre-labelled with the parameter names, but they're empty at first.
In this article's example, the function is get_the_recipe()
. At the function room's entrance, there's an empty box labelled the_recipe
, which represents the parameter name.
When you call get_the_recipe(original_recipe)
from the main script, Monty fetches the contents of the box labelled original_recipe
in the main room. He takes the object he finds in this box with him as he enters the function room get_the_recipe
.
When he's in the function room, he finds the empty box labelled the_recipe
and puts the object in it.
However, suppose the function includes a line to reassign a new object to the_recipe
. In that case, the original object is no longer in the box labelled the_recipe
.
I wrote about analogies a few times in the past (Whizzing Through Wormholes and The Wrong Picture.) No analogy is perfect. In the Monty analogy, Monty removes the object from the box labelled original_recipe
in the main room to take it to the function room. In reality, original_recipe
is still a valid reference for the object, even when the object is in the function. The object isn't removed from one box to go into another. I have found a solution for this analogy problem, but the margins here aren't big enough! Ask in the comments if you really want to know.
Pass by Assignment: A Summary
Some languages use 'pass by value'. Only the value of an argument is passed to a function. The function doesn't have access to the original data that exists in the main scope.
Other languages use 'pass by reference'. In these languages, the function's argument is a reference or pointer to data that exists in the scope that calls the function. Therefore, if the function changes the argument, the function changes the original data.
Python's 'pass by assignment' is something in between these two. If you pass a variable name as a function argument, the object this name refers to is assigned to a local variable in the function. This local variable is the parameter name. The function has access to the original object and can make changes to that object if the object is mutable. However, the parameter name will no longer refer to the original argument if you reassign a new object to it.
This may seem like a confusing hybrid system which behaves like 'pass by value' at times and like 'pass by reference' at other times. However, once you understand what's happening behind the scenes when you pass an argument to a function, you can use it to your advantage as it gives you flexibility.
And you'll also be well-placed to avoid any unexpected consequences!
If you use immutable arguments, then Python functions behave similarly to 'pass by value'. Even though the function has access to the object in the scope that calls the function, it can't make changes to it.
If you use mutable arguments, you have the choice of mutating the original data, similar to 'pass by reference' functions in other languages. But you can also reassign the parameter name if you don't want to mutate the original object.
Do you find this topic confusing? Don't worry, you're not alone!
Code in this article uses Python 3.12
Stop Stack
#70
Tempted to become a member of The Python Coding Place? Now, there's a monthly subscription option that's only $15 / month. Get access to all the video courses and the members' forum…
Thank you to all those who supported me with one-off donations. This means a lot and helps me focus on writing more articles and keeping more of these articles free for everyone. Here's the link again for anyone who wants to make a one-off donation to support The Python Coding Stack
The Python Coding Book is available (Ebook and paperback). This is the First Edition, which follows from the "Zeroth" Edition that has been available online for a while—Just ask Google for "python book"!
And if you read the book already, I'd appreciate a review on Amazon. These things matter so much for individual authors!
Any questions? Just ask…
Appendix: Code Blocks
Code Block #1
class BlankObject:
def __new__(cls, *args, **kwargs):
print("Creating instance")
return super().__new__(cls)
test = BlankObject()
Code Block #2
# ...
print(*dir(test), sep="\n")
Code Block #3
# ...
print(id(test))
Code Block #4
# ...
test = BlankObject()
another_test = BlankObject()
print(id(test), id(another_test))
Code Block #5
# ...
print(type(test))
Code Block #6
obj = object()
print(type(obj))
Code Block #7
original_recipe = "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta"
print(f"The original recipe is:\n{original_recipe}\n")
def get_the_recipe(the_recipe):
print(f"The recipe within the function is:\n{the_recipe}\n")
return the_recipe
get_the_recipe(original_recipe)
Code Block #8
original_recipe = "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta"
print(f"The original recipe's identity is: {id(original_recipe)}\n")
def get_the_recipe(the_recipe):
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
get_the_recipe(original_recipe)
Code Block #9
original_recipe = "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta"
print(f"The original recipe is:\n{original_recipe}")
print(f"The original recipe's identity is: {id(original_recipe)}\n")
def get_the_recipe(the_recipe):
the_recipe = the_recipe.replace("Snaking", "Snaking Spicy")
print(f"The recipe within the function is:\n{the_recipe}")
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
get_the_recipe(original_recipe)
Code Block #10
# ...
keith_s_recipe = get_the_recipe(original_recipe)
print(f"Keith's recipe is:\n{keith_s_recipe}\n")
print(f"Simone's original recipe is:\n{original_recipe}")
Code Block #11
original_recipe = "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta"
print(f"The original recipe's identity is: {id(original_recipe)}\n")
def get_the_recipe(the_recipe):
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
get_the_recipe(original_recipe)
Code Block #12
original_recipe = "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta"
print(f"The original recipe is:\n{original_recipe}")
print(f"The original recipe's identity is: {id(original_recipe)}\n")
def get_the_recipe(the_recipe):
the_recipe = the_recipe.replace("Snaking", "Snaking Spicy")
print(f"The recipe within the function is:\n{the_recipe}")
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
keith_s_recipe = get_the_recipe(original_recipe)
print(f"Keith's recipe is:\n{keith_s_recipe}\n")
print(f"Simone's original recipe is:\n{original_recipe}")
Code Block #13
original_recipe = {
"name": "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta",
"ingredients": [
"Chicken",
"GIL-free Guacamole",
"Bruschetta",
# a few more ingredients
],
"preparation time": "1 hour",
# ...
}
print(f"The original recipe's identity is: {id(original_recipe)}\n")
def get_the_recipe(the_recipe):
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
get_the_recipe(original_recipe)
Code Block #14
# ...
print(f"The original recipe's identity is: {id(original_recipe)}\n")
def get_the_recipe(the_recipe):
the_recipe["name"] = the_recipe["name"].replace("Snaking", "Snaking Spicy")
the_recipe["ingredients"].append("Chillies")
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
keith_s_recipe = get_the_recipe(original_recipe)
print(
f"Keith's recipe is called\n{keith_s_recipe['name']}\n"
f"and includes:\n{', '.join(keith_s_recipe['ingredients'])}\n"
)
Code Block #15
# ...
print(
f"Simone's original recipe is called\n{original_recipe['name']}\n"
f"and includes:\n{', '.join(original_recipe['ingredients'])}\n"
)
Code Block #16
import sys
original_recipe = {
# ...
}
print(f"The original recipe's identity is: {id(original_recipe)}")
print(
f"Number of references to object in global scope: "
f"{sys.getrefcount(original_recipe)}\n"
)
def get_the_recipe(the_recipe):
the_recipe["name"] = the_recipe["name"].replace("Snaking", "Snaking Spicy")
the_recipe["ingredients"].append("Chillies")
print(f"The recipe's identity within the function is: {id(the_recipe)}")
print(
f"Number of references to object within function's local scope: "
f"{sys.getrefcount(the_recipe)}\n"
)
return the_recipe
keith_s_recipe = get_the_recipe(original_recipe)
Code Block #17
import sys
# ...
def get_the_recipe(the_recipe):
# ...
keith_s_recipe = get_the_recipe(original_recipe)
print(
f"Number of references to object in global scope after function call: "
f"{sys.getrefcount(original_recipe)}\n"
)
Code Block #18
# ...
get_the_recipe(original_recipe)
print(
f"Number of references to object in global scope after function call:
f"{sys.getrefcount(original_recipe)}\n"
)
Code Block #19
import sys
original_recipe = {
"name": "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta",
"ingredients": [
"Chicken",
"GIL-free Guacamole",
"Bruschetta",
# a few more ingredients
],
"preparation time": "1 hour",
# ...
}
print(f"The original recipe's identity is: {id(original_recipe)}")
def get_the_recipe(the_recipe):
the_recipe["name"] = the_recipe["name"].replace("Snaking", "Snaking Spicy")
the_recipe["ingredients"].append("Chillies")
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
keith_s_recipe = get_the_recipe(original_recipe)
print(
f"Keith's recipe is called\n{keith_s_recipe['name']}\n"
f"and includes:\n{', '.join(keith_s_recipe['ingredients'])}\n"
)
print(
f"Simone's original recipe is called\n{original_recipe['name']}\n"
f"and includes:\n{', '.join(original_recipe['ingredients'])}\n"
)
Code Block #20
import copy
original_recipe = {
"name": "Snaking Chicken with GIL-free Guacamole on Byte-Sized Bruschetta",
"ingredients": [
"Chicken",
"GIL-free Guacamole",
"Bruschetta",
# a few more ingredients
],
"preparation time": "1 hour",
# ...
}
print(f"The original recipe's identity is: {id(original_recipe)}")
def get_the_recipe(the_recipe):
the_recipe = copy.deepcopy(the_recipe)
the_recipe["name"] = the_recipe["name"].replace("Snaking", "Snaking Spicy")
the_recipe["ingredients"].append("Chillies")
print(f"The recipe's identity within the function is: {id(the_recipe)}\n")
return the_recipe
keith_s_recipe = get_the_recipe(original_recipe)
print(
f"Keith's recipe is called\n{keith_s_recipe['name']}\n"
f"and includes:\n{', '.join(keith_s_recipe['ingredients'])}\n"
)
print(
f"Simone's original recipe is called\n{original_recipe['name']}\n"
f"and includes:\n{', '.join(original_recipe['ingredients'])}\n"
)
I thought the recipe analogy was excellent. Very good way to explain it.
The white room analogy seems to need the concept of mutable vs immutable. Maybe the actual objects go in boxes labeled with "addresses" and the rooms use boxes with variable labels and pieces of paper with an address inside? Some of the object boxes are locked and can't be changed... 🤷🏼♂️ What's your solution?