Why does this work? (Project "The Boredless Tourist")

Hello there.

I am working on the above mentioned project of the computer science course:
Link

Tasks 27 - 34 are about creating a function called add_attraction and testing it.

Here is my code:

destinations = ["Paris, France", "Shanghai, China", "Los Angeles, USA", "São Paulo, Brazil", "Cairo, Egypt"]
test_traveler = ['Erin Wilkes', 'Shanghai, China', ['historical site', 'art']]
attractions = [[] for dest in destinations]

def get_destination_index(destination):
  destination_index = destinations.index(destination)
  return destination_index

def get_traveler_location(traveler):
  traveler_destination = traveler[1]
  traveler_destination_index = get_destination_index(traveler_destination)
  return traveler_destination_index

def add_attraction(destination, attraction):
  try:
    destination_index = get_destination_index(destination)
    attractions_for_destination = attractions[destination_index]
    attractions_for_destination.append(attraction)
    return
  except ValueError:
    return
    
test_destination_index = get_traveler_location(test_traveler)
add_attraction("Los Angeles, USA", ['Venice Beach', ['beach']])

print(test_destination_index)
print(attractions)

It produces the output as demanded by the task:

[[], [], [['Venice Beach', ['beach']]], [], []]

Why does this work?
How can it be, that the content of the list “attractions” is altered by the function?
Shouldn’t only the list “attractions_for_destination” be edited?

Looking forward to more insight about this topic.

The short answer is that your line attractions_for_destination = attractions[destination_index] does not create a new list object contained within the variable attractions_for_destination.

What Python does is it copies the reference to the list, so both attractions_for_destination and attractions[destination_index] refer to the same list object in memory.

Because of this behaviour, attractions_for_destination.append(attraction) is the same as doing attractions[destination_index].append(attraction).

You can verify this like so:

# test code - relies on your existing function definitions, so append to end of file!
test_destination = "Shanghai, China" # pick a destination
add_attraction(test_destination, ['Guangfulin', ['historical site']]) # add a test attraction
dest_index = get_destination_index(test_destination) #get destination index
existing_list = attractions[dest_index] # assign existing list to a variable

if existing_list is attractions[dest_index]:
    print("Same object")
else:
    print("Different object")

test_list = existing_list[:]
if existing_list is test_list:
    print("Same list")
else:
    print("Different list!")

The is operator returns true if the objects are the same.
https://docs.python.org/3/library/operator.html#operator.is_

Edit: Changed the verification code. Taking a slice of the list (existing_list[:]) does create a duplicate object, which demonstrates better that the original test is pointing to the same object. :slight_smile:

3 Likes

here:

attractions_for_destination = attractions[destination_index]

this variable now points to the already existing list in memory.

for example:

x = [1, 2, 3]
y = x

some people now expect y is a copy of x, its not. Both variable point to same list in memory:

x = [1, 2, 3]
y = x
y.append(4)
print(x) # output: 1, 2, 3, 4

i would like to disclaim, i am not a fan of this approach. Like you discovered, its confusing

7 Likes

can also use list(existing_list), this will create a copy.

understanding C pointers can be useful on occasion, even for python :slight_smile:

4 Likes

Ha, I was about to reply to your previous post along similar lines…

4 Likes

Thank you very much for the quick responses!

Now that’s good to know.

Does this behaviour only apply to lists or does it work the same for other data structures?

For example:
x = 1
y = x

Does that create a new object or just reference the original one?

it seems to reference the same one:

image

the identity is the same.

To truly grasp this topic, i think you will need to learn the C language and the pointer/reference concepts.

3 Likes

Turns out that this is covered on the Python FAQs!

https://docs.python.org/3/faq/programming.html#why-did-changing-list-y-also-change-list-x

But then, it makes sense that 3 is always 3

4 Likes

I disagree. Python object-variable binding is an internally consistent system that only causes difficulty here because it is not taught from the beginning as part of the Python curriculum. In C, you are forced to bring in pointers early on, as they are integral to the language, and what you can do without explicitly invoking them is quite limited. Pointers are taught, and then everyone moves on.

In teaching Python, however, you can get by without mentioning anything about the internals of variable assignment. CC’s Python3 course leaves it at this:

In Python, we assign variables by using the equals sign ( = )

(“equals sign”?? Come on!) With this approach, there won’t be any problems until you start copying lists, and then you get threads like this one.

If a unit had been inserted very early on concerning the call stack and object assignment (and, no, it’s not about pointers!), much of this confusion would be avoided.

1 Like

I agree that an understanding of C pointers isn’t necessary to understand that Python deals with assignment in the way that it does here, but if you’re aware of how they work then this whole business is less surprising/scary. So, they’re not essential, but knowing about them helps a bit here.

No argument there, though I can understand why CC opted to call it the “equals sign”. Get people used to the syntax and how it works first, using language they understand - hence, equals sign - then once they’ve got the gist, tell them it’s actually the assignment operator. (I’m assuming they do include the words “assignment operator” somewhere…)

I would not start with C, but once you reach a certain level in python, its nice to delve a bit into C, to help you understand python a little better. If the waters are deep, you can always safely go back to python, no harm done.

2 Likes

@stetim94 and @thepitycoder I’m certainly in favor of learning C if one is so inclined. I’m only suggesting here that Python be taught correctly in the first place. A bit of time spent on objects, assignment of variables to them, and the call stack would make things much easier later on, for learners and mentors both!

1 Like

Would it be a good idea to jump straight to C++ after learning Python, if I want to get more into game development?

Or should I first go with C before doing that?

For game development in general? I’d imagine you wouldn’t be able to meaningfully leverage any benefits C++ would give you over python, you may as well use python as a prototyping/learning language, and then if there’s some library or such that you terribly need to be using, or some community is using a particular language, then yeah go ahead. pypy is also decently fast.

2 Likes

Well, from what I understand so far, C++ is supposed to be “closer to the machine” since it’s a compiled language and you can make use of runtime environments to benefit both “performance-wise” and “platform support-wise”.

In the long run, I would like to create 3D games with Unity or Unreal Engine.

When it comes to more complicated 3D games, I think there is no way around using C++? (Correct me if I’m wrong!)

If that is the case, I would like to know, if I should learn C before starting C++ or if I can jump straight to C++.

EDIT: AFAIK Unity utilizes C#, so there would be a third language in the pool. Maybe that would be a good next step after learning python?

1 Like

If you’re wanting to make 3D games using Unity, then yes C# would be a good way to go.

Learning Python first will get you to the point where you understand the programming concepts; once you’ve got those, learning another language is simply a case of applying what you already know in different words. :slight_smile:

Pick the easiest language that supports whatever it is you’re looking to do. Then when you’ve figured out what you’re doing you’ll be better able to do it in any language. Trying to learn C++ and UE at the same time might not be a great idea. But then again it might be fine if you have access to good community/resources which matters more than the language.

Don’t learn C or C++ or C#. Go learn game programming.