An alias is an alternative name for something. In Python, two variables are said to be aliases when they contain the same memory address. For example, the following code creates two variables, both of which refer to a single list:
When we modify the list using one of the variables, references through the other variable show the change as well:
| >>> celegans_phenotypes = ['Emb', 'Him', 'Unc', 'Lon', 'Dpy', 'Sma'] |
| >>> celegans_alias = celegans_phenotypes |
| >>> celegans_phenotypes[5] = 'Lvl' |
| >>> celegans_phenotypes |
| ['Emb', 'Him', 'Unc', 'Lon', 'Dpy', 'Lvl'] |
| >>> celegans_alias |
| ['Emb', 'Him', 'Unc', 'Lon', 'Dpy', 'Lvl'] |
Aliasing is one of the reasons why the notion of mutability is important. For example, if x and y refer to the same list, then any changes you make to the list through x will be “seen” by y, and vice versa. This can lead to all sorts of hard-to-find errors in which a list’s value changes as if by magic, even though your program doesn’t appear to assign anything to it. This can’t happen with immutable values like strings. Since a string can’t be changed after it has been created, it’s safe to have aliases for it.
Aliasing occurs when you use list parameters as well, since parameters are variables. Here is a function that takes a list, removes its last item, and returns the list:
| >>> def remove_last_item(L: list) -> list: |
| ... """Return list L with the last item removed. |
| ... |
| ... Precondition: len(L) >= 0 |
| ... |
| ... >>> remove_last_item([1, 3, 2, 4]) |
| ... [1, 3, 2] |
| ... """ |
| ... del L[-1] |
| ... return L |
| ... |
| >>> |
In the code that follows, a list is created and stored in a variable; then that variable is passed as an argument to remove_last_item:
| >>> celegans_markers = ['Emb', 'Him', 'Unc', 'Lon', 'Dpy', 'Lvl'] |
| >>> remove_last_item(celegans_markers) |
| ['Emb', 'Him', 'Unc', 'Lon', 'Dpy'] |
| >>> celegans_markers |
| ['Emb', 'Him', 'Unc', 'Lon', 'Dpy'] |
When the call on function remove_last_item is executed, parameter L is assigned the memory address that celegans_markers contains. That makes celegans_markers and L aliases. When the last item of the list that L refers to is removed, that change is “seen” by celegan_markers as well.
Since remove_last_item modifies the list parameter, the modified list doesn’t actually need to be returned. You can remove the return statement:
| >>> def remove_last_item(L: list) -> None: |
| ... """Remove the last item from L. |
| ... |
| ... Precondition: len(L) >= 0 |
| ... |
| ... >>> remove_last_item([1, 3, 2, 4]) |
| ... """ |
| ... del L[-1] |
| ... |
| >>> celegans_markers = ['Emb', 'Him', 'Unc', 'Lon', 'Dpy', 'Lvl'] |
| >>> remove_last_item(celegans_markers) |
| >>> celegans_markers |
| ['Emb', 'Him', 'Unc', 'Lon', 'Dpy'] |
Notice that we did not use typing.List in the type contract for remove_last_item. This is because the function does not rely on having values of any particular type inside the list. We could instead use typing.List and specify Any as the type:
| >>> from typing import List, Any |
| >>> def remove_last_item(L: List[Any]) -> None: |
| ... """Remove the last item from L. |
| ... |
| ... Precondition: len(L) >= 0 |
| ... |
| ... >>> remove_last_item([1, 3, 2, 4]) |
| ... """ |
| ... del L[-1] |
As we’ll see in List Methods, several methods modify a list and return None, like the second version of remove_last_item.