Skip to content

Latest commit

 

History

History
900 lines (670 loc) · 63 KB

Python_intro.md

File metadata and controls

900 lines (670 loc) · 63 KB

Python

Reference: Python 3 Reference

Getting Started

Python is an interpreted programming language. As a developer you write Python (.py) files in a text editor and then put those files into the python interpreter to be executed. The way to run a python file is like this on the command line:

$ python name-of-script.py

A Python program is read by a parser. Input to the parser is a stream of tokens, generated by the lexical analyzer: Lexical_Analyzer —> Stream of Tokens —> Parser.

To test a short amount of code in python sometimes it is quickest and easiest not to write the code in a file. This is made possible because Python can be run as a command line itself. Type the following on the Windows, Mac or Linux command line:

  • python (for MacBook)
  • py (for Windows)

Whenever you are done in the python command line, you can simply type the following to quit the python command line interface: exit().

Indentation

Indentation refers to the spaces at the beginning of a code line. Where in other programming languages the indentation in code is for readability only, the indentation in Python is very important. Python uses indentation to indicate a block of code, scope of a function (analogous to curly braces). The number of spaces is up to you as a programmer, but it has to be at least one. White space is how Python knows we are entering a new block of code. Python accepts many different kinds of indentation to indicate blocks. Use four spaces but elsewhere you might encounter two-space indentation or tabs (which Python will see as different from spaces).

If the indentation from one line to the next is different and there is no command (like if) that indicates an incoming block then Python will raise an IndentationError. These errors could mean, for example, that one line had two spaces but the next one had three. Python tries to indicate where this error happened by printing the line of code it couldn't parse and using a ^ to point to where the indentation was different from what it expected.

Comments

Comments can be used to explain Python code. Comments can be used to make the code more readable. Comments can be used to prevent execution when testing code. Python does not really have a syntax for multi line comments. To add a multiline comment you could insert a # for each line. Since Python will ignore string literals that are not assigned to a variable, you can add a multiline string (triple quotes) in your code, and place your comment inside it:

"""
This is a comment
written in
more than just one line
"""

As long as the string is not assigned to a variable, Python will read the code, but then ignore it, and you have made a multiline comment.

Built-in Help and Output

help() takes an argument of anything you require the manual and how-to's.

print("Statement") statement is often used to output variables.

User Input

Python allows for user input. That means we are able to ask the user for input. The method is a bit different in Python 3.6 than Python 2.7. Python 3.6 uses the input() method. Python 2.7 uses the raw_input() method. Python stops executing when it comes to the input() function, and continues when the user has given some input. Using the input() function, we take input from a user, and using the print() function, we display output on the screen. Using the input() function, users can give any information to the application in the strings or numbers format.

Built-in Webserver / LocalHost

To start a webserver run the command below.

Python 2 method:

python -m simpleHTTPserver 8080

Python 3 method:

python3 -m http.server

That will open a webserver on port 8080. You can then open your browser at http://127.0.0.1:8080/ (Loopback IP). The webserver is also accessible over the network using your Interface Internet Facing IP address. This is a default server that you can use to download files from the machine.

Similar to NODE Js, python PIP can be used to generate a file transfer server FTP by first downloading the library pip install pyftpdlib then setup the server using python2 or python3 python3 -m pyftpdlib -p 21 w. Note: Remember the directory from which you're hosting the server matters in what files/Documents are being made available.

Variables

Python variables are created when you assign a value to it. Python has no command for declaring a variable. Variables do not need to be declared with any particular type, and can even change type after they have been set.

  • A variable name must start with a letter or the underscore character. A variable name cannot start with a number.
  • A variable name can only contain alpha-numeric characters and underscores (A-z, 0-9, and _).
  • Variable names are case-sensitive (age, Age and AGE are three different variables).
  • Python allows you to assign values to multiple variables in one line. Note: Make sure the number of variables matches the number of values, or else you will get an error.

Multi words variable names: camelCase, PascalCase, snake_case.

  • Functions are mostly declared using snake_case.
  • Classes meanwhile are mostly declared using PascalCase.

Similar to other programming languages, the + operator can be used to concat variables. To add a space between the concatenated string, add a "whitespace". NOTE: However this works as a mathematical operator for NUMBERS. If you try to combine a string and a number, Python will give you an error.

Scope and Namespaces

A scope is a textual region of a Python program where a namespace is directly accessible. "Directly accessible" here means that an unqualified reference to a name attempts to find the name in the namespace.

Global Variable can be used by everyone, both inside and outside of functions. Local Variables are variables declared inside a function. However a global keyword can be used to make a variable declared inside a function globally accessible. Also, use the global keyword if you want to make a change to a global variable inside a function.

A variable is only available from inside the region it is created. This is called scope. A variable created inside a function belongs to the local scope of that function, and can only be used/accessed inside that function. Therefore nested functions can have access to the same variable. The local namespace for a function is created when the function is called, and deleted when the function returns or raises an exception that is not handled within the function. Of course, recursive invocations each have their own local namespace.

