<jupyter_start><jupyter_text>Variables Variable assignmentPython has built-in support for arithmetic expressions and so can be used as a calculator. When we evaluate an expression in Python, the result is displayed, but not necessarily stored anywhere.<jupyter_code>2 + 2
4 * 2.5<jupyter_output><empty_output><jupyter_text>If we want to access the result in subsequent code, we have to store it. We put it in a box, with a name on the box. This is a _variable_. In Python we _assign_ a value to a variable using the _assignment operator_ `=`<jupyter_code>four = 2 + 2
four<jupyter_output><empty_output><jupyter_text>As well as numeric [literal values](https://en.wikipedia.org/wiki/Literal_(computer_programming)) Python also has built in support for representing textual data as sequences of characters, which in computer science terminology are termed [_strings_](https://en.wikipedia.org/wiki/String_(computer_science)). Strings in Python are indicated by enclosing their contents in either a pair of single quotation marks `'...'` or a pair of double quotation marks `"..."`, for example<jupyter_code>greeting = "hello world"<jupyter_output><empty_output><jupyter_text>Naming variablesWe can name variables with any combination of lower and uppercase characters, digits and underscores `_` providing the first character is not a digit and the name is not a [reserved keyword](https://docs.python.org/3/reference/lexical_analysis.htmlkeywords).<jupyter_code>fOuR = 4
four_integer = 4
integer_4 = 4
# invalid as name cannot begin with a digit
4_integer = 4
# invalid as for is a reserved word
for = 4<jupyter_output><empty_output><jupyter_text>It is good practice to give variables descriptive and meaningful names to help make code self-documenting. As most modern development environments (including Jupyter Lab!) offer _tab completion_ there is limited disadvantage from a keystroke perspective of using longer  names. Note however that the names we give variables only have meaning to us:<jupyter_code>two_plus_two = 5<jupyter_output><empty_output><jupyter_text>_Aside:_ Reading error messages We have already seen a couple of examples of Python error messages. It is important, when learning to program, to develop an ability to read an error message and find, from what can initially seem like confusing noise, the bit of the error message which tells you where the problem is.For example consider the following<jupyter_code>number_1 = 1
number_2 = "2"
sum_of_numbers = number_1 + number_2<jupyter_output><empty_output><jupyter_text>We may not yet know what `TypeError` or `Traceback` refer to. However, we can see that the error happens on the _third_ line of our code cell. We can also see that the error message: > `unsupported operand type(s) for +: 'int' and 'str'` ...tells us something important. Even if we don't understand the rest, this is useful for debugging!  Undefined variables and `None` If we try to evaluate a variable that hasn't ever been defined, we get an error.<jupyter_code>seven<jupyter_output><empty_output><jupyter_text>In Python names are case-sensitive so for example `six`, `Six` and `SIX` are all different variable names<jupyter_code>six = 6
six
Six<jupyter_output><empty_output><jupyter_text>There is a special `None` keyword in Python which can be assigned to variables to indicate a variable with no-value. This is _not_ the same as an undefined variable:<jupyter_code>nothing
nothing = None
nothing
print(nothing)<jupyter_output><empty_output><jupyter_text>Anywhere we can use a literal value, we can instead use a variable name, for example<jupyter_code>5 + four * six
scary = six * six * six
scary<jupyter_output><empty_output><jupyter_text>*Supplementary Materials*: There's more on variables at [Software Carpentry](https://swcarpentry.github.io/python-novice-inflammation/01-intro/index.html).  Reassignment and multiple labels We can reassign a variable - that is change what is in the box the variable labels.<jupyter_code>scary = 25
scary<jupyter_output><empty_output><jupyter_text>The data that was previously labelled by the variable is lost. No labels refer to it any more - so it has been [_garbage collected_](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). We might imagine something pulled out of the box, and disposed of, to make way for the next occupant. In reality, though, it is the _label_ that has moved. We can see this more clearly if we have more than one label referring to the same box<jupyter_code>name = "<NAME>"
nom = name
print(name)
print(nom)<jupyter_output><empty_output><jupyter_text>and we move just one of those labels:<jupyter_code>nom = "<NAME> <NAME>"
print(name)
print(nom)<jupyter_output><empty_output><jupyter_text>Variables and memoryWe can now better understand our mental model of variables as labels and boxes: each box is a piece of space (an *address*) in computer memory. Each label (_variable_) is a reference to such a place and the data contained in the memory defines an _object_ in Python. Python objects come in different types - so far we have encountered both numeric (integer) and textual (string) types - more on this later.When the number of labels on a box (_variables referencing an address_) gets down to zero, then the data in the box cannot be accessed any more. This will trigger Python's garbage collector, which will then 'empty' the box (_deallocated the memory at the address_), making it available again to store new data.Lower-level languages such as C and Fortran do not have garbage collectors as a standard feature. So a memory address with no references to it and which has not been specifically marked as free remains unavailable for other usage, which can lead to difficult to fix [_memory leak_](https://en.wikipedia.org/wiki/Memory_leak) bugs.When we execute<jupyter_code>name = "<NAME>"
nom = name
nom = "<NAME>"
name = "<NAME>"<jupyter_output><empty_output><jupyter_text>the following happens1. A new text (_string_) object `"<NAME>"` is created at a free address in memory and the variable `name` is set to refer to that address2. The variable `nom` is set to refer to the object at the address referenced by `name`3. A new text (_string_) object `"<NAME>"` is created at a free address in memory and the variable `nom` is set to refer to that address4. A new text (_string_) object `"<NAME>"` is created at a free address in memory, the variable `name` is set to refer to that address and the garbage collector deallocates the memory used to hold `"Grace Hopper"` as this memory is no longer referenced by any variables. _Supplementary materials_: The website [Python Tutor](https://pythontutor.com/) has a great interactive tool for visualizing how memory and references work in Python which is great for visualising memory and references. Try the [scenario we just looked at](https://pythontutor.com/visualize.htmlcode=name%20%3D%20%22Grace%20Hopper%22%0Anom%20%3D%20name%0Anom%20%3D%20%22Grace%20Brewster%20Murray%20Hopper%22%0Aname%20%3D%20%22Admiral%20Hopper%22&cumulative=false&curInstr=4&heapPrimitives=true&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false).  Variables in notebooks and kernels When code cells are executed in a notebook, the variable names and values of the referenced objects persist between cells<jupyter_code>number = 1<jupyter_output><empty_output><jupyter_text>There if  we change a variable in one cell<jupyter_code>number = number + 1<jupyter_output><empty_output><jupyter_text>It keeps its new value for the next cell.<jupyter_code>number<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0720875065893516, 'fraction_numerical': 0.010938323668950976, 'mean_word_length': 5.85546522131888, 'pattern_counts': {'":': 0, '<': 78, '<?xml version=': 0, '>': 79, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '140405', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 2219, 'n_tokens_neox': 2100, 'n_words': 950, 'path': '01-beginner/015variables.ipynb', 'repo_name': 'UCL-RITS/doctoral-programming-intro', 'size': '7681'}