fix(guide): simplify directory structure

This commit is contained in:
Mrugesh Mohapatra
2018-10-16 21:26:13 +05:30
parent f989c28c52
commit da0df12ab7
35752 changed files with 0 additions and 317652 deletions

View File

@ -0,0 +1,114 @@
---
title: The Python Dict
---
A Dictionary (a.k.a "dict") in python is a built-in datatype that can be used to store **`key-value`** pairs. This allows you to treat a **`dict`** like it's a *database* to store and organize data.
The special thing about dictionaries is the way they are implemented. Hash-table-like structure makes it easy to check for
existence - which means that we can easily determine if a specific key is present in the dictionary without needing to examine
every element. The Python interpreter can just go to the location key and check if the key is there.
Dictionaries can use almost any arbitrary datatypes, like strings, integers etc, for keys. However, values that are not hashable,
that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys. Numeric types used for keys obey the normal rules for numeric comparison: if two numbers compare equal (such as `1` and `1.0`) then they can be used interchangeably to index the same dictionary entry. (Note however, that since computers store floating-point numbers as approximations it is usually unwise to use them as dictionary keys.)
One most important requirement of a dictionary is that the keys **must** be unique.
To create an empty dictionary just use a pair of braces:
```python
>>> teams = {}
>>> type(teams)
>>> <class 'dict'>
```
To create a non-empty dictionary with some initial values, place a comma-seperated list of key-value pairs:
```python
>>> teams = {'barcelona': 1875, 'chelsea': 1910}
>>> teams
{'barcelona': 1875, 'chelsea': 1910}
```
It's easy to add key-value pairs to an existing dictionary:
```python
>>> teams['santos'] = 1787
>>> teams
{'chelsea': 1910, 'barcelona': 1875, 'santos': 1787} # Notice the order - Dictionaries are unordered !
>>> # extracting value - Just provide the key
...
>>> teams['barcelona']
1875
```
**`del`** operator is used to delete a key-value pair from the dict. In scenarios where a key that's already in use is again used to store values, the old value associated with that key is completely lost. Also, keep in mind that it's an error to extract the value using an non-existent key.
```python
>>> del teams['santos']
>>> teams
{'chelsea': 1910, 'barcelona': 1875}
>>> teams['chelsea'] = 2017 # overwriting
>>> teams
{'chelsea': 2017, 'barcelona': 1875}
```
**`in`** keyword can be used to check whether a key exist in the dict or not:
```python
>>> 'sanots' in teams
False
>>> 'barcelona' in teams
True
>>> 'chelsea' not in teams
False
```
**`keys`** is a built-in *method* that can be used to get the keys of a given dictionary. To extract the keys present in a dict as lists:
```python
>>> club_names = list(teams.keys())
>>> club_names
['chelsea', 'barcelona']
```
**`values`** is a built-in *method* that can be used to get the values of a given dictionary. To extract the values present in a dict as lists:
```python
>>> club_names = list(teams.values())
>>> club_names
[1910, 1875]
```
Yet another way of creating dictionary is using the **`dict()`** method:
```python
>>> players = dict( [('messi','argentina'), ('ronaldo','portugal'), ('kaka','brazil')] ) # sequence of key-value pair is passed
>>> players
{'ronaldo': 'portugal', 'kaka': 'brazil', 'messi': 'argentina'}
>>>
>>> # If keys are simple strings, it's quite easier to specify pairs using keyword arguments
...
>>> dict( totti = 38, zidane = 43 )
{'zidane': 43, 'totti': 38}
```
Dict comprehensions can be used as well to create dictionaries from arbitrary key and value expressions:
```python
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
```
**Looping in Dictionary**
To simply loop over the keys in the dictionary, rather than the keys and values:
```python
>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> for key in d:
... print(key) # do something
...
x
y
z
```
To loop over both key and value you can use the following:
For Python 2.x:
```python
>>> for key, item in d.iteritems():
... print items
...
1
2
3
```
Use **`items()`** for Python 3.x:
```python
>>> for key, item in d.items():
... print(key, items)
...
x 1
y 2
z 3
```

View File