A namespace is a mapping from names to objects. Examples of namespaces are: the set of built-in names (containing functions such as abs(), and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is absolutely no relation between names in different namespaces; for instance, two different modules may both define a function without confusion -- users of the modules must prefix it with the module name. The built-in names actually also live in a module; this is called builtins.

Data Types and type() function

In programming, data type is an important concept. Variables can store data of different types, and different types can do different things. Python has the following data types built-in by default, in these categories. You can get the data type of any object by using the type() function. In Python, the data type is set when you assign a value to a variable.

Built-in Types

Category Types
Text Type str
Numeric Types int, float, complex
Sequence Types list, tuple, range
Mapping Type dict
Set Types set, frozenset
Boolean Type True or False
Binary Types bytes, bytearray, memoryview

Numeric Types

  • Integer -> int: whole number, positive or negative, without decimals, of unlimited length.
  • Decimals -> float: "floating point number" is a number, positive or negative, containing one or more decimals. Float can also be scientific numbers with an "e" to indicate the power of 10.
  • Complex -> complex: complex numbers are written with a "j" as the imaginary part.

Note: You can convert from one type to another with the int(), float(), and complex() methods. However you cannot convert complex numbers into another number type.

Casting

There may be times when you want to specify a type on to a variable. This can be done with casting. Python is an object-orientated language, and as such it uses classes to define data types, including its primitive types. Casting in python is therefore done using constructor functions:

  • int() - constructs an integer number from an integer literal, a float literal (by removing all decimals), or a string literal (providing the string represents a whole number).
  • float() - constructs a float number from an integer literal, a float literal or a string literal (providing the string represents a float or an integer).
  • str() - constructs a string from a wide variety of data types, including strings, integer literals and float literals.

String Types

Strings in python are surrounded by either single or double quotation marks. Like many other popular programming languages, strings in Python are arrays of bytes representing unicode characters with Index starting from 0. Square brackets can be used to access elements of the string.

Slicing

You can return a range of characters by using the slice syntax. Specify the start index and the end index, separated by a colon, to return a part of the string. By leaving out the start index, the range will start at the first character. By leaving out the end index, the range will go to the end. Use negative indexes to start the slice from the end of the string.

txt_str[x:y] where x is start index (inclusive), and y is end index (exclusive), txt_str analogous to a variable name.

String Methods

Reference: https://www.w3schools.com/python/python_strings_methods.asp

Python has a set of built-in methods that you can use on strings. Visit the link above for more.

  • The upper() method returns the string in upper case.
  • The lower() method returns the string in lower case.
  • The replace() method replaces a string with another string.
  • The strip() method removes any whitespace/newline (\n) from the beginning or the end.
  • The split() method returns a list where the text between the specified separator becomes the list items.
  • The isalpha() method checks that a string doesn’t contain any non-letter characters and returns boolean.

Escaping Characters

To insert characters that are illegal in a string, use an escape character. An escape character is a backslash \ followed by the character you want to insert. An example of an illegal character is a double quote inside a string that is surrounded by double quotes.

String Formatting

To make sure a string will display as expected, we can format the result with the format() method in four ways.

  1. Basic replacement:
car = "benz"
price = 49
txt = "The cost of the {} is {} dollars"
print(txt.format(car, price))
  1. You can use index numbers (a number inside the curly brackets {0}) to be sure the values are placed in the correct placeholders. Also, if you want to refer to the same value more than once, use the index number (positional).
quantity = 3
itemno = 567
price = 49
myorder = "I want {0} pieces of item number {1} for {2} dollars."
print(myorder.format(quantity, itemno, price))
  1. You can also use named indexes by entering a name inside the curly brackets {variable}, but then you must use names when you pass the parameter values (keyword).
myorder = "I have a {carname}, it is a {model}."
print(myorder.format(carname="Ford", model="Mustang"))

Positional and keyword arguments can be arbitrarily combined.

  1. Often you'll want more control over the formatting of your output than simply printing space-separated values. There are several ways to format output. To use formatted string literals (also called f-strings for short), let you include the value of Python expressions inside a string by prefixing the string with f or F and writing expressions as {expression}. An optional format specifier can follow the expression. This allows greater control over how the value is formatted. Inside this string, you can write a Python expression between { and } characters that can refer to variables or literal values. String literal prefixed by r or R is interpreted as raw strings and not in binary format by the parser. Modifiers can be used to convert the value before it is formatted. Passing an integer after the : will cause that field to be a minimum number of characters wide. This is useful for making columns line up. !a applies ascii(), !s applies str(), and !r applies repr().
name = "Fred"
f"He said his name is {name!r}."
f"He said his name is {repr(name)}."  # repr() is equivalent to !r

width = 10
precision = 4
value = decimal.Decimal("12.34567")
f"result: {value:{width}.{precision}}"  # nested fields

today = datetime(year=2017, month=1, day=27)
f"{today:%B %d, %Y}"  # using date format specifier
f"{today=:%B %d, %Y}" # using date format specifier and debugging

number = 1024
f"{number:#0x}"  # using integer format specifier

foo = "bar"
f"{ foo = }" # preserves whitespace

line = "The mill's closed"
f"{line = }"
f"{line = :20}"
f"{line = !r:20}"

Boolean Types

In programming you often need to know if an expression is True or False. You can evaluate any expression in Python, and get one of two answers, True or False. When you compare two values, the expression is evaluated and Python returns the Boolean answer. The bool() function allows you to evaluate any value, and give you True or False in return. NOTE: Almost any value is evaluated to True if it has some sort of content. In fact, there are not many values that evaluate to False, except empty values, such as (), [], {}, "", the number 0, and the value None. And of course the value False evaluates to False. Python also has many built-in functions that return a boolean value, like the isinstance() function, which can be used to determine if an object is of a certain data type. Unlike the type() function, the isinstance() function returns true or false. Its arguments are the variable and the data type you're testing against.

Python Operators

Python divides the operators in the following groups. The following tokens are operators:

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~       :=
<       >       <=      >=      ==      !=

The following tokens are delimiters:

(       )       [       ]       {       }
,       :       .       ;       @       =       ->
+=      -=      *=      /=      //=     %=      @=
&=      |=      ^=      >>=     <<=     **=
  • Bitwise operators
  • Arithmetic operators
  • Assignment operators
  • Comparison operators
  • Logical/Boolean operators

Logical operators are used to combine conditional statements. Boolean operators aren't just evaluated from left to right. Just like with arithmetic operators, there's an order of operations for boolean operators: not is evaluated first; and is evaluated next; or is evaluated last. For example, True or not False and False returns True. Parentheses () ensure your expressions are evaluated in the order you want. Anything in parentheses is evaluated as its own unit.

Operator Description Example
and Returns True if both statements are true x < 5 and x < 10
or Returns True if one of the statements is true x < 5 or x < 4
not Reverse the result, returns False if the result is true not(x < 5 and x < 10)

Identity Operators

Identity operators are used to compare the objects, not if they are equal, but if they are actually the same object, with the same memory location:

Operator Description Example
is Returns True if both variables are the same object x is y (analogous to x === y)
is not Returns True if both variables are not the same object x is not y (analogous to x !== y)

Membership Operators

Membership operators are used to test if a sequence is presented in an object:

Operator Description Example
in Returns True if a sequence with the specified value is present in the object/collection x in y
not in Returns True if a sequence with the specified value is not present in the object/collection x not in y

Division (/) always returns a float. To do floor division and get an integer result (discarding any fractional result) you can use the // operator.

Python Collections and Sequences (Arrays -> List[], Tuple(), Set{} and Objects -> Dictionary{})

There are four collection data types in the Python programming language:

  • List is a collection which is ordered and changeable. Allows duplicate members.
  • Tuple is a collection which is ordered and unchangeable. Allows duplicate members.
  • Set is a collection which is unordered and unindexed. No duplicate members.
  • Dictionary is a collection which is ordered and changeable. No duplicate members.

When we say that lists are ordered, it means that the items have a defined order, and that order will not change. If you add new items to a collection, the new items will be placed at the end of the collection. The list is changeable/mutable, meaning that we can change, add, and remove items in a list after it has been created. Duplicates means allow two or more of same item in the collection.

Python List [ ]

Lists are indexed collections used to store multiple items in a single variable. List items can be of any data type, and can contain different data types. It is also possible to use the list() constructor when creating a new list.

Syntax -> mylist = ["apple", "banana", "cherry"] thislist = list(("apple", "banana", "cherry")) # note the double round-brackets

[Xstart:Yend] where x is start index (inclusive), and y is end index (exclusive)

  • Access list using positive index to start from the beginning, and negative index to start from the end. We can also slice them using range [Xstart:Yend]. By leaving out the start value, the range will start at the first item. By leaving out the end value, the range will go on to the end of the list.
  • Change Item Value -> To change the value of a specific item, refer to the index number. To change the value of items within a specific range [Xstart:Yend], define a list with the new values, and refer to the range of index numbers where you want to insert the new values. If you insert more/less items than you replace, the new items will be inserted where you specified, and the remaining items will move accordingly. Note: The length of the list will change when the number of items inserted does not match the number of items replaced.

List Comprehension -> List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list. List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.

For example, assume we want to create a list of squares, like:

squares = []
for x in range(10):
    squares.append(x**2)

# squares = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Note that this creates (or overwrites) a variable named x that still exists after the loop completes. We can calculate the list of squares without any side effects using:

squares = list(map(lambda x: x**2, range(10)))

or, equivalently:

squares = [x**2 for x in range(10)] # which is more concise and readable.

newlist = [x for x in fruits if "a" in x]

Syntax -> [expression for item in iterable if condition == True] The expression is the current item in the iteration, but it is also the outcome, which you can manipulate before it ends up like a list item in the new list. The expression can also contain conditions, not like a filter, but as a way to manipulate the outcome. A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it.

[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

The condition is like a filter that only accepts the items that valuate to True. The condition works with conditional operator that evaluates to true or false. The condition is optional and can be omitted. The iterable can be any iterable object, like a list, tuple, set, and dict.

Method Description
append() Adds an element at the end of the list
clear() Removes all the elements from the list, return an empty list-no content
copy() and list() Returns a copy of the list
count() Returns the number of elements with the specified value
extend() Add the elements of a list (or any iterable) to the end of the current list or use + operator to join/concatenate 2/more lists
index() Returns the index of the first element with the specified value
insert() Adds an element at the specified position
pop() Removes the element at the specified position
remove() Removes the item with the specified value
reverse() Reverses the order of the list
sort() Sorts the list

Note: Python does not have built-in support for arrays, but Python Lists can be used instead. However, to work with arrays in Python you will have to import a library, like the NumPy library.

Python Tuple ( )

Unlike lists, tuples are written with round brackets. To create a tuple with only one item, you have to add a comma after the item, otherwise Python will not recognize it as a tuple. It is also possible to use the tuple() constructor to make a tuple. We access items in tuple analogous to list with their index [i].

Syntax -> mytuple = ("apple", "banana", "cherry") thistuple = tuple(("apple", "banana", "cherry")) # note the double round-brackets

Tuples are unchangeable, meaning that you cannot change, add, or remove items once the tuple is created. But there are some workarounds. You can convert the tuple into a list, using list() method, change the list, and convert the list back into a tuple using the tuple() method.

Sequence Unpacking a Tuple

When we create a tuple, we normally assign values to it. This is called "packing"/declaring (javascript) a tuple. But, in Python, we are also allowed to extract the values back into variables. This is called "unpacking". Unpacking is analogous to deconstructing. Sequence unpacking requires that there are as many variables on the left side of the equals sign as there are elements in the sequence.

fruits = ("apple", "banana", "cherry") <—> (x, y, z) = fruits # where x = apple

The rest and spread operator (...) in javascript is analogous to * asterisk. If the number of variables is less than the number of values, you can add an * to the variable name and the values will be assigned to the variable as a list. Tuples are immutable, and usually contain a heterogeneous sequence of elements that are accessed via unpacking (see later in this section) or indexing (or even by attribute in the case of namedtuples). Lists are mutable, and their elements are usually homogeneous and are accessed by iterating over the list.

Python Sets { }

Set items are unordered (You cannot access items in a set by referring to an index or a key), unchangeable/immutable (Once a set is created, you cannot change its items, but you can add new items), and do not allow duplicate values. It is also possible to use the set() constructor to make a set or any empty set. A set is an unordered collection with no duplicate elements. Basic uses include membership testing and eliminating duplicate entries. Set objects also support mathematical operations like union, intersection, difference, and symmetric difference. To add one item to a set use the add() method. To add items from another set into the current set, use the update() and union() method. It will exclude any duplicate items. Its argument doesn't have to be another set, it can be any utterable object (tuple, lists, dictionaries).

Method Description
difference() Returns a set containing the difference between two or more sets
difference_update() Removes the items in this set that are also included in another, specified set
discard() Remove the specified item
intersection() Returns a set that is the intersection of two other sets
intersection_update() Removes the items in this set that are not present in other, specified set(s)
isdisjoint() Returns True or False whether two sets have a intersection or not
issubset() Returns True or False whether another set contains this set or not
issuperset() Returns whether this set contains another set or not
symmetric_difference() Returns a set with the symmetric differences of two sets
symmetric_difference_update() Inserts the symmetric differences from this set and another

Python Dictionary

Dictionaries are used to store data values in "key":"value" pairs. A dictionary is a collection which is ordered, changeable and does not allow duplicates (duplicate values will overwrite existing values). Dictionaries are written with curly brackets, and have keys and values. A dictionary can contain dictionaries, this is called nested dictionaries. The dict() constructor builds dictionaries directly from sequences of key-value pairs:

dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}

Accessing Items

You can access the items of a dictionary by referring to its key name, inside square brackets. Or a method called get() that will give you the same result.

thisdict = {
  "key1": "value1",
  "key2": "value2",
} # x = thisdict["key1"] or x = thisdict.get("key1")

The keys() method will return a list of all the keys in the dictionary. The values() method will return a list of all the values in the dictionary. The items() method will return each item in a dictionary, as tuples () in a list []. [("Key", "value"), ("Key2", "value2"), ...]

Changing and Adding Items

The update() method will update the dictionary with the items from the given argument. Its argument must be a dictionary {}, or an iterable object with key:value pairs. You can also change and add the value of a specific item by referring to its key name and assigning a new or different value.

Copy a Dictionary

There are ways to make a copy, one way is to use the built-in dictionary method copy(). Another way to make a copy is to use the built-in function dict().

Method Description
fromkeys() Returns a dictionary with the specified keys and their respective assigned values
get() Returns the value of the specified key
pop() Removes the element with the specified key
popitem() Removes the last inserted key-value pair
setdefault() Returns the value of the specified key. If the key does not exist: insert the key, with the specified value

Python Iterators

An iterable is an object that contains a countable number of values. Lists, tuples, dictionaries, and sets are all iterable objects. Technically, in Python, an iterator is an object which implements the iterator protocol. All these objects have a iter() and next() method which is used to get an iterator. They are iterable containers which you can get an iterator from. An iterator is an object that can be iterated upon, meaning that you can traverse through all the values.

Note that every iterator is also an iterable, but not every iterable is an iterator. For example, a list is iterable but a list is not an iterator. An iterator is an object with a state which remembers where it is after every iteration. It can be created from an iterable object by using the function iter(iterable_obj). To make this possible, the class of an object needs either a method __iter__(), which returns an iterator.

When a for loop is executed, for statement calls iter() on the iterable object, which it is supposed to loop over. If this call is successful, the iter() call will return an iterator object that defines the method __next__(), which accesses elements of the object one at a time. The __next__() method will raise a StopIteration exception, if there are no further elements available. The for loop will terminate as soon as it catches a StopIteration exception.

Python expects iterable objects in several different contexts, the most important being the for statement. In the statement for X in Y, Y must be an iterator or some object for which iter() can create an iterator. These two statements are equivalent:

for i in iter(obj):
    print(i)

for i in obj:
    print(i)

Let's call the __next__() method using the next() built-in function. To create an object/class as an iterator you have to implement the methods __iter__() and __next__() to your object. Using the iter() and next() methods:

# list of cities
cities = ["Berlin", "Vienna", "Zurich"]

# initialize the object
iterator_obj = iter(cities)    # iterator_obj = cities.__iter__()

print(next(iterator_obj))
print(next(iterator_obj))
print(next(iterator_obj))
print(next(iterator_obj))    # raises StopIteration

As you have learned in the Python Classes/Objects chapter, all classes have a function called __init__(), which allows you to do some initializing when the object is being created. The __iter__() method acts similar, you can do operations (initializing etc.), but must always return the iterator object itself. The __next__() method also allows you to do operations, and must return the next item in the sequence.

Files also support iteration by calling the readline() method until there are no more lines in the file. Two common operations on an iterator's output are 1) performing some operation for every element, 2) selecting a subset of elements that meet some condition.

Python Generators

Generators are a special class of functions that simplify the task of writing iterators. Regular functions compute a value and return it, but generators return an iterator which in turn returns a stream of values. Using the yield keyword to replace the return keyword. Any function containing a yield keyword is a generator function; this is detected by Python's bytecode compiler which compiles the function specially as a result.

def generate_ints(N):
    for i in range(N):
        yield i

What if the local variables weren't thrown away from private namespace (function context in JS) on exiting a regular function? What if you could later resume the function where it left off? This is what generators provide; they can be thought of as resumable functions.

When you call a generator function, it doesn't return a single value; instead it returns a generator object that supports the iterator protocol. On executing the yield expression, the generator outputs the value of i, similar to a return statement. The big difference between yield and a return statement is that on reaching a yield the generator's state of execution is suspended and local variables are preserved not destroyed. On the next call to the generator's __next__() method, the function will resume executing. Inside a generator function, return value causes StopIteration(value) to be raised from the __next__() method. Once this happens, or the bottom of the function is reached, the procession of values ends and the generator cannot yield any further values.

A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator's iterator.__next__()/next(iterator) method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned.

Python Condition, Loops, and Control Flow

Control flow gives us this ability to choose among outcomes based on what else is happening in the program. Python supports the usual logical conditions from mathematics. These conditions can be used in several ways, most commonly in "if elif else statements", "for loops", and "while loops".

If / Elif / Else

An "if statement" is written by using the if keyword. Python relies on indentation (whitespace at the beginning of a line) to define scope in the code. Other programming languages often use curly-brackets for this purpose. The elif (analogous to "else if") keyword is python's way of saying "if the previous conditions were not true, then try this condition". The else keyword catches anything which isn't caught by the preceding conditions.

Nested If -> Analogous to Switch-Case statements. You can have if statements inside if statements; this is called nested if statements.

Short Hand If ... Else / Ternary Operators, or Conditional Expressions -> If you have only one statement to execute, one for if, and one for else, you can put it all on the same line. Note: if statements cannot be empty, but if you for some reason have an if statement with no content, put in the pass statement to avoid getting an error.

The While Loop

With the while loop we can execute a set of statements as long as a condition is true. With the break statement we can stop the loop even if the while condition is true. With the continue statement we can stop the current iteration, and continue with the next. With the else statement we can run a block of code once when the condition no longer is true.

i = 1
while i < 6:
    print(i)
    i += 1
    if i == 3:
        continue
    if i == 5:
        break
else:
    print("i is no longer less than 6")

The For Loop

A "for" loop is used for iterating over a sequence (that is either a list, a tuple, a dictionary, a set, or a string). This is less like the for keyword in other programming languages, and works more like an iterator method as found in other object-orientated programming languages. With the for loop we can execute a set of statements, once for each item in a list, tuple, set etc. The for loop does not require an indexing variable to set beforehand.

  • With the break statement we can stop the loop before it has looped through all the items. With the continue statement we can stop the current iteration of the loop, and continue with the next.
  • A nested loop is a loop inside a loop. The "inner loop" will be executed one time for each iteration of the "outer loop".
  • The else keyword in a for loop specifies a block of code to be executed when the loop is finished. The else block will NOT be executed if the loop is stopped by a break statement. Note: for loops cannot be empty, but if you for some reason have a for loop with no content, put in the pass statement to avoid getting an error.

When looping through a sequence, the position index and corresponding value can be retrieved at the same time using the enumerate() function. enumerate() is often used when looping through a list and recording the indexes at which certain conditions are met:

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

To loop over two or more sequences at the same time, the entries can be paired with the zip() function.

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

Python Functions

A function is a block of code which only runs when it is called. You can pass data/Information known as parameters/arguments. You can send any data types of argument to a function (string, number, list, dictionary etc.), and it will be treated as the same data type inside the function. A function can return data as a result. To call a function, use the function name followed by parenthesis (). To let a function return a value, use the return statement. In Python a function is defined using the def (defined) keyword:

def my_function(parameter):
    print("Hello from a function" + parameter)

my_function(argument) # calling the function
my_function(argument="value") # keyword argument

From a function's perspective: A parameter is the variable listed inside the parentheses in the function definition. An argument is the value that is sent to the function when it is called.

You can also send keyword arguments with the key = value syntax. This way the order of the arguments does not matter. If we call the function without argument, it uses the default value.

Arbitrary Parameters and Arguments, *args and **kwargs analogous to rest and spread arguments. If you do not know how many parameters that will be passed into your function, add an asterisks * before the parameter name in the function definition. This way the function will receive a tuple of arguments, and can access the items accordingly.

Lambda Functions

A lambda function is a small anonymous function analogous to an arrow function in JS. A lambda function can take any number of arguments, but can only have one expression, which is evaluated and the result is returned. Use lambda functions when an anonymous function is required for a short period of time (i.e using it within a high order function such as array methods map(), reduce(), filter()). The power of lambda is better shown when you use them as an anonymous function inside another function.

Syntax -> lambda arguments : expression

x = lambda a: a + 10
print(x(5)) # return is 15

def add10_func(a):
    return a + 10

x = lambda a, b: a * b
print(x(5, 6)) # return is 30

def multiply_func(a, b):
    return a * b

Python Class and Objects

Classes provide a means of bundling data and functionality together. Creating a new class creates a new type of object, allowing new instances of that type to be made. Each class instance can have attributes attached to it for maintaining its state. Class instances can also have methods (defined by its class) for modifying its state. Python is an object oriented programming language. Almost everything in Python is an object, with its properties and methods. A class is just a way of organizing and producing objects with similar attributes and methods or an object constructor, or a "blueprint" for creating objects. To create a class, use the keyword class.

The def init() Function

Analogous to constructor function in JavaScript classes. To understand the meaning of classes we have to understand the built-in def __init__() function. All classes may have a function called def __init__(), which is called automatically every time the class is being initiated/used to create a new object instance. Use the def __init__() function to assign attributes to object properties, or other operations that are necessary to do when the object is being created. The part that is magic is the fact that self is the first parameter passed to __init__(). Python will use the first parameter that __init__() receives to refer to the object being created; this is why it's often called self, since this parameter gives the object being created its identity. The self parameter is a reference to the current instance of the class, and is used to access variables that belong to the class. It does not have to be named self, you can call it whatever you like, but it has to be the first parameter of any function in the class.

When dealing with classes in Python, you can have variables that are available everywhere (global variables), variables that are available to all members of a certain class (class variables) i.e subclasses variables, and variables that are only available to particular instances of a class (instance variables/attributes/properties). Instance variables are for variable unique to each instance and class variables are for attributes and methods shared by all instances of the class. Classes can have class variables that store information about each class object. We call them member variables since they are information that belongs to the class object. Creating member variables and assigning them initial values is as easy as creating any other variable. Member of a class can access it outside the class with the dot operator; however inside the class we access it with self.memberVariable. However shared data can have possibly surprising effects with involving mutable objects such as lists and dictionaries. Never initialize a empty list [] or dict {} as a class/member variable, instead initialize it with the __init__(self) function.

class ClassName:
    memberVariable = "initialValue"

    def __init__(self, arg1, arg2):   # initializing instance attribute/variable
        self.arg1 = arg1
        self.arg2 = arg2

# Object instance of ClassName
NewClass = ClassName()

Special Methods using Dunder (Double Underscore)

Reference: https://docs.python.org/3/reference/datamodel.html#special-method-names

  • The def __str__(self) function converts outputs into string via string formatting.
  • The def __eq__(self) function compares object instances based on "logic of the method".
  • The def __repr__(self) function is one useful class method to override. It is short for representation; by providing a return value in this method, we can tell Python how to represent an object of our class (for instance, when using a print statement).

Having seen the mechanics behind the iterator protocol, it is easy to add iterator behavior to your classes. Define an __iter__() method which returns an object with a __next__() method. If the class defines __next__(), then __iter__() can just return self.

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

Static Method vs Class Method vs Instance Method

Reference: https://realpython.com/instance-class-and-static-methods-demystified/

class MyClass:
    def method(self):
        return "instance method called", self

    @classmethod # decorator
    def classmethod(cls):
        return "class method called", cls

    @staticmethod # decorator
    def staticmethod():
        return "static method called"

Please note that naming these parameters self and cls is just a convention. You could just as easily name them anything else. All that matters is that they're positioned first in the parameter list for the method. Static methods can neither access the object instance state nor the class state. They work like regular functions but belong to the class's (and every instance's) namespace.

Object Oriented Programming Principles

INHERITANCE -> https://github.com/CoreyMSchafer/code_snippets/blob/master/Object-Oriented/4-Inheritance/oop-finish.py Class inheritance allows us to define a sub-class that inherits all the methods and properties from another class. Inheritance is the process by which one class takes on the attributes and methods of another, and it's used to express an is-a relationship.

  • Parent class is the class being inherited from, also called base class.
  • Child class is the class that inherits from another class, also called derived class.

To create a class that inherits the functionality from another class, send the parent class as a parameter when creating the child class. You can always add more properties/attributes/parameters to the def __init__().

class childClass(parentClass):  # analogous to the extend keyword in Javascript
    def __init__(self, arg1, arg2, ex_arg1, ex_arg2): # constructor of child class instance variables and append parent class instance variable
        super().__init__(arg1, arg2) # keep the inheritance of the parent class instance variable
        self.ex_arg1 = ex_arg1
        self.ex_arg2 = ex_arg2

isinstance() returns a boolean value for checking if an object is an instance of a class. 1st arg and 2nd arg is object and class respectively. issubclass() returns a boolean value for checking if a childClass is an instance of parent class. 1st arg and 2nd arg is childClass and ParentClass respectively.

ENCAPSULATION A language mechanism for restricting direct access to some of the object's components. A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.

POLYMORPHISM The provision of a single interface to entities of different type or the use of a single symbol to represent multiple different types.

Python Decorators

In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope. The term closure is often used as a synonym for anonymous function, though strictly, an anonymous function is a function literal without a name, while a closure is an instance of a function, a value, whose non-local variables have been bound either to values or to storage locations.

function html_tag(tag){
    function wrap_text(msg){
        console.log('<' + tag + '>' + msg + '</' + tag + '>')
    }
    return wrap_text
}

html_tag('h1')('Test Headline!')
html_tag('p')('Test Paragraph!')

A higher-order function is a function that takes one or more functions as arguments, returns a function, or both. A particularly interesting subset of higher-order functions are higher-order functions that decorate a function. "Function Decorators" take a function as an argument, and return a new function that has semantically similar behaviour, but is "decorated" with some additional functionality.

Python Module

Reference: https://docs.python.org/3/py-modindex.html Consider a module to be the same as a code library/file containing a set of functions you want to include in your application. There are several built-in modules in Python, which you can import whenever you like. To create a module just save the code you want in a file with the file extension .py.

Use a Module -> Now we can use the module we just created, by using the import statement. When using a function from a module, use the syntax: module_name.function_name. The module can contain functions, as already described, but also variables of all types (arrays, dictionaries, objects etc), all can be accessed using dot . operator.

Using the dir() function -> There is a built-in function to list all the function names (or variable names) in a module. The dir() function. Note that it lists all types of names: variables, modules, functions, etc.

The built-in dir() and help() functions are useful as interactive aids for working with large modules like os:

>>> import module_name
>>> dir(moduleName)
<returns a list of all module functions>
>>> help(moduleName)
<returns an extensive manual page created from the module's docstrings>

dir() does not list the names of built-in functions and variables. If you want a list of those, they are defined in the standard module builtins:

>>> import builtins
>>> dir(builtins)

Re-naming a module (optional) -> You can create an alias when you import a module, by using the as keyword: import module_name as newName. Specified import from module -> You can choose to import only parts from a module, by using the from keyword. Syntax from <module_name> import <myFunc>. Note: When importing using the from keyword, do not use the module name when referring to elements in the module (i.e there's no need for using dot . operator), just call the respective elements by their name.

Python Dates

A date in Python is not a data type of its own, but we can import a module named datetime to work with dates as date objects. Syntax -> import datetime.

The datetime module has many methods to return information about the date object.

datetime.now() method -> returns the current date which contains year, month, day, hour, minute, second, and microsecond.

datetime() constructor method -> To create a date, the datetime() class requires three parameters to create a date: year, month, day. The datetime() class also takes parameters for time and timezone (hour, minute, second, microsecond, tzone), but they are optional, and has a default value of 0 (None for timezone).

strftime(format) method -> To formatting date objects into readable strings, takes one parameter (format) to specify the format of the returned string. A reference of all the legal format codes: https://www.w3schools.com/python/python_datetime.asp

Python Math

Python has a set of built-in math functions, including an extensive math module. To use it, you must import the math module (import math), that allows you to perform mathematical tasks on numbers. https://www.w3schools.com/python/module_math.asp The min() and max() functions can be used to find the lowest or highest value in an iterable. The abs() function returns the absolute (positive) value of the specified number. The pow(x, y) function returns the value of x to the power of y.

Python JSON

JSON is a syntax for storing and exchanging data. JSON is text, written with JavaScript object notation. Python has a built-in package called json, which can be used to work with JSON data. Import the json module: (import json). Rather than having users constantly writing and debugging code to save complicated data types to files, Python allows you to use the popular data interchange format called JSON (JavaScript Object Notation). The standard module called json can take Python data types/hierarchies, and convert them to string representations; this process is called serializing/encoding. Reconstructing the data from the string representation is called deserializing/decoding/parsing. Between serializing and deserializing, the string representing the object may have been stored in a file or data, or sent over a network connection to some distant machine.

Note: The JSON format is commonly used by modern applications to allow for data exchange. Many programmers are already familiar with it, which makes it a good choice for interoperability.

json.loads() -> Convert from JSON to Python. If you have a JSON string, you can parse it by using the json.loads() method. The result will be a dictionary.

json.dumps() -> Convert from Python to JSON. If you have a Python iterable object/data type, you can convert it into a JSON string by using the json.dumps() method. The result however isn't very readable and neatly formatted (i.e no line breaks and indentations). However the .dumps() method accepts optional parameters such as indent = #num and separators = (", ", ": ") and sort_keys=True/False.

json.dump() -> Another variant of the dumps() function, called dump(), simply encodes/serializes the python objects to a text file. So if f is a text file object opened for writing, we can do this: json.dump(x, f). To decode the object again, if f is a text file object which has been opened for reading: x = json.load(f). This simple serialization technique can handle lists and dictionaries, but serializing arbitrary class instances in JSON requires a bit of extra effort.

PIP

What is PIP? Analogous to NPM -> https://pypi.org/ PIP is a package manager for Python packages, or modules if you like. A package contains all the files you need for a module. Modules are Python code libraries you can include in your project. Downloading a package is very easy. Checking for pip version -> python3 -m pip --version. Upgrade python3 -m pip install --upgrade pip setuptools wheel. Visit the website link and search for packages for whatever. Open the command line interface and tell PIP to download the package you want using this syntax pip install <package> and to uninstall pip uninstall <package>.

Python File Handling (file I/O) -> CRUD

File handling is an important part of any web application. Python has several functions for creating, reading, updating, and deleting files. If files are located in a different location (i.e server) or folder, you will have to specify the absolute file path.

open(filename, mode) -> The key function for working with files in Python is the open() function. The open() function takes two parameters: filename and mode. The open method can be used to read, create and update/append using four different methods (modes) for opening a file, specifying how the file should be handled as binary or text mode. The first argument is a string containing the filename. The second argument is another string containing a few characters describing the way in which the file will be used. mode can be r when the file will only be read, w for only writing (an existing file with the same name will be erased), and a opens the file for appending; any data written to the file is automatically added to the end. r+ opens the file for both reading and writing. The mode argument is optional; r will be assumed if it's omitted.

Read and write mode (r+) || FILE HANDLE MODE

Mode Description
r Read - Default value. Opens a file for reading, error if the file does not exist
a Append - Opens a file for appending/updating (adding new data to the end of the file), creates the file if it does not exist
w Write - Opens a file for writing, creates the file if it does not exist
x Create - Creates the specified file, returns an error if the file exists
r+ Read and write mode
t Text - Default value. Text mode
b Binary - Binary mode (e.g. images)

read() -> The open() function with r mode returns a file object, which has a read() method for reading the content of the file. By default the read() method returns the whole text, but you can also specify how many characters/line you want to return. readline() method. Reading between the lines.

write() -> The open() function with w mode returns a file object, prepares Python to send data into the file by write() method for writing into the content of the file, creates the file if it doesn't exist.

create() -> The open() function with x mode returns a file object, prepares Python to create data into new file by create() method for creating the specified file, returns an error if the file exists.

Note: You should always close your files. In some cases, due to buffering, changes made to a file may not show until you close the file. Using the close() method. During the I/O process, data is buffered: this means that it is held in a temporary location before being written to the file. Python doesn't flush the buffer -- that is, write data to the file -- until it's sure you're done writing. One way to do this is to close the file. If you write to a file without closing, the data won't make it to the target file. Python file objects have a closed attribute which is True when the file is "closed" and False otherwise. By checking file_object.closed, we'll know whether our file is closed and can call close() on it if it's still open.

The with and as Keywords

Is there a way to get Python to automatically close our files for us? Of course there is. This is Python. You may not know this, but file objects contain a special pair of built-in methods: __enter__() and __exit__(). The details aren't important, but what is important is that when a file object's __exit__() method is invoked, it automatically closes the file. How do we invoke this method? With with and as.

The syntax looks like this:

with open("file", "mode") as variable:
    variable.read()/append()/create()/write()
    # Read or write to the file without having to use close() method

Delete

To delete a file, you must import the OS module, and run its os.remove() function/method.

import os
os.remove("filename.ext")
os.path.exists("filename.ext")
os.rmdir("foldername")

Note: To avoid getting an error, you might want to check if the file exists before you try to delete it. You can only remove empty folders.

Python Error Handling

Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions and are not unconditionally fatal: you will soon learn how to handle them in Python programs. Most exceptions are not handled by programs, however, and result in error messages displayed. The string printed as the exception type is the name of the built-in exception that occurred. This is true for all built-in exceptions, but need not be true for user-defined exceptions (although it is a useful convention). Standard exception names are built-in identifiers (not reserved keywords).

The try block lets you test a block of code for errors. The except block lets you handle the error. Analogous to catch in Javascript. The finally block, if specified, lets you execute code, regardless of the result of the try- and except blocks which is called a clean-up action that must be executed under all circumstances. The try ... except statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. You can use the else keyword to define a block of code to be executed if no errors were raised. The raise keyword. You can define what kind of error to raise/throw, and the text to print to the user. The raise statement allows the programmer to force a specified exception to occur.

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

As you can see, the finally clause is executed in any event. The TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed. In real world applications, the finally clause is useful for releasing external resources (such as files or network connections), regardless of whether the use of the resource was successful.

Error/Exception handling. When an error occurs, or exception as we call it, Python will normally stop and generate an error message. These exceptions/errors can be handled using the try statement. A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:

... except (RuntimeError, TypeError, NameError):
...     pass

Common Exceptions

Exception Description
ArithmeticError Raised when an error occurs in numeric calculations
AssertionError Raised when an assert statement fails
AttributeError Raised when attribute reference or assignment fails
Exception Base class for all exceptions
EOFError Raised when the input() method hits an "end of file" condition (EOF)
FloatingPointError Raised when a floating point calculation fails
GeneratorExit Raised when a generator is closed (with the close() method)
ImportError Raised when an imported module does not exist
IndentationError Raised when indentation is not correct
IndexError Raised when an index of a sequence does not exist
KeyError Raised when a key does not exist in a dictionary
KeyboardInterrupt Raised when the user presses Ctrl+c, Ctrl+z or Delete
LookupError Raised when errors raised cant be found
MemoryError Raised when a program runs out of memory
NameError Raised when a variable does not exist
NotImplementedError Raised when an abstract method requires an inherited class to override the method
OSError Raised when a system related operation causes an error
OverflowError Raised when the result of a numeric calculation is too large
ReferenceError Raised when a weak reference object does not exist
RuntimeError Raised when an error occurs that do not belong to any specific expections
StopIteration Raised when the next() method of an iterator has no further values
SyntaxError Raised when a syntax error occurs
TabError Raised when indentation consists of tabs or spaces
SystemError Raised when a system error occurs
SystemExit Raised when the sys.exit() function is called
TypeError Raised when two different types are combined
UnboundLocalError Raised when a local variable is referenced before assignment
UnicodeError Raised when a unicode problem occurs
UnicodeEncodeError Raised when a unicode encoding problem occurs
UnicodeDecodeError Raised when a unicode decoding problem occurs
UnicodeTranslateError Raised when a unicode translation problem occurs
ValueError Raised when there is a wrong value in a specified data type
ZeroDivisionError Raised when the second operator in a division is zero

Regular Expressions

Python has a built-in package called re, which can be used to work with Regular Expressions. Import the re module: import re. The re module offers a set of functions that allows us to search a string for a match:

Function Description
findall() Returns a list containing all matches
search() Returns a Match object if there is a match anywhere in the string. If there is no match, the value None will be returned, instead of the Match Object.
split() Returns a list where the string has been split at each match
sub() Replaces one or many matches with a string