<jupyter_start><jupyter_text>Variables and Simple Data Types In Python variable names are just **symbols** that point to some object (everything is an object) in memory. The variable name (or symbol) has no special meaning other than a way for us programmers to easily deal with objects by using a symbolic name.Unlike languages like Java or C, Python variables (symbols) are not **declared** with some **static type**. A symbol can can change what object it points to at any time, and since the type is associated with the object, not the symbol, the type of the variable is essentially **dynamic**. This is called **dynamic** typing.So symbols (or variables) are simply names for objects in memory. Any object has a **memory address** (representing the starting memory address), which can easily be recovered in Python, although there is rarely any need to use memory addresses. We will use it here because it helps understand how Python deals wioth variables and objects in many situations. So useful as a learning and debugging tool. To "create" a variable we have to first create an object, and then assign that object to a symbol.For example:<jupyter_code>a = "hello"<jupyter_output><empty_output><jupyter_text>Here we actually created an object, the string `hello` in memory, and assigned that symbol a name `a`. We can get the memory address of this object by using the `id()` function:<jupyter_code>id(a)<jupyter_output><empty_output><jupyter_text>And we can get the hexadecimal equivalent of this decimal number by using the `hex()` function:<jupyter_code>hex(id(a))<jupyter_output><empty_output><jupyter_text>Data Types Many of the basic data types in Python, such as integers, floats, strings, lists, dictionaries and more have corresponding **literals**. A literal is simply a hardcoded value, such as an actual number, or an actual string. We can use the `type()` function to determine the type of an object:<jupyter_code>a = 'hello'
type(a)
a = 10
type(a)<jupyter_output><empty_output><jupyter_text>Notice how the type of `a` changed - that's because the object `a` was pointing to changed from a string to an integer. The most important data types in Python, and their corresponding data types are:  Integers<jupyter_code>a = 10
print(id(a), type(a), a)
a = -10
print(id(a), type(a), a)<jupyter_output>4480037328 <class 'int'> -10<jupyter_text>Integers in Python can be of any size (as long as they will fit in memory). There is no concept of short integers, long integers, etc as there is in languages such as C or Java. Python integers will just grow as large as needed. Computations will get slower, but you don't need to do anything special to handle them.  Floats<jupyter_code>a = 10.5
print(id(a), type(a), a)
a = -3.14
print(id(a), type(a), a)<jupyter_output>4479517128 <class 'float'> -3.14<jupyter_text>Floats are stored in Python (and in most other programming languages) using a binary representation.Just as a decimal number such as:```0.0123``` is technically ```0/10 + 1/100 + 2/1000 + 3/10000```, which uses powers of `10`, a binary representation uses powers of `2` instead.For example:`0.125` is `0/2 + 0/4 + 1/8` Some numbers do not have an **exact** decimal representation. For example the number `1/3` would be `3/10 + 3/100 + 3/1000 + ...` Similarly, some numbers, even though they may have an exact **decimal** representation, do **not** have a binary representation. For example `0.1` has a binary representation of `0/2 + 0/4 + 0/8 + 1/16 + 1/32 + 0/64 + 0/128 + 1/256 + 1/512 + 0/1024 + 0/2048 + 1/4096 + ...`This is sometimes written as:`0.00011001100110011... (base 2)` We can look at an approximation:<jupyter_code>1/16 + +1/32 + 1/256 + 1/512 + 1/4096<jupyter_output><empty_output><jupyter_text>So this binary representation is **infinite** and **converges** to `0.1`.Since we cannot possibly store an infinite representation in a computer, there is a limit to how many binary digits are used to store such a number, which means that the decimal number `0.1` **does not have an exact representation** in our program! This will become important to understand when we try to compare two flkoats using **equality**.<jupyter_code>a = 0.1
print(a)<jupyter_output>0.1<jupyter_text>Ok, so why does it look like Python is holding an exact represenattion of `0.1`? Beware, this is simply Python rounding things for visual represenation purposes.Python supports formatting specifiers when printing things like numbers, where we can for example define how many digits after the decimal point we want to display:<jupyter_code>print(format(0.1, '.10f'))<jupyter_output>0.1000000000<jupyter_text>Still looks exact!<jupyter_code>print(format(0.1, '.30f'))<jupyter_output>0.100000000000000005551115123126<jupyter_text>Booleans In Python, **booleans** are actually just **integers**, where `0` is **false** and `1` is **true**.But we don't actually use these numbers, we use some special objects that have the symbols `True` and `False`.<jupyter_code>a = True
print(id(a), type(a), a)
True == 1<jupyter_output><empty_output><jupyter_text>Other Numeric Types Python has other numeric types as well, such as complex numbers, rational numbers (fractions), and something related to floats (but with more specifiable precision)m called decimals. For example, to create a complex number, we can use literals:<jupyter_code>a = 1 + 1j
print(type(a), a)
b = 2 + 2j
a + b
a * b<jupyter_output><empty_output><jupyter_text>Fractions can also be created, but we won't cover this in this primer.  The None type There is a special object in Python, called `None` that is used to indicate **nothing**. Think of it like the empty set in mathematics.It is a regular object, and has a type of `NoneType`:<jupyter_code>a = None
print(id(a), type(a), a)
print(a)<jupyter_output>None
{'fraction_non_alphanumeric': 0.09448954914493005, 'fraction_numerical': 0.040766971843150805, 'mean_word_length': 5.239224137931035, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '246581', 'license': 'MIT', 'n_tokens_mistral': 1878, 'n_tokens_neox': 1693, 'n_words': 792, 'path': '01 - Variables and Simple Data Types.ipynb', 'repo_name': 'fredflev/python-primer', 'size': '5789'}