@ -0,0 +1,108 @@
---
title: Python Floating Point Numbers
---
Some general information about floating point numbers and how they work in Python, can be found <a href='https://docs.python.org/3/tutorial/floatingpoint.html' target='_blank' rel='nofollow'>here</a>.
Nearly all implementations of Python follow the IEEE 754 specification: Standard for Binary Floating-Point Arithmetic. More information found on the <a href='http://grouper.ieee.org/groups/754/' target='_blank' rel='nofollow'>IEEE site</a>.
Float objects can be created using using <a href='https://docs.python.org/3/reference/lexical_analysis.html#floating-point-literals' target='_blank' rel='nofollow'>floating point literals</a>:
>>> 3.14
3.14
>>> 314\. # Trailing zero(s) not required.
314.0
>>> .314 # Leading zero(s) not required.
0.314
>>> 3e0
3.0
>>> 3E0 # 'e' or 'E' can be used.
3.0
>>> 3e1 # Positive value after e moves the decimal to the right.
30.0
>>> 3e-1 # Negative value after e moves the decimal to the left.
0.3
>>> 3.14e+2 # '+' not required but can be used for exponent part.
314.0
Numeric literals do not contain a sign, however creating negative float objects is possible by prefixing with a unary `-` (minus) operator with no space before the literal
>>> -3.141592653589793
-3.141592653589793
>>> type(-3.141592653589793)
<class 'float'>
Likewise, positive float objects can be prefixed with a unary `+ (`plus) operator with no space before the literal. Usually `+` is omitted:
>>> +3.141592653589793
3.141592653589793
Note that leading and trailing zero(s) are valid for floating point literals
>>> 0.0
0.0
>>> 00.00
0.0
>>> 00100.00100
100.001
>>> 001e0010 # Same as 1e10
10000000000.0
The <a href='https://docs.python.org/3/library/functions.html#float' target='_blank' rel='nofollow'>`float` constructor</a> is another way to create `float` objects.
Creating `float` objects with floating point literals is preferred when possible:
>>> a = 3.14 # Prefer floating point literal when possible.
>>> type(a)
<class 'float'>
>>> b = int(3.14) # Works but unnecessary.
>>> type(b)
<class 'float'>
However, the float constructor allows for creating float objects from other number types:
>>> a = 4
>>> type(a)
<class 'int'>
>>> print(a)
4
>>> b = float(4)
>>> type(b)
<class 'float'>
>>> print(b)
4.0
>>> float(400000000000000000000000000000000)
4e+32
>>> float(.00000000000000000000000000000004)
4e-32
>>> float(True)
1.0
>>> float(False)
0.0
The `float` constructor will also make `float` objects from strings that represent number literals:
>>> float('1')
1.0
>>> float('.1')
0.1
>>> float('3.')
3.0
>>> float('1e-3')
0.001
>>> float('3.14')
3.14
>>> float('-.15e-2')
-0.0015
The `float` constructor can also be used to make numeric representation of `NaN` (Not a Number), negative `infinity` and `infinity` (note strings for these are case insensitive):
>>> float('nan')
nan
>>> float('inf')
inf
>>> float('-inf')
-inf
>>> float('infinity')
inf
>>> float('-infinity')
-inf

View File

@ -0,0 +1,15 @@
---
title: The Python Data Structures
---
A data structure is a particular way of organizing data in a computer so that it can be used efficiently. Python comes with a robust set of inbuilt data structures. Some of the most commonly used ones are :-
* Lists
* Tuples
* Dictionaries
Majorly, the data structures can be divided into two categories :-
* Mutable :- A mutable data structure is a structure whose state can be modified after it is created. Python Lists and Dictionaries are mutable.
* Immutable :- An immutable data structure can't be modified. Example :- Once a tuple is created, we can't update the values inside it.
## Reference:
[Python Data Structures](https://docs.python.org/3.7/tutorial/datastructures.html)

View File

@ -0,0 +1,147 @@
---
title: Python Integers
---
The theoretical domain for integers in python is negative infinity to infinity. In practice, integer values are limited by the amount of available memory.
In Python 2, there was a distinction between **`int`**, numbers that fit in a 32 or 64 bit _C long_, and **`long`**, numbers limited by available memory. Python 3 unified the two types into just **`int`**, more info in <a href='https://www.python.org/dev/peps/pep-0237/' target='_blank' rel='nofollow'>PEP 237</a>.
**`int` creation using integer literals**
<a href='https://docs.python.org/3/reference/lexical_analysis.html#integer-literals' target='_blank' rel='nofollow'>Integer Literals</a>
_Integer objects_ can be created using using integer literals. Unadorned numbers without decimals are integer literals:
>>> 1234567890 # Unadorned numbers are integer literals
1234567890
>>> type(1234567890)
<class 'int'>
Numeric literals do not contain a sign, however creating negative _integer objects_ is possible by prefixing with a unary `-` (minus) operator with no space before the literal:
>>> -1234567890
-1234567890
>>> type(-1234567890)
<class 'int'>
Likewise, positive integer objects can be created by prefixing a unary `+` (plus) operator with no space before the digits. Usually `+` is ommited:
>>> +1234
1234
Binary (base 2, prefix: `0b` or `0B`), octal (base 8, prefix: `0o` or `0O`), and hexadecimal (base 16, prefix: `0x` or `0X`) integers can also be created using integer literals:
>>> 0b1, 0b10, 0b11
(1, 2, 3)
>>> 0o1, 0o10, 0o11
(1, 8, 9)
>>> 0x1, 0x10, 0x11
(1, 16, 17)
Note that leading 0's for non-zero integer literals are **not allowed**:
>>> 0 # Zero by itself is okay.
0
>>> 01 # Leading zero(s) cause SyntaxError.
File "<stdin>", line 1
01
^
SyntaxError: invalid token
The `int` <a href='https://docs.python.org/3/library/functions.html#int' target='_blank' rel='nofollow'>constructor</a> is another way to create _integer objects_.
class int(x=0)
class int(x, base=10)
Creating _integer objects_ with integer literals is preferred when possible:
>>> a = 1 # Prefer integer literal when possible.
>>> type(a)
<class 'int'>
>>> b = int(1) # Works but unnecessary.
>>> type(b)
<class 'int'>
However, the constructor allows for creating _integer objects_ from other number types:
>>> a = 1.123
>>> type(a)
<class 'float'>
>>> print(a)
1.123
>>> b = int(1.123)
>>> type(b)
<class 'int'>
>>> print(b)
1
Using the `int` constructor for floating point numbers will truncate the number towards zero:
>>> int(-1.23)
-1
>>> int(1.23)
1
The built-in `boolean` constants are instances of the `bool` class, and are subclasses of the `int` class, making them a kind of numeric type:
>>> type(True)
<class 'bool'>
>>> issubclass(bool, int)
True
If that doesn't make sense to you, don't worry. For now just remember that calling the int constructor with `boolean` objects will return _integer objects_:
>>> int(True)
1
>>> int(False)
0
The `int` constructor will also make _integer objects_ from strings:
>>> a = "10"
>>> type(a)
<class 'str'>
>>> b = int("10")
>>> type(b)
<class 'int'>
_Strings_ for the `int` constructor must represent an integer literal:
The second parameter of the `int` constructor is to specify a base (default: 10). Valid bases are 0 and 2-36.
If an explicit base is provided the first argument must be a string.
>>> int("111", 2)
7
>>> int(111, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() can't convert non-string with explicit base
The string used for the `int` constructor with an explicit base must be a valid integer literal for that base:
>>> int('11', 2)
3
>>> int('12', 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 2: '12'
Both prefixed and non-prefixed strings of integer literals can be used, however, if used, the prefix must match the provided base.
>>> int('1101', 2)
13
>>> int('0b1101', 2)
13
>>> int('0x1101', 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 2: '0x1101'
If a prefixed string and base 0 is used, the created integer object will use the base specified by the prefix. If no prefix is used, then the base is assumed 10
>>> int('100', 0)
100
>>> int('0b100', 0)
4
>>> int('0o100', 0)
64

View File

@ -0,0 +1,115 @@
---
title: The Python Objects
---
> In Python, everything is an _object_.
_Objects_ represent a logical grouping of attributes. Attributes are data and/or functions. When an object is created in Python it is created with an _identity_, _type_, and _value_.
In other languages, _primitives_ are _values_ that have no _properties_ (attributes). For example, in javascript `undefined`, `null`, `boolean`, `string`, `number`, and `symbol` (new in ECMAScript 2015) are primitives.
In Python, there are no primitives. `None`, _booleans_, _strings_, _numbers_, and even _functions_ are all _objects_ regardless how they are created.
We can demonstrate this using some built-in functions:
* <a href='https://docs.python.org/3/library/functions.html#id' target='_blank' rel='nofollow'>`id`</a>
* <a href='https://docs.python.org/3/library/functions.html#type' target='_blank' rel='nofollow'>`type`</a>
* <a href='https://docs.python.org/3/library/functions.html#dir' target='_blank' rel='nofollow'>`dir`</a>
* <a href='https://docs.python.org/3/library/functions.html#issubclass' target='_blank' rel='nofollow'>`issubclass`</a>
Built-in constants `None`, `True`, and `False` are _objects_:
We test the `None` object here.
```python
>>> id(None)
4550218168
>>> type(None)
<class 'NoneType'>
>>> dir(None)
[__bool__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> issubclass(type(None), object)
True
```
Next, let's inspect `True`.
```python
>>> id(True)
4550117616
>>> type(True)
<class 'bool'>
>>> dir(True)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
>>> issubclass(type(True), object)
True
```
No reason to leave out `False`!
```python
>>> id(False)
4550117584
>>> type(False)
<class 'bool'>
>>> dir(False)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
>>> issubclass(type(False), object)
True
```
_Strings_, even when created by a string literals, are also _objects_.
```python
>>> id("Hello campers!")
4570186864
>>> type('Hello campers!')
<class 'str'>
>>> dir("Hello campers!")
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> issubclass(type('Hello campers!'), object)
True
```
Same with _numbers_
```python
>>> id(42)
4550495728
>>> type(42)
<class 'int'>
>>> dir(42)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
>>> issubclass(type(42), object)
True
```
## Functions are Objects Too
> In Python, functions are first class objects.
_Functions_ in Python are also _objects_, created with an _identity_, _type_, and _value_. They too can be passed into other _functions_:
```python
>>> id(dir)
4568035688
>>> type(dir)
<class 'builtin_function_or_method'>
>>> dir(dir)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']
>>> issubclass(type(dir), object)
True
```
It is also possible to bind functions to a name and called the bound function using that name:
```python
>>> a = dir
>>> print(a)
<built-in function dir>
>>> a(a)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']
```
Resources:
* <a href='https://www.jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/' target='_blank' rel='nofollow'>Click Here</a> to learn more.

View File

@ -0,0 +1,85 @@
---
title: The Python Range
---
## Python Ranges
Rather than being a function, a range is actually an <a href='https://docs.python.org/3/library/stdtypes.html#immutable-sequence-types' target='_blank' rel='nofollow'>immutable sequence type</a> and is commonly used for looping a specific number of times in for loops.
**Creation:**
`ranges` are created using the `range` constructor. The parameters for the constructor are:
* `start`: Inclusive first value of the range (optional integer, defaults to 0).
* `stop` : Exclusive stop value, range stops when this value or greater would be provided (required integer).
* `step` : The amount added to the current value to get the next value (optional integer, defaults to 1).
```python
>>> range(10) # Only the stop parameter is required.
range(0, 10)
>>> range(0, 10) # Default for start parameter is 0.
range(0, 10)
>>> range(0, 10, 1) # Default for step is 1\. Start parameter is required if
step is needed.
range(0, 10)
```
**Examples:**
Since `ranges` are iterables they can be passed into the `list` and `tuple` constructors to create those types of sequences. Using this fact, we can visualize some examples:
```python
>>> list(range(10)) # range as argument for list constructor.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> tuple(range(10)) # range as argument for tuple constructor.
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
```
Zero length `ranges`:
```python
>>> list(range(10, 0)) # start greater than stop with postive step.
[]
>>> list(range(10, 10)) # start equal to stop with postive step.
[]
>>> list(range(10, 10, -1)) # start equal to stop with negative step.
[]
>>> list(range(0, 10, -1)) # start less than stop with negative step.
[]
```
`ranges` with step arguments:
```python
>>> list(range(0, 10, 2)) # next value would be 10, stops at 8.
[0, 2, 4, 6, 8]
>>> list(range(0, 10, 3)) # next value would be 12, stops at 9.
[0, 3, 6, 9]
>>> list(range(0, 10, 4)) # next value would be 12, stops at 8.
[0, 4, 8]
>>> list(range(10, 0, -1)) # negative step makes decreasing ranges.
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> list(range(-5, -30, -3)) # negative integers are valid arguments.
[-5, -8, -11, -14, -17, -20, -23, -26, -29]
```
**Benefits:**
The benefit of using `range` is that regardless of how large of a range specified, only a small amount of memory is needed to store the `range`, the values for start, stop, and step. The individual values of the `ranges` are calculated upon iteration.
```python
>>> import sys
>>> a_range = range(1000000)
>>> a_list = list(a_range)
>>> a_tuple = tuple(a_range)
>>> sys.getsizeof(a_range)
48
>>> sys.getsizeof(a_list)
9000112
>>> sys.getsizeof(a_tuple)
8000048
```
### More Inforamtion:
<a href='https://docs.python.org/3/library/stdtypes.html#ranges' target='_blank' rel='nofollow'>Python Doc - Ranges</a>
**TODO: Methods `ranges` do and do not implement**

View File

@ -0,0 +1,12 @@
---
title: The Python Scopes
---
**TODO: NOTE**
* This belongs near the code block and indentation information.
* This is also related to variables since name finding and binding is related to scopes.
**TODO: CONTENT**
* `global`
* `nonlocal`

View File

@ -0,0 +1,26 @@
---
title: The Python Strings
---
Python allows `str` objects, or _strings_, to be expressed in a few different ways:
* Single quotes: `'Single quote strings can have "double" quotes inside.'`
* Double quotes: `"Double quote strings can have 'single' quotes inside."`
* Triple quoted:
"""Triple quoted strings can span multiple lines.
Unescaped "double" and 'single' quotes in triple quoted strings are retained."""
'''Triple quoted strings can be 'single'or "double" quotes.
Unescaped newlines are also retained.'''
* Immutable: You cannot directly edit/change a Python string after you have created it. For example, if you try to directly reassign/change the first letter in a string, an error is thrown.
>>> foo = "my string"
>>> foo[0] = "a"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
## Reference:
<a href='https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str' target='_blank' rel='nofollow'>Text Sequence Type _str_</a>

View File

@ -0,0 +1,176 @@
---
title: The Tuples
---
## The Tuples
A tuple is a sequence of Python objects. Tuples are immutable which means they cannot be modified after creation, unlike lists.
>>> tuple = (1,2,3)
>>> tuple[1] = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
**Creation:**
An empty `tuple` is created using a pair of round brackets, `()`:
```shell
>>> empty_tuple = ()
>>> print(empty_tuple)
()
>>> type(empty_tuple)
<class 'tuple'>
>>> len(empty_tuple)
0
```
A `tuple` with elements is created by separating the elements with commas (surrounding round brackets, `()`, are optional with exceptions):
```shell
>>> tuple_1 = 1, 2, 3 # Create tuple without round brackets.
>>> print(tuple_1)
(1, 2, 3)
>>> type(tuple_1)
<class 'tuple'>
>>> len(tuple_1)
3
>>> tuple_2 = (1, 2, 3) # Create tuple with round brackets.
>>> print(tuple_2)
(1, 2, 3)
>>> tuple_3 = 1, 2, 3, # Trailing comma is optional.
>>> print(tuple_3)
(1, 2, 3)
>>> tuple_4 = (1, 2, 3,) # Trailing comma in round brackets is also optional.
>>> print(tuple_4)
(1, 2, 3)
```
A `tuple` with a single element must have the trailing comma (with or without round brackets):
```shell
>>> not_tuple = (2) # No trailing comma makes this not a tuple.
>>> print(not_tuple)
2
>>> type(not_tuple)
<class 'int'>
>>> a_tuple = (2,) # Single element tuple. Requires trailing comma.
>>> print(a_tuple)
(2,)
>>> type(a_tuple)
<class 'tuple'>
>>> len(a_tuple)
1
>>> also_tuple = 2, # Round brackets omitted. Requires trailing comma.
>>> print(also_tuple)
(2,)
>>> type(also_tuple)
<class 'tuple'>
```
Round brackets are required in cases of ambiguity (if the tuple is part of a larger expression):
> Note that it is actually the comma which makes a tuple, not the parentheses. The parentheses are optional, except in the empty tuple case, or when they are needed to avoid syntactic ambiguity. For example, `f(a, b, c)` is a function call with three arguments, while `f((a, b, c))` is a function call with a 3-tuple as the sole argument.
```shell
>>> print(1,2,3,4,) # Calls print with 4 arguments: 1, 2, 3, and 4
1 2 3 4
>>> print((1,2,3,4,)) # Calls print with 1 argument: (1, 2, 3, 4,)
(1, 2, 3, 4)
>>> 1, 2, 3 == (1, 2, 3) # Equivalent to 1, 2, (3 == (1, 2, 3))
(1, 2, False)
>>> (1, 2, 3) == (1, 2, 3) # Use surrounding round brackets when ambiguous.
True
```
A `tuple` can also be created with the `tuple` constructor:
```shell
>>> empty_tuple = tuple()
>>> print(empty_tuple)
()
>>> tuple_from_list = tuple([1,2,3,4])
>>> print(tuple_from_list)
(1, 2, 3, 4)
>>> tuple_from_string = tuple("Hello campers!")
>>> print(tuple_from_string)
('H', 'e', 'l', 'l', 'o', ' ', 'c', 'a', 'm', 'p', 'e', 'r', 's', '!')
>>> a_tuple = 1, 2, 3
>>> b_tuple = tuple(a_tuple) # If the constructor is called with a tuple for
the iterable,
>>> a_tuple is b_tuple # the tuple argument is returned.
True
```
**Accessing elements of a `tuple`:**
Elements of `tuples` are accessed and index the same way that `lists` are.
```shell
>>> my_tuple = 1, 2, 9, 16, 25
>>> print(my_tuple)
(1, 2, 9, 16, 25)
```
_Zero indexed_
```shell
>>> my_tuple[0]
1
>>> my_tuple[1]
2
>>> my_tuple[2]
9
```
_Wrap around indexing_
```shell
>>> my_tuple[-1]
25
>>> my_tuple[-2]
16
```
**Packing and Unpacking:**
The statement `t = 12345, 54321, 'hello!'` is an example of tuple packing: the values `12345`, `54321` and `'hello!'` are packed together in a tuple. The reverse operation is also possible:
```shell
>>> x, y, z = t
```
This is called, appropriately enough, sequence unpacking and works for any sequence on the right-hand side. Sequence unpacking requires that there are as many variables on the left side of the equals sign as there are elements in the sequence. Note that multiple assignment is really just a combination of tuple packing and sequence unpacking.
```shell
>>> t = 1, 2, 3 # Tuple packing.
>>> print(t)
(1, 2, 3)
>>> a, b, c = t # Sequence unpacking.
>>> print(a)
1
>>> print(b)
2
>>> print(c)
3
>>> d, e, f = 4, 5, 6 # Multiple assignment combines packing and unpacking.
>>> print(d)
4
>>> print(e)
5
>>> print(f)
6
>>> a, b = 1, 2, 3 # Multiple assignment requires each variable (right)
have a matching element (left).
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
```
**Immutable:**
`tuples` are immutable containers, guaranteeing **which** objects they contain will not change. It does **not** guarantee that the objects they contains will not change:
```shell
>>> a_list = []
>>> a_tuple = (a_list,) # A tuple (immutable) with a list (mutable) element.
>>> print(a_tuple)
([],)
>>> a_list.append("Hello campers!")
>>> print(a_tuple) # Element of the immutable is mutated.
(['Hello campers!'],)
```
**Uses:**
Functions can only return a single value, however, a heterogenuous `tuple` can be used to return multiple values from a function. One example is the built-in `enumerate` function that returns an iterable of heterogenuous `tuples`:
```shell
>>> greeting = ["Hello", "campers!"]
>>> enumerator = enumerate(greeting)
>>> enumerator.next()
>>> enumerator.__next__()
(0, 'Hello')
>>> enumerator.__next__()
(1, 'campers!')
```
### More Inforamtion:
<a href='https://docs.python.org/3/library/stdtypes.html#tuples' target='_blank' rel='nofollow'>Python Docs - Tuples</a>