diff --git a/Class.md b/Class.md new file mode 100644 index 0000000..b711ad8 --- /dev/null +++ b/Class.md @@ -0,0 +1,279 @@ +# Classes +Classes model the behavior of objects in the "real" world. Methods implement the behaviors of these types of objects. Member variables hold (current) state. Classes enable us to implement new data types in Python. + +The `class:` statement is used to define a class. The `class:` statement creates a class object and binds it to a name. +A simple class + +``` +class A: + pass + +a = A() +``` + +To define a new style class (recommended), inherit from object or from another class that does. Example: +``` +class A(object): +pass + + +a = A() +a +<__main__.A object at 0x82fbfcc> +``` +## Defining methods +A method is a function defined in class scope and with first parameter self: + +``` +class B(object): + def show(self): + print 'hello from B' + +b = B() +b.show() +hello from B +``` +A method as we describe it here is more properly called an instance method, in order to distinguish it from class methods and static methods. + +## The constructor + +The constructor is a method named __init__. + +``` +class A(object): + def __init__(self, name): + self.name = name + def show(self): + print 'name: "%s"' % self.name + +a = A('dave') +a.show() +name: "dave" +``` + +Notes: + + The self variable is explicit. It references the current object, that is the object whose method is currently executing. + The spelling ("self") is optional, but everyone spells it that way. + +## Member variables + +Defining member variables -- Member variables are created with assignment. Example: + +``` +class A(object): + def __init__(self, name): + self.name = name +``` +A small gotcha -- Do this: +``` +class A(object): + def __init__(self, items=None): + if items is None: + self.items = [] + else: + self.items = items +``` +Do not do this: +``` + class B: + def __init__(self, items=[]): # wrong. list ctor evaluated only once. + self.items = items + +``` + +In the second example, the def statement and the list constructor are evaluated only once. Therefore, the item member variable of all instances of class B, will share the same value, which is most likely not what you want. + + +## Calling methods + * Use the instance and the dot operator. + * Calling a method defined in the same class or a superclass: + * From outside the class -- Use the instance: + +``` + some_object.some_method() + an_array_of_of_objects[1].another_method() +``` +From within the same class -- Use self: + +``` + self.a_method() +``` +From with a subclass when the method is in the superclass and there is a method with the same name in the current class -- Use the class (name) or use super: + +``` + SomeSuperClass.__init__(self, arg1, arg2) super(CurrentClass, + self).__init__(arg1, arg2) +``` +Calling a method defined in a specific superclass -- Use the class (name). + +## Class variables + + * Also called static data. + + * A class variable is shared by instances of the class. + +Define at class level with assignment. Example: + +``` + class A(object): + size = 5 + def get_size(self): + return A.size +``` + Reference with classname.variable. +``` + Caution: self.variable = x creates a new member variable. +``` +## Class methods and static methods + +### Instance (plain) methods: + + An instance method receives the instance as its first argument. + +### Class methods: + * A class method receives the class as its first argument. + * Define class methods with built-in function classmethod() or with decorator @classmethod. + * See the description of classmethod() built-in function at "Built-in Functions": http://docs.python.org/2/library/functions.html#classmethod + +``` +class C(object): + @classmethod + def fun(cls, arg1, arg2, ...): + .... +fun: function that needs to be converted into a class method +returns: a class method for function. +``` + +### Static methods: + * A static method receives neither the instance nor the class as its first argument. + * Define static methods with built-in function staticmethod() or with decorator @staticmethod. + * See the description of staticmethod() built-in function at "Built-in Functions": http://docs.python.org/2/library/functions.html#staticmethod + +``` +class C(object): + @staticmethod + def fun(arg1, arg2, ...): + ... +returns: a static method for function fun. + +``` + + +## Inheritance + +In order to inherit fro, an existing class, use the following syntax: +``` +class Animal(object): + def __init__(self, name="unknown"): + self.name = name + def __str__(self): + print("I'm a {0}".format(self.name)) + +def Cat(Animal): + def __init__(self): + super().__init__(name='CAT') +``` +Here, the parent’s class Animal is initialised inside the child class using the super keyword. The Cat inherits from Animal and therefore has the method print already available. +## Multiple inheritance + +if Python cannot find a variable or method in the local namespace, it will perform a depth first search of the super classes in the same order in which the superclasses are specified in the class definition. + +That’s all you need to know !! Let us study an example: +``` +class Vehicle(object): + def __init__(self, name, color, wheel): + self.name = name + self.wheels = None + self.color = None + + def set_wheels(self, n): + self.wheels = 2 + + def __str__(self): + txt = str(self.name) + ":" + registration + return txt + +class TwoWheeler(object): + + def __init__(self, name, color): + self.name = name + self.set_wheels(2) + self.color = None + + def __str__(self): + return self.wheels + +class MotorVehicle(object): + + def __init__(self, name, color, power): + self.name = name + self.set_wheels(2) + self.color = None + self.power + +class Bicycle(TwoWheeler, gear): + + def __init__(self, name, color, gear): + self.name = name + self.set_wheels(2) + self.color = None +``` + + +## property + +The property built-in function enables us to write classes in a way that does not require a user of the class to use getters and setters. Example: + +``` +class Person: + def __init__(self, height): + self.height = height + + def get_height(self): + return self.height + + def set_height(self, height): + self.height = height + +jane = Person(153) # Jane is 153cm tall + +jane.height += 1 # Jane grows by a centimetre +jane.set_height(jane.height + 1) # Jane grows again + +``` + +The property built-in function is also a decorator. So, the following is equivalent to the above example: + +``` +class Person: + def __init__(self, name, surname): + self.name = name + self.surname = surname + + @property + def fullname(self): + return "%s %s" % (self.name, self.surname) + + @fullname.setter + def fullname(self, value): + # this is much more complicated in real life + name, surname = value.split(" ", 1) + self.name = name + self.surname = surname + + @fullname.deleter + def fullname(self): + del self.name + del self.surname + +jane = Person("Jane", "Smith") +print(jane.fullname) + +jane.fullname = "Jane Doe" +print(jane.fullname) +print(jane.name) +print(jane.surname) + +``` + diff --git a/Collections.md b/Collections.md new file mode 100644 index 0000000..7c3fabf --- /dev/null +++ b/Collections.md @@ -0,0 +1,653 @@ +# Collections + +## List + * Lists are a container data type that acts as a dynamic array. + * The Python list type is called list. It is a type of sequence – we can use it to store multiple values, and access them sequentially, by their position, or index, in the list. We define a list literal by putting a comma-separated list of values inside square brackets ([ and ]): + * A list has a (current) length -- Get the length of a list with len(mylist). + * A list has an order -- The items in a list are ordered, and you can think of that order as going from left to right. + * A list is heterogeneous -- You can insert different types of objects into the same list. + * Lists are mutable, + +``` +# a list of strings +animals = ['cat', 'dog', 'fish', 'bison'] + +# a list of integers +numbers = [1, 7, 34, 20, 12] + +# an empty list +my_list = [] + +# a list of variables we defined somewhere else +things = [ + one_variable, + another_variable, + third_variable] +``` + +eg: +``` +>>> l = [1, 2, 3] +>>> l[0] +1 +>>> l.append(1) +>>> l +[1, 2, 3, 1] +``` + +## Built in methods in List +### Difference between append() and extend() + +Lists have several methods amongst which the append() and extend() methods. The former appends an object to the end of the list (e.g., another list) while the later appends each element of the iterable object (e.g., anothee list) to the end of the list. + +For example, we can append an object (here the character ‘c’) to the end of a simple list as follows: +``` +>>> stack = ['a','b'] +>>> stack.append('c') +>>> stack +['a', 'b', 'c'] +``` +However, if we want to append several objects contained in a list, the result as expected (or not...) is: +``` +>>> stack.append(['d', 'e', 'f']) +>>> stack +['a', 'b', 'c', ['d', 'e', 'f']] +``` + +The object ['d', 'e', 'f'] has been appended to the exiistng list. However, it happens that sometimes what we want is to append the elements one by one of a given list rather the list itself. You can do that manually of course, but a better solution is to use the extend() method as follows: +``` +>>> stack = ['a', 'b', 'c'] +>>> stack.extend(['d', 'e','f']) +>>> stack +['a', 'b', 'c', 'd', 'e', 'f'] +``` +## Other list methods +### index +The index() methods searches for an element in a list. For instance: +``` +>>> my_list = ['a','b','c','b', 'a'] +>>> my_list.index('b') +1 +``` +It returns the index of the first and only occurence of ‘b’. If you want to specify a range of valid index, you can indicate the start and stop indices: +``` +>>> my_list = ['a','b','c','b', 'a'] +>>> my_list.index('b', 2) +3 +``` +Warning + +if the element is not found, an error is raised + +### insert +You can remove element but also insert element wherever you want in a list: +``` +>>> my_list.insert(2, 'a') +>>> my_list +['b', 'c', 'a', 'b'] +``` +The insert() methods insert an object before the index provided. + +### remove +Similarly, you can remove the first occurence of an element as follows: +``` +>>> my_list.remove('a') +>>> my_list +['b', 'c', 'b', 'a'] +``` +### pop +Or remove the last element of a list by using: +``` +>>> my_list.pop() +'a' +>>> my_list +['b', 'c', 'b'] +```` +which also returns the value that has been removed. + +### count +You can count the number of element of a kind: +``` +>>> my_list.count('b') +2 +``` +### sort +There is a sort() method that performs an in-place sorting: +``` +>>> my_list.sort() +>>> my_list +['a', 'b', 'b', 'c'] +``` + +There is also the possiblity to sort in the reverse order: +``` +>>> my_list.sort(reverse=True) +>>> my_list +['c', 'b', 'b', 'a'] +``` +### reverse +Finally, you can reverse the element in-place: +``` +>>> my_list = ['a', 'c' ,'b'] +>>> my_list.reverse() +>>> my_list +['b', 'c', 'a'] +``` +### Operators +The + operator can be used to extend a list: +``` +>>> my_list = [1] +>>> my_list += [2] +>>> my_list +[1, 2] +>>> my_list += [3, 4] +>>> my_list +[1, 2, 3, 4] +``` + +The * operator ease the creation of list with similar values + +``` +>>> my_list = [1, 2] +>>> my_list = my_list * 2 +>>> my_list +[1, 2, 1, 2] +``` +### Slicing +Slicing uses the symbol : to access to part of a list: +``` +>>> list[first index:last index:step] +>>> list[:] +>>> a = [0, 1, 2, 3, 4, 5] +[0, 1, 2, 3, 4, 5] +>>> a[2:] +[2, 3, 4, 5] +>>> a[:2] +[0, 1] +>>> a[2:-1] +[2, 3, 4] +``` +By default the first index is 0, the last index is the last one..., and the step is 1. The step is optional. So the following slicing are equivalent: +``` +>>> a = [1, 2, 3, 4, 5, 6, 7, 8] +>>> a[:] +[1, 2, 3, 4, 5, 6, 7, 8] +>>> a[::1] +[1, 2, 3, 4, 5, 6, 7, 8] +>>> a[0::1] +[1, 2, 3, 4, 5, 6, 7, 8] +``` +### List comprehension +Traditionally, a piece of code that loops over a sequence could be written as follows: +``` +>>> evens = [] +>>> for i in range(10): +... if i % 2 == 0: +... evens.append(i) +>>> evens +[0, 2, 4, 6, 8] +``` +This may work, but it actually makes things slower for Python because the interpreter works on each loop to determine what part of the sequence has to be changed. + +A list comprehension is the correct answer: +``` +>>> [i for i in range(10) if i % 2 == 0] +[0, 2, 4, 6, 8] +``` +Besides the fact that it is more efficient, it is also shorter and involves fewer elements. + +### Filtering Lists +``` +>>> li = [1, 2] +>>> [elem*2 for elem in li if elem>1] +[4] +``` +### Lists as Stacks +The Python documentation gives an example of how to use lists as stacks, that is a last-in, first-out data structures (LIFO). + +An item can be added to a list by using the append() method. The last item can be removed from the list by using the pop() method without passing any index to it. +``` +>>> stack = ['a','b','c','d'] +>>> stack.append('e') +>>> stack.append('f') +>>> stack +['a', 'b', 'c', 'd', 'e', 'f'] +>>> stack.pop() +'f' +>>> stack +['a, 'b', 'c', 'd', 'e'] +``` +### Lists as Queues +Another usage of list, again presented in Python documentation is to use lists as queues, that is a first in - first out (FIFO). +``` +>>> queue = ['a', 'b', 'c', 'd'] +>>> queue.append('e') +>>> queue.append('f') +>>> queue +['a', 'b', 'c', 'd', 'e', 'f'] +>>> queue.pop(0) +'a' +``` +### How to copy a list +There are three ways to copy a list: +``` +>>> l2 = list(l) +>>> l2 = l[:] +>>> import copy +>>> l2 = copy.copy(l) +``` +Warning +Don’t do l2 = l, which is a reference, not a copy. +The preceding techniques for copying a list create shallow copies. IT means that nested objects will not be copied. Consider this example: +``` +>>> a = [1, 2, [3, 4]] +>>> b = a[:] +>>> a[2][0] = 10 +>>> a +[1, 2, [10, 4]] +>>> b +[1, 2, [10, 4]] +``` +To get around this problem, you must perform a deep copy: +``` +>>> import copy +>>> a = [1, 2, [3, 4]] +>>> b = copy.deepcopy(a) +>>> a[2][0] = 10 +>>> a +[1, 2, [10, 4]] +>>> b +[1, 2, [3, 4]] +``` +### Inserting items into a sorted list +The bisect module provides tools to manipulate sorted lists. +``` +>>> x = [4, 1] +>>> x.sort() +>>> import bisect +>>> bisect.insort(x, 2) +>>> x +[1, 2, 4] +``` +To know where the index where the value would have been inserted, you could have use: +``` +>>> x = [4, 1] +>>> x.sort() +>>> import bisect +>>> bisect.bisect(x, 2) +2 + +``` + + +# Tuples +In Python, tuples are part of the standard language. This is a data structure very similar to the list data structure. The main difference being that tuple manipulation are faster than list because tuples are immutable. + +## Constructing tuples +To create a tuple, place values within brackets: +``` +>>> l = (1, 2, 3) +>>> l[0] +1 +``` +It is also possible to create a tuple without parentheses, by using commas: +``` +>>> l = 1, 2 +>>> l +(1, 2) +``` +If you want to create a tuple with a single element, you must use the comma: +``` +>>> singleton = (1, ) +``` +You can repeat a tuples by multiplying a tuple by a number: +``` +>>> (1,) * 5 +(1, 1, 1, 1, 1) +``` +Note that you can concatenate tuples and use augmented assignement (*=, +=): +``` +>>> s1 = (1,0) +>>> s1 += (1,) +>>> s1 +(1, 0, 1) +``` +## Tuple methods +Tuples are optimised, which makes them very simple objects. There are two methods available only: + +index, to find occurence of a value +count, to count the number of occurence of a value +``` +>>> l = (1,2,3,1) +>>> l.count(1) +2 +>>> l.index(2) +1 +``` +## Interests of tuples +So, Tuples are useful because there are + +faster than lists +protect the data, which is immutable +tuples can be used as keys on dictionaries +In addition, it can be used in different useful ways: + +## Tuples as key/value pairs to build dictionaries +``` +>>> d = dict([('jan', 1), ('feb', 2), ('march', 3)]) +>>> d['feb'] +2 +``` + +## signing multiple values +``` +>>> (x,y,z) = ('a','b','c') +>>> x +'a' +>>> (x,y,z) = range(3) +>>> x +0 +``` +## Tuple Unpacking +Tuple unpacking allows to extract tuple elements automatically is the list of variables on the left has the same number of elements as the length of the tuple +``` +>>> data = (1,2,3) +>>> x, y, z = data +>>> x +1 +``` +## Tuple can be use as swap function +This code reverses the contents of 2 variables x and y: +``` +>>> (x,y) = (y,x) +``` +Warning +Consider the following function: +``` +def swap(a, b): + (b, a) = (a, b) +``` +then: +``` +a = 2 +b = 3 +swap(a, b) +#a is still 2 and b still 3 !! a and b are indeed passed by value not reference. +``` + +## length +To find the length of a tuple, you can use the len() function: +``` +>>> t= (1,2) +>>> len(t) +2 +``` +## Slicing (extracting a segment) +``` +>>> t = (1,2,3,4,5) +>>> t[2:] +(3, 4, 5) +``` +## Copy a tuple +To copy a tuple, just use the assignement: +``` +>>> t = (1, 2, 3, 4, 5) +>>> newt = t +>>> t[0] = 5 +>>> newt +(1, 2, 3, 4, 5) +``` +Warning + +You cannot copy a list with the = sign because lists are mutables. The = sign creates a reference not a copy. Tuples are immutable therefore a = sign does not create a reference but a copy as expected. + +## Tuple are not fully immutable !! +If a value within a tuple is mutable, then you can change it: +``` +>>> t = (1, 2, [3, 10]) +>>> t[2][0] = 9 +>>> t +(1, 2, [9, 10]) +``` +## Convert a tuple to a string +You can convert a tuple to a string with either: +``` +>>> str(t) +or + +>>> `t` +``` +## math and comparison +comparison operators and mathematical functions can be used on tuples. Here are some examples: +``` +>>> t = (1, 2, 3) +>>> max(t) +3 +``` + + +# Dicts +A dictionary is a sequence of items. Each item is a pair made of a key and a value. Dictionaries are not sorted. You can access to the list of keys or values independently. +``` +>>> d = {'first':'string value', 'second':[1,2]} +>>> d.keys() +['first', 'second'] +>>> d.values() +['string value', [1, 2]] +``` +You can access to the value of a given key as follows: +``` +>>> d['first'] +'string value' +``` +Warning +You can not have duplicate keys in a dictionary +dictionary have no concept of order among elements + +## Methods to query information +In addition to keys and values methods, there is also the items method that returns a list of items of the form (key, value). The items are not returned in any particular order: +``` +>>> d = {'first':'string value', 'second':[1,2]} +>>> d.items() +[('a', 'string value'), ('b', [1, 2])] +``` +The iteritems method works in much the same way, but returns an iterator instead of a list. See iterators section for an example. +You can check for the existence of a specific key with has_key: +``` +>>> d.has_key('first') +True +``` +The expression d.has_key(k) is equivalent to k in d. The choice of which to use is largely a matter of taste. + +In order to get the value corresponding to a specific key, use get or pop: +``` +>>> d.get('first') # this method can set an optional value, if the key is not found +'string value' +``` +It is useful for things like adding up numbers: +``` +sum[value] = sum.get(value, 0) + 1 +``` +## setdefault, collections + +The difference between get and pop is that pop also removes the corresponding item from the dictionary: +``` +>>> d.pop('first') +'string value' +>>> d +{'second': [1, 2]} +``` +Finally, popitem removes and returns a pair (key, value); you do not choose which one because a dictionary is not sorted +``` +>>> d.popitem() +('a', 'string value') +>>> d +{'second': [1, 2]} + +``` +## Methods to create new dictionary +Since dictionaries (like other sequences) are objects, you should be careful when using the affectation sign: +``` +>>> d1 = {'a': [1,2]} +>>> d2 = d1 +>>> d2['a'] = [1,2,3,4] +>>> d1['a] +[1,2,3,4] +``` +To create a new object, use the copy method (shallow copy): +``` +>>> d2 = d1.copy() +``` +See also +``` +copy.deepcopy() +``` +You can clear a dictionary (i.e., remove all its items) using the clear() method: +``` +>>> d2.clear() +{} +``` +The clear() method deletes all items whereas del() deletes just one: +``` +>>> d = {'a':1, 'b':2, 'c':3} +>>> del d['a'] +>>> d.clear() +``` +Create a new item with default value (if not provided, None is the default): +``` +>>> d2.setdefault('third', '') +>>> d2['third'] +'' +``` +Create a dictionary given a set of keys: +``` +>>> d2.fromkeys(['first', 'second']) +``` +another syntax is to start from an empty dictionary: +``` +>>> {}.fromkeys(['first', 'second']) +{'first': None, 'second': None} +``` +Just keep in ,ind thqt the fromkeys() method creates a new dictionary with the given keys, each with a default corresponding value of None. + +## Combining dictionaries +Given 2 dictionaries d1 and d2, you can add all pairs of key/value from d2 into d1 by using the update method (instead of looping and assigning each pair yourself: +``` +>>> d1 = {'a':1} +>>> d2 = {'a':2; 'b':2} +>>> d1.update(d2) +>>> d1['a'] +2 +>>> d2['b'] +2 +``` +The items in the supplied dictionary are added to the old one, overwriting any items there with the same keys. + +## iterators +Dictionary provides iterators over values, keys or items: +``` +>>> [x for x in t.itervalues()] +['string value', [1, 2]] +>>> +>>> [x for x in t.iterkeys()] +['first', 'csecond'] +>>> [x for x in t.iteritems()] +[('a', 'string value'), ('b', [1, 2])] +``` +## Views +viewkeys, viewvalues, viewitems are set-like objects providing a view on D’s keys, values or items. + +# comparison +you can compare dictionaries! Python first compares the sorted key-value pairs. It first sorts dictionaries by key and comapre their initial items. If the items hae different values, Python figures out the comparison between them. Otherwise, the second items are compared and so on. + + + +## Sets + +Sets are constructed from a sequence (or some other iterable object). Since sets cannot have duplicated, there are usually used to build sequence of unique items (e.g., set of identifiers). +``` +4.5.1. Quick example +>>> a = set([1, 2, 3, 4]) +>>> b = set([3, 4, 5, 6]) +>>> a | b # Union +{1, 2, 3, 4, 5, 6} +>>> a & b # Intersection +{3, 4} +>>> a < b # Subset +False +>>> a - b # Difference +{1, 2} +>>> a ^ b # Symmetric Difference +{1, 2, 5, 6} +``` +Note + +the intersection, subset, difference and symmetric difference can be be called with method rather that symbols. See below for examples. + +## Ordering +Just as with dictionaries, the ordering of set elements is quite arbitrary, and shouldn’t be relied on. + +## Operators +As mentionned in the quick example section, each operator is associated to a symbol (e.g., &) and a method name (e.g. union). +``` +>>> a = set([1, 2, 3]) +>>> b = set([2, 3, 4]) +>>> c = a.intersection(b) # equivalent to c = a & b +>>> a.intersection(b) +set([2, 3]) +>>> c.issubset(a) +True +>>> c <= a +True +>>> c.issuperset(a) +False +>>> c >= a +False +>>> a.difference(b) +set([1]) +>>> a - b +set([1]) +>>> a.symmetric_difference(b) +set([1, 4]) +>>> a ^ b +set([1, 4]) +``` +You can also copy a set using the copy method: +``` +>>> a.copy() +set([1, 2, 3]) +``` + +Frozensets +Sets are mutable, and may therefore not be used, for example, as keys in dictionaries. + +Another problem is that sets themselves may only contain immutable (hashable) values, and thus may not contain other sets. + +Because sets of sets often occur in practice, there is the frozenset type, which represents immutable (and, therefore, hashable) sets. + +Quick Example +``` +>>> a = frozenset([1, 2, 3]) +>>> b = frozenset([2, 3, 4]) +>>> a.union(b) +frozenset([1, 2, 3, 4]) +``` +## Set of Sets +Sets may only contain immutable (hashable) values, and thus may not contain other sets, in which case frozensets can be useful. Consider the following example: +``` +>>> a = set([1, 2, 3]) +>>> b = set([2, 3, 4]) +>>> a.add(b) +>>> a.add(frozenset(b)) +``` +## Using set as key in a dictionary +If you want to use a set as a key dictionary, you will need frozenset: +``` +>>> fa = {frozenset([1,2]): 1} +>>> fa[ frozenset([1,2]) ] +1 +``` +### Methods +frozensets have less methods than sets. +There are some operators similar to sets (intersection(), union(), symmetric_difference(), difference(), issubset(), isdisjoint(), issuperset()) and a copy() method. + diff --git a/Condition.md b/Condition.md new file mode 100644 index 0000000..47feb23 --- /dev/null +++ b/Condition.md @@ -0,0 +1,111 @@ +# Conditional execution + +## Boolean expressions + + * A boolean expression is an expression that is either true or false. + +``` +>>>5 == 5 +True +>>>5 == 6 +False +``` +True and False are special values that belong to the class bool ; they are not strings: +``` +>>> type(True) + +>>> type(False) +< class 'bool'> +``` + +The == operator is one of the comparison operators ; the others are: + +``` + x != y # x is not equal to y + x > y # x is greater than y + x < y # x is less than y + x >= y # x is greater than or equal to y + x <= y # x is less than or equal to y + x is y # x is the same as y + x is not y # x is not the same as y + +``` + +Logical operators: + +``` +and or is not in + +``` + +``` +eg: + +x > 0 and x < 10 + +is true only if x is greater than 0 and less than 10 + +>>> 17 and True +True +``` +## If statement +The if statement is a compound statement that enables us to conditionally execute blocks of code. + +The syntax looks like +``` +if expression: + do this + +``` + +``` +if x > 0: + print('x is positive') +``` + +The boolean expression after the if statement is called the condition . We end the if statement with a colon character (:) and the line(s) after the if statement are indented. + +![If-condition](diagram-1.png) + +## Alternative execution(IF-ELSE) + +A second form of the if statement is alternative execution , in which there are two possibilities and the condition determines which one gets executed. + +The syntax looks like this: +``` +if x% 2 ==0 : + print('x is even') +else: + print('x is odd') +``` + +![Screenshot](diagram-2.png) + +## Chained Condition(IF-ELIF-ELSE) + +Sometimes there are more than two possibilities and we need more than two branches. One way to express a computation like that is a +chained conditional : +``` +if x < y: + print('x is less than y') +elif x > y: + print('x is greater than y') +else: + print('x and y are equal') +``` +![Screenshot](diagram-3.png) + +## Nested conditionals + +One conditional can also be nested within another. We could have written the three-branch example like this: +``` +if x == y: + print('x and y are equal') +else: + if x < y: + print('x is less than y') + else: + print('x is greater than y') +``` +![Screenshot](diagram-4.png) + diff --git a/Exception_Handling.md b/Exception_Handling.md new file mode 100644 index 0000000..7dac5b6 --- /dev/null +++ b/Exception_Handling.md @@ -0,0 +1,149 @@ +# Errors and exceptions + +Errors or mistakes in a program are often referred to as bugs. They are almost always the fault of the programmer. The process of finding and eliminating errors is called debugging. Errors can be classified into three major groups: + + Syntax errors + Runtime errors + Logical errors + +## Syntax errors + +Python will find these kinds of errors when it tries to parse your program, and exit with an error message without running anything. +* leaving out a keyword +* putting a keyword in the wrong place +* leaving out a symbol, such as a colon, comma or brackets +* misspelling a keyword +* incorrect indentation +* empty block + + +``` +myfunction(x, y): + return x + y + +else: + print("Hello!") + +if mark >= 50 + print("You passed!") + +if arriving: + print("Hi!") +esle: + print("Bye!") + +if flag: +print("Flag is set!") + + +``` + +## Runtime errors + +If a program is syntactically correct – that is, free of syntax errors – it will be run by the Python interpreter. + +Some examples of Python runtime errors: + +* division by zero +* performing an operation on incompatible types +* using an identifier which has not been defined +* accessing a list element, dictionary value or object attribute which doesn’t exist +* trying to access a file which doesn’t exist + + +## Logical errors + +Logical errors are the most difficult to fix. They occur when the program runs without crashing, but produces an incorrect result. + + +* using the wrong variable name +* indenting a block to the wrong level +* using integer division instead of floating-point division +* getting operator precedence wrong +* making a mistake in a boolean expression +* off-by-one, and other numerical errors + + +### How to handle exceptions? + +We use try...except blocks to handle any exception. The basic syntax looks like + +``` +try: + statements to be inside try clause + statement2 + statement3 + ... +except ExceptionName: + statements to evaluated in case of ExceptionName happens +``` + + +It works in the following way: + +* First all lines between try and except statements. +* If ExceptionName happens during execution of the statements then except clause statements execute +* If no exception happens then the statements inside except clause does not execute. +* If the Exception is not handled in the except block then it goes out of try block. + + + +``` +>>> def get_number(): +... "Returns a float number" +... number = float(input("Enter a float number: ")) +... return number +... +>>> +>>> while True: +... try: +... print(get_number()) +... except ValueError: +... print("You entered a wrong value.") +... +Enter a float number: 45.0 +45.0 +Enter a float number: 24,0 +You entered a wrong value. +Enter a float number: Traceback (most recent call last): + File "", line 3, in + File "", line 3, in get_number +KeyboardInterrupt +``` + +## Raising exceptions + +One can raise an exception using raise statement. + +``` +>>> raise ValueError("A value error happened.") +Traceback (most recent call last): + File "", line 1, in +ValueError: A value error happened. +``` +We can catch these exceptions like any other normal exceptions. +``` +>>> try: +... raise ValueError("A value error happened.") +... except ValueError: +... print("ValueError in our code.") +... +ValueError in our code. +``` +## Using finally for cleanup + +If we want to have some statements which must be executed under all circumstances, we can use finally clause, it will be always executed before finishing try statements. +``` +>>> try: +... fobj = open("hello.txt", "w") +... res = 12 / 0 +... except ZeroDivisionError: +... print("We have an error in division") +... finally: +... fobj.close() +... print("Closing the file object.") +... +We have an error in division +Closing the file object. +``` +In this example we are making sure that the file object we open, must get closed in the finally clause. diff --git a/File_handling.md b/File_handling.md new file mode 100644 index 0000000..00cc2bb --- /dev/null +++ b/File_handling.md @@ -0,0 +1,106 @@ +# File handling +A file is some information or data which stays in the computer storage devices. + +File opening + +To open a file we use open() function. It requires two arguments, first the file path or file name, second which mode it should open. Modes are like + +* “r” -> open read only, you can read the file but can not edit / delete anything inside +* “w” -> open with write power, means if the file exists then delete all content and open it to write +* “a” -> open in append mode + +The default mode is read only, ie if you do not provide any mode it will open the file as read only. Let us open a file +``` +>>> fobj = open("love.txt") +>>> fobj +<_io.TextIOWrapper name='love.txt' mode='r' encoding='UTF-8'> +``` +## Closing a file + +After opening a file one should always close the opened file. We use method close() for this. +``` +>>> fobj = open("love.txt") +>>> fobj +<_io.TextIOWrapper name='love.txt' mode='r' encoding='UTF-8'> +>>> fobj.close() +``` + +To read the whole file at once use the read() method. +``` +>>> fobj = open("sample.txt") +>>> fobj.read() +'I love Python\nPradeepto loves KDE\nSankarshan loves Openoffice\n' +``` +If you call read() again it will return empty string as it already read the whole file. readline() can help you to read one line each time from the file. +``` +>>> fobj = open("sample.txt") +>>> fobj.readline() +'I love Python\n' +>>> fobj.readline() +'Pradeepto loves KDE\n' +``` +To read all the lines in a list we use readlines() method. +``` +>>> fobj = open("sample.txt") +>>> fobj.readlines() +['I love Python\n', 'Pradeepto loves KDE\n', 'Sankarshan loves Openoffice\n'] +``` +You can even loop through the lines in a file object. +``` +>>> fobj = open("sample.txt") +>>> for x in fobj: +... print(x,) +... +I love Python +Pradeepto loves KDE +Sankarshan loves Openoffice +``` +Let us write a program which will take the file name as the input from the user and show the content of the file in the console. + +``` +name = input("Enter the file name: ") +fobj = open(name) +print(fobj.read()) +fobj.close() +``` +In the last line you can see that we closed the file object with the help of close() method. + +The output +``` +$ ./showfile.py +Enter the filename: sample.txt +I love Python +Pradeepto loves KDE +Sankarshan loves Openoffice +``` +### Using the with statement + +In real life scenarios we should try to use with statement. It will take care of closing the file for you. +``` +>>> with open('setup.py') as fobj: +... for line in fobj: +... print line, +... +``` +### Writing in a file + +Let us open a file then we will write some random text into it by using the write() method. +``` +>>> fobj = open("ircnicks.txt", 'w') +>>> fobj.write('powerpork\n') +>>> fobj.write('indrag\n') +>>> fobj.write('mishti\n') +>>> fobj.write('sankarshan') +>>> fobj.close() +``` +Now read the file we just created +``` +>>>fobj = open('ircnicks.txt') +>>> s = fobj.read() +>>> print(s) +powerpork +indrag +mishti +sankarshan + +``` diff --git a/Introduction.md b/Introduction.md new file mode 100644 index 0000000..3f54aab --- /dev/null +++ b/Introduction.md @@ -0,0 +1,302 @@ +# what is python ? + * Python is a high-level general purpose programming language: + * Because code is automatically compiled to byte code and executed, Python is suitable for use as a scripting language, Web application implementation language, etc. + * Because Python can be extended in C and C++ + * Important features of Python: + * Built-in high level data types: strings, lists, dictionaries, etc. + * The usual control structures: if, if-else, if-elif-else, while, plus a powerful collection iterator (for). + * Multiple levels of organizational structure: functions, classes, modules, and packages. These assist in organizing code. An excellent and large example is the Python standard library. + * Some things you will need to know: + + Python uses indentation to show block structure. Indent one level to show the beginning of a block. Out-dent one level to show the end of a block. As an example, the following C-style code: +``` + if (x) + { + if (y) + { + f1() + } + f2() + } +``` +in Python would be: +``` + if x: + if y: + f1() + f2() +``` +And, the convention is to use four spaces (and no hard tabs) for each level of indentation. Actually, it's more than a convention; it's practically a requirement. Following that "convention" will make it so much easier to merge your Python code with code from other sources. + +## An overview of Python: + + * A scripting language -- Python is suitable (1) for embedding, (2) for writing small unstructured scripts, (3) for "quick and dirty" programs. + + * Not a scripting language -- (1) Python scales. (2) Python encourages us to write code that is clear and well-structured. + + * Interpreted, but also compiled to byte-code. Modules are automatically compiled (to .pyc) when imported, but may also be explicitly compiled. + + * Provides an interactive command line and interpreter shell. In fact, there are several. + + * Dynamic -- For example: + * Types are bound to values, not to variables. + * Function and method lookup is done at runtime. + * Values are inspect-able. + * There is an interactive interpreter, more than one, in fact. + * You can list the methods supported by any given object. + + * Strongly typed at run-time, not compile-time. Objects (values) have a type, but variables do not. + + * Reasonably high level -- High level built-in data types; high level control structures (for walking lists and iterators, for example). + + * Object-oriented -- Almost everything is an object. Simple object definition. Data hiding by agreement. Multiple inheritance. Interfaces by convention. Polymorphism. + + * Highly structured -- Statements, functions, classes, modules, and packages enable us to write large, well-structured applications. Why structure? Readability, locate-ability, modifiability. + + * Indented block structure -- "Python is pseudo-code that runs." + + * Automatic garbage collection. (But, there is a gc module to allow explicit control of garbage collection.) + ## Python interpreter +### There are two modes for using the Python interpreter: + * Interactive Mode + * Script Mode + +### Interactive Mode + +Without passing python script file to the interpreter, directly execute code to Python prompt. +Example: +``` +>>>2+6 +``` +Output: +``` +8 +``` +### Script Mode +programmers can store Python script source code in a file with the .py extension, and use the interpreter to execute the contents of the file. To execute the script by the interpreter, you have to tell the interpreter the name of the file. For example, if you have a script name MyFile.py and you’re working on Unix, to run the script you have to type: +``` +python MyFile.py +``` + +## Comments +Comments are any text to the right of the # symbol and is mainly useful as notes for the reader of the program. +eg: +``` + print('hello world') # Note that print is a function + + or + # Note that print is a function + print('hello world') +``` +### Use as many useful comments as you can in your program to: + * explain assumptions + * explain important decisions + * explain important details + * explain problems you're trying to solve + * explain problems you're trying to overcome in your program, etc +### types of comments +* single line comment use (#) +* Multiple line comments use(''' or """) + +## Indentation + * Whitespace is important in Python. Actually, whitespace at the beginning of the line is important. This is called indentation. + * Leading whitespace (spaces and tabs) at the beginning of the logical line is used to determine the indentation level of the logical line, which in turn is used to determine the grouping of statements. + * This means that statements which go together must have the same indentation.Each such set of statements is called a block + +``` + i = 5 + # Error below! Notice a single space at the start of the line + print('Value is', i) + print('I repeat, the value is', i) + +``` +output: +``` + + File "whitespace.py", line 3 + print('Value is', i) + ^ + IndentationError: unexpected indent +``` +## How to indent ? + * Use four spaces for indentation. This is the official Python language recommendation. Good editors will automatically do this for you.like pycharm,atom,sublime,eclipse..etc + + + +## Literal Constants + * An example of a literal constant is a number like 5, 1.23, or a string like 'This is a string' or "It's a string!" +## Numbers + * Numbers are mainly of two types - integers and floats. + * There is no separate long type. The int type can be an integer of any size. + +eg: + An example of an integer is 2 which is just a whole number. + +## Strings + * A string is a sequence of characters. Strings are basically just a bunch of words. + * You will be using strings in almost every Python program that you write, so pay attention to the following part. + *Single Quote + * You can specify strings using single quotes such as 'Quote me on this' + * All white space i.e. spaces and tabs, within the quotes, are preserved as-is + * Double Quotes + * Strings in double quotes work exactly the same way as strings in single quotes. An example is "What's your name?" + * Triple Quotes {#triple-quotes} + * You can specify multi-line strings using triple quotes(""" or ''')You can use single quotes and double quotes freely within the triple quotes. + eg: +``` + '''This is a multi-line string. This is the first line. + This is the second line. + "What's your name?," I asked. + He said "Bond, James Bond." + ''' +``` + * Strings Are Immutable + * This means that once you have created a string, you cannot change it. +## format method + * Sometimes we may want to construct strings from other information. This is where the format() method is useful. + * A string can use certain specifications and subsequently, the format method can be called to substitute those specifications with corresponding arguments to the format method. + +``` + age = 20 + name = 'Danny' + print('{0} was {1} years old when he wrote this book'.format(name, age)) + print('Why is {0} playing with that python?'.format(name)) +``` +output: +``` + $ python str_format.py + Danny was 20 years old when he wrote this book + Why is Danny playing with that python? +``` + +## Escape Sequences + * Suppose, you want to have a string which contains a single quote ('), how will you specify this string? For example, the string is "What's your name?". You cannot specify 'What's your name?' because Python will be confused as to where the string starts and ends. So, you will have to specify that this single quote does not indicate the end of the string. This can be done with the help of what is called an escape sequence. + * you can specify the string as 'What\'s your name?' or "What's your name?" + * the newline character - \n + +``` + 'This is the first line\nThis is the second line' +``` +output: +``` + "This is the first line. + This is the second line" +``` +The tab: \t + +eg: +``` + 'This is the first line\t\t\t\t\tThis is the second line' +``` +output: +``` + "This is the first line This is the second line" +``` +The lists of Escape Characters in Python are: + +* \a: alert +* \b: backspace +* \cx: Control X +* \e: escape +* \f: Form feed +* \n: New line or next line +* \r: carriage return +* \s: space +* \t: tab +* \v: Vertical Tab + + + + +## Variable +### what is Variables + + * Container of date , we need some way of storing any information and manipulate them as well. +### Naming Variable + * The first character of the identifier must be a letter of the alphabet (uppercase ASCII or lowercase ASCII or Unicode character) or an underscore (_). + * The rest of the identifier name can consist of letters (uppercase ASCII or lowercase ASCII or Unicode character), underscores (_) or digits (0-9) + * Identifier names are case-sensitive. For example, myname and myName are not the same. Note the lowercase n in the former and the uppercase N in the latter +## Data Types + * Variables can hold values of different types called data types. +## Object + * Python refers to anything used in a program as an object. +## Operators and expressions + * Operators are the symbols which tells the Python interpreter to do some mathematical or logical operation. +eg: +``` + >>> 2 + 3 + 5 + >>> 23 - 3 + 20 + >>> 22.0 / 12 + 1.8333333333333333 + >>> 14 % 3(modulue operator) + 2 +``` +``` +=>Relational Operators +< Is less than +<= Is less than or equal to +> Is greater than +>= Is greater than or equal to +== Is equal to +!= Is not equal to + >>> 1 < 2 + True + >>> 3 > 34 + False + >>> 23 == 45 + False + >>> 34 != 323 + True +``` +## Logical Operators + * To do logical AND , OR we use and ,*or* keywords. x and y returns False if x is False else it returns evaluation of y. If x is True, it returns True. +eg: +``` + >>> 1 and 4 + 4 + >>> 1 or 4 + 1 + >>> -1 or 4 + -1 + >>> 0 or 4 + 4 +``` +=>Shorthand Operator + * x op= expression is the syntax for shorthand operators. It will be evaluated like x = x op expression +eg: +``` + >>> a = 12 + >>> a += 13 (equal to a = a+1) + >>> a + 25 +``` +## Expressions + * Generally while writing expressions we put spaces before and after every operator so that the code becomes clearer to read, like +eg: +``` + a = 234 * (45 - 56.0 / 34) +``` +## Conversions + * Convert from one type to another type +eg: +``` + >>> a = 5 + >>> type(a) + + >>> a = str(a) + >>> a + '5' + >>> type(a) + +``` + + + + + + + + + diff --git a/Iteration.md b/Iteration.md new file mode 100644 index 0000000..b6faaac --- /dev/null +++ b/Iteration.md @@ -0,0 +1,195 @@ +# Iteration + +## For + +Iterate over a sequence or an "iterable" object. + * Sequences and containers are iterable. Examples: tuples, lists, strings, dictionaries. + +syntax: + +``` +for variable in sequence: + Statement1 + Statement2 + ... + +``` + +``` +>>> a = ['Fedora', 'is', 'powerful'] +>>> for x in a: +... print(x) +... +Fedora +is +powerful +``` + +``` +>>> a = [1, 2, 3, 4, 5] +>>> for x in a: +... print(x) +1 +2 +3 +4 +5 +``` + +``` +>>> languages = ["C", "C++", "Perl", "Python"] +>>> for x in languages: +... print x +... +C +C++ +Perl +Python +>>> +``` + +## The range() Function + +The built-in function range() is the right function to iterate over a sequence of numbers + +range(n) + +``` +>>> range(10) +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +``` + +range(begin,end) + +``` +>>> range(1,5) +[1, 2, 3, 4] + +``` + +range(begin,end, step) +``` +>>> range(4,50,5) +[4, 9, 14, 19, 24, 29, 34, 39, 44, 49] +``` + +eg-2: + +``` +n = 101 +sum = 0 +for i in range(1,n): + sum = sum + i +print sum +``` + + +## While loop + +The while statement allows you to repeatedly execute a block of statements as long as a condition is true. + +The syntax for while statement is like + +``` +while condition: + statement1 + statement2 +``` + +eg: + +``` +>>> n = 0 +>>> while n < 11: +... print(n) +... n += 1 +... +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +```` + + +## Loop Control Statements + +### Break statement + +It is used to exit a while loop or a for loop. It terminates the looping & transfers execution to the statement next to the loop. + +``` +#!/usr/bin/python + +count = 0 + +while count <= 100: print (count) count += 1 if count >= 3 : + break +``` + +output: + +``` +0 +1 +2 +``` + +### Continue statement +It causes the looping to skip the rest part of its body & start re-testing its condition. + +``` +#!/usr/bin/python + +for x in range(10): + #check whether x is even + if x % 2 == 0: + continue + print x + +``` + +output: +``` +1 +3 +5 +7 +9 +``` + +### Pass Statement + +It is used in Python to when a statement is required syntactically and the programmer does not want to execute any code block or command. + +``` +for letter in 'TutorialsCloud': + if letter == 'C': + pass + print 'Pass block' + print 'Current letter is:', letter +``` +Output: +``` +Current letter is : T +Current letter is : u +Current letter is : t +Current letter is : o +Current letter is : r +Current letter is : i +Current letter is : a +Current letter is : l +Current letter is : s +Pass block +Current letter is : C +Current letter is : l +Current letter is : o +Current letter is : u +Current letter is : d +``` diff --git a/Operators_and_expressions.md b/Operators_and_expressions.md new file mode 100644 index 0000000..e3f8821 --- /dev/null +++ b/Operators_and_expressions.md @@ -0,0 +1,62 @@ +## Operators and expressions + * Operators are the symbols which tells the Python interpreter to do some mathematical or logical operation. +eg: +``` + >>> 2 + 3 + 5 + >>> 23 - 3 + 20 + >>> 22.0 / 12 + 1.8333333333333333 + >>> 14 % 3(modulue operator) + 2 +``` + +## Relational Operators +``` +< Is less than +<= Is less than or equal to +> Is greater than +>= Is greater than or equal to +== Is equal to +!= Is not equal to +``` +eg +``` + >>> 1 < 2 + True + >>> 3 > 34 + False + >>> 23 == 45 + False + >>> 34 != 323 + True +``` +## Logical Operators + * To do logical AND , OR we use and ,*or* keywords. x and y returns False if x is False else it returns evaluation of y. If x is True, it returns True. +eg: +``` + >>> 1 and 4 + 4 + >>> 1 or 4 + 1 + >>> -1 or 4 + -1 + >>> 0 or 4 + 4 +``` +=>Shorthand Operator + * x op= expression is the syntax for shorthand operators. It will be evaluated like x = x op expression +eg: +``` + >>> a = 12 + >>> a += 13 (equal to a = a+1) + >>> a + 25 +``` +## Expressions + * Generally while writing expressions we put spaces before and after every operator so that the code becomes clearer to read, like +eg: +``` + a = 234 * (45 - 56.0 / 34) +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..c16a597 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# python-basics +* [Basics](basics.md) +* [Introductions](Introduction.md) +* [Lexical matters](chap-2.md) +* [Variables](Variables.md) +* [Operators and expressions](Operators_and_expressions.md) +* [Strings](Strings.md) +* [Conditional execution](Condition.md) +* [Iteration](Iteration.md) +* [Collections](Collections.md) +* [Exception Handling](Exception_Handling.md) +* [Function](func.md) +* [Files](File_handling.md) +* [Modules](modulue.md) +* [Class](Class.md) +* [Regular Expressions](regular-expression.md) + + + diff --git a/Screenshot from 2017-12-20 17:12:12.png b/Screenshot from 2017-12-20 17:12:12.png new file mode 100644 index 0000000..6226dcf Binary files /dev/null and b/Screenshot from 2017-12-20 17:12:12.png differ diff --git a/Strings.md b/Strings.md new file mode 100644 index 0000000..bf11e2f --- /dev/null +++ b/Strings.md @@ -0,0 +1,138 @@ +# Strings +A string is an ordered sequence of characters. Here are a few characteristics of strings: + * A string has a length. Get the length with the len() built-in function. + * A string is indexable. Get a single character at a position in a string with the square bracket operator, for example mystring[5]. + * You can retrieve a slice (sub-string) of a string with a slice operation, for example mystring[5:8]. + * Create strings with single quotes or double quotes. + +## Operators on strings +You can concatenate strings with the "+" operator. +You can create multiple concatenated copies of a string with the "*" operator. +And, augmented assignment (+= and *=) also work. +Examples: +``` +>>> 'cat' + ' and ' + 'dog' +'cat and dog' +>>> '#' * 40 +'########################################' +>>> +>>> s1 = 'flower' +>>> s1 += 's' +>>> s1 +'flowers' +``` +## Repetition +The next operator we can apply to a string is the repetition operator (*). + +``` +>>> 'go'*3 +'gogogo' +>>> 'go'*3+'twins'*2 +'gogogotwinstwins' +>>> 'go'*3+' twins'*2 +'gogogo twins twins' +>>> +``` + + +## The in operator +The word in is a boolean operator that takes two strings and returns True if the first appears as a substring in the second: +``` +>>>'a' in 'banana' +True +>>>'seed' in 'banana' +False +``` +## format method + * Sometimes we may want to construct strings from other information. This is where the format() method is useful. + * A string can use certain specifications and subsequently, the format method can be called to substitute those specifications with corresponding arguments to the format method. + +``` + age = 20 + name = 'Danny' + print('{0} was {1} years old when he wrote this book'.format(name, age)) + print('Why is {0} playing with that python?'.format(name)) +``` +output: +``` + $ python str_format.py + Danny was 20 years old when he wrote this book + Why is Danny playing with that python? +``` + +## Escape Sequences + * Suppose, you want to have a string which contains a single quote ('), how will you specify this string? For example, the string is "What's your name?". You cannot specify 'What's your name?' because Python will be confused as to where the string starts and ends. So, you will have to specify that this single quote does not indicate the end of the string. This can be done with the help of what is called an escape sequence. + * you can specify the string as 'What\'s your name?' or "What's your name?" + * the newline character - \n + +``` + 'This is the first line\nThis is the second line' +``` +output: +``` + "This is the first line. + This is the second line" +``` +The tab: \t + +eg: +``` + 'This is the first line\t\t\t\t\tThis is the second line' +``` +output: +``` + "This is the first line This is the second line" +``` +The lists of Escape Characters in Python are: +``` +* \a: alert +* \b: backspace +* \cx: Control X +* \e: escape +* \f: Form feed +* \n: New line or next line +* \r: carriage return +* \s: space +* \t: tab +* \v: Vertical Tab +``` +## Strings are immutable +It is temp +ting to use the operator on the left side of an assignment, with the intention of changing a character in a string. For example: +``` +>>> greeting = 'Hello, world!' +>>> greeting[0] = 'J' +TypeError : 'str' object does not support item assignment +``` + +## Indexing +We next examine the index operator [] , an operator that has no mathematical counterpart. +This operator is useful when we want to get at particular characters in a string. + + +![Indexing](indexing.png) + +``` +>>> +>>> name = "Venkata Teja" +>>> first = name[0] +>>> first +'V' +>>> second = name[8] +>>> second +'T' +>>> +``` +## Slicing +The slice operator [ : ] is similar to the index operator except that it can get multicharacter +parts of the string. + +``` +>>> name = "Venkata Teja" +>>> name[0:6] +'Venkat' +>>> name[8:] +'Teja' + +``` + diff --git a/Variables.md b/Variables.md new file mode 100644 index 0000000..d994f01 --- /dev/null +++ b/Variables.md @@ -0,0 +1,80 @@ +## Variable +### what is Variables + + * Container of date , we need some way of storing any information and manipulate them as well. +### Naming Variable + * The first character of the identifier must be a letter of the alphabet (uppercase ASCII or lowercase ASCII or Unicode character) or an underscore (_). + * The rest of the identifier name can consist of letters (uppercase ASCII or lowercase ASCII or Unicode character), underscores (_) or digits (0-9) + * Identifier names are case-sensitive. For example, myname and myName are not the same. Note the lowercase n in the former and the uppercase N in the latter +## Data Types in Python + * Everything in Python programming is an object, and each object has its own unique identity(a type and a value). + + * There are many native(built-in) data types available in Python. + +## Some important are: + + * Numbers: An are integers (such as 1, 2, 3…), floats (such as 2.6, 4.8 etc), fractions (such as ½. ¾ etc) or even complex numbers. + - int (signed integer) + - float + - long + - complex + * Sequences: + - Strings: Sequence of Unicode characters, like an HTML document. + - Bytes/Byte array: Any type of file. + - Lists: An ordered sequence of values. + - Tuples: An ordered immutable sequence of values. + + * Boolean: Holds either true or false values. + * Sets: An unordered container of values. + * Dictionaries: An key-paired values set in an unordered way. +### Keywords +The following identifiers are used as reserved words, or keywords of the language, and cannot be used as ordinary identifiers. They must be typed exactly as written here: +``` +False class finally is return +None continue for lambda try +True def from nonlocal while +and del global not with +as elif if or yield +assert else import pass +break except in raise +``` + +## Object + * Python refers to anything used in a program as an object. +## Multiple Assignment +With Python, you can assign one single value to several variables at the same time. This lets you initialize several variables at once, which you can reassign later in the program yourself, or through user input. + +Through multiple assignment, you can set the variables x, y, and z to the value of the integer 0: +``` +x = y = z = 0 +print(x) +print(y) +print(z) +``` +Output +``` +0 +0 +0 +``` +## Conversions + * Convert from one type to another type +eg: +``` + >>> a = 5 + >>> type(a) + + >>> a = str(a) + >>> a + '5' + >>> type(a) + +``` + + + + + + + + diff --git a/argparse.txt b/argparse.txt new file mode 100644 index 0000000..387a94a --- /dev/null +++ b/argparse.txt @@ -0,0 +1,66 @@ +import argparse + +parser = argparse.ArgumentParser(description='Demo') +parser.add_argument('--verbose', + action='store_true', + help='verbose flag' ) + +args = parser.parse_args() + +if args.verbose: + print("~ Verbose!") +else: + print("~ Not so verbose") + + +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--verbose', '-v', + action='store_true', + help='verbose flag' ) + +args = parser.parse_args() + +if args.verbose: + print("~ Verbose!") +else: + print("~ Not so verbose") + + +#####positional +parser = argparse.ArgumentParser() +parser.add_argument('filename') +args = parser.parse_args() + +print("~ Filename: {}".format(args.filename)) + + + + +#nargs + +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('nums', nargs=2) +args = parser.parse_args() + +print("~ Nums: {}".format(args.nums)) + + +parser = argparse.ArgumentParser() +parser.add_argument('nums', nargs='*') +args = parser.parse_args() + +print("~ Nums: {}".format(args.nums)) + + + +parser = argparse.ArgumentParser() +parser.add_argument('nums', nargs=2, type=int) +args = parser.parse_args() + +print("~ Nums: {}".format(args.nums)) + + diff --git a/basics.md b/basics.md new file mode 100644 index 0000000..5356640 --- /dev/null +++ b/basics.md @@ -0,0 +1,4487 @@ + +## Python Basics + +### Math Operators + +From **Highest** to **Lowest** precedence: + +| Operators | Operation | Example | +| --------- | ---------------- | --------------- | +| ** | Exponent | `2 ** 3 = 8` | +| % | Modulus/Remaider | `22 % 8 = 6` | +| // | Integer division | `22 // 8 = 2` | +| / | Division | `22 / 8 = 2.75` | +| * | Multiplication | `3 * 3 = 9` | +| - | Subtraction | `5 - 2 = 3` | +| + | Addition | `2 + 2 = 4` | + +Examples of expressions in the interactive shell: + +```python +>>> 2 + 3 * 6 +20 +``` + +```python +>>> (2 + 3) * 6 +30 +``` + +```python +>>> 2 ** 8 +256 +``` + +```python +>>> 23 // 7 +3 +``` + +```python +>>> 23 % 7 +2 +``` + +```python +>>> (5 - 1) * ((7 + 1) / (3 - 1)) +16.0 +``` + + + +### Data Types + +| Data Type | Examples | +| ---------------------- | ----------------------------------------- | +| Integers | `-2, -1, 0, 1, 2, 3, 4, 5` | +| Floating-point numbers | `-1.25, -1.0, --0.5, 0.0, 0.5, 1.0, 1.25` | +| Strings | `'a', 'aa', 'aaa', 'Hello!', '11 cats'` | + + + +### String Concatenation and Replication + +String concatenation: + +```python +>>> 'Alice' 'Bob' +'AliceBob' +``` + +Note: Avoid `+` operator for string concatenation. Prefer string formatting. + +String Replication: + +```python +>>> 'Alice' * 5 +'AliceAliceAliceAliceAlice' +``` + + + +### Variables + +You can name a variable anything as long as it obeys the following three rules: + +1. It can be only one word. +1. It can use only letters, numbers, and the underscore (`_`) character. +1. It can’t begin with a number. +1. Variable name starting with an underscore (`_`) are considered as "unuseful`. + +Example: + +```python +>>> spam = 'Hello' +>>> spam +'Hello' +``` + +```python +>>> _spam = 'Hello' +``` + +`_spam` should not be used again in the code. + + + +### Comments + +Inline comment: + +```python +# This is a comment +``` + +Multiline comment: + +```Python +# This is a +# multiline comment +``` + +Code with a comment: + +```python +a = 1 # initialization +``` + +Please note the two spaces in front of the comment. + +Function docstring: + +```python +def foo(): + """ + This is a function docstring + You can also use: + ''' Function Docstring ''' + """ +``` + + + +### The print() Function + +```python +>>> print('Hello world!') +Hello world! +``` + +```python +>>> a = 1 +>>> print('Hello world!', a) +Hello world! 1 +``` + + + +### The input() Function + +Example Code: + +```python +>>> print('What is your name?') # ask for their name +>>> myName = input() +>>> print('It is good to meet you, {}'.format(myName)) +What is your name? +Al +It is good to meet you, Al +``` + + + +### The len() Function + +Evaluates to the integer value of the number of characters in a string: + +```python +>>> len('hello') +5 +``` + +Note: test of emptiness of strings, lists, dictionary, etc, should **not** use len, but prefer direct +boolean evaluation. + +```python +>>> a = [1, 2, 3] +>>> if a: +>>> print("the list is not empty!") +``` + + + +### The str(), int(), and float() Functions + +Integer to String or Float: + +```python +>>> str(29) +'29' +``` + +```python +>>> print('I am {} years old.'.format(str(29))) +I am 29 years old. +``` + +```python +>>> str(-3.14) +'-3.14' +``` + +Float to Integer: + +```python +>>> int(7.7) +7 +``` + +```python +>>> int(7.7) + 1 +8 +``` + + + +## Flow Control + +### Comparison Operators + +| Operator | Meaning | +| -------- | ------------------------ | +| `==` | Equal to | +| `!=` | Not equal to | +| `<` | Less than | +| `>` | Greater Than | +| `<=` | Less than or Equal to | +| `>=` | Greater than or Equal to | + +These operators evaluate to True or False depending on the values you give them. + +Examples: + +```python +>>> 42 == 42 +True +``` + +```python +>>> 40 == 42 +False +``` + +```python +>>> 'hello' == 'hello' +True +``` + +```python +>>> 'hello' == 'Hello' +False +``` + +```python +>>> 'dog' != 'cat' +True +``` + +```python +>>> 42 == 42.0 +True +``` + +```python +>>> 42 == '42' +False +``` + +### Boolean evaluation + +Never use `==` or `!=` operator to evaluate boolean operation. Use the `is` or `is not` operators, +or use implicit boolean evaluation. + +NO (even if they are valid Python): + +```python +>>> True == True +True +``` + +```python +>>> True != False +True +``` + +YES (even if they are valid Python): + +```python +>>> True is True +True +``` + +```python +>>> True is not False +True +``` + +These statements are equivalent: + +```Python +>>> if a is True: +>>> pass +>>> if a is not False: +>>> pass +>>> if a: +>>> pass +``` + +And these as well: + +```Python +>>> if a is False: +>>> pass +>>> if a is not True: +>>> pass +>>> if not a: +>>> pass +``` + + + +### Boolean Operators + +There are three Boolean operators: and, or, and not. + +The *and* Operator’s *Truth* Table: + +| Expression | Evaluates to | +| ----------------- | ------------ | +| `True and True` | `True` | +| `True and False` | `False` | +| `False and True` | `False` | +| `False and False` | `False` | + +The *or* Operator’s *Truth* Table: + +| Expression | Evaluates to | +| ---------------- | ------------ | +| `True or True` | `True` | +| `True or False` | `True` | +| `False or True` | `True` | +| `False or False` | `False` | + +The *not* Operator’s *Truth* Table: + +| Expression | Evaluates to | +| ----------- | ------------ | +| `not True` | `False` | +| `not False` | `True` | + + + +### Mixing Boolean and Comparison Operators + +```python +>>> (4 < 5) and (5 < 6) +True +``` + +```python +>>> (4 < 5) and (9 < 6) +False +``` + +```python +>>> (1 == 2) or (2 == 2) +True +``` + +You can also use multiple Boolean operators in an expression, along with the comparison operators: + +```python +>>> 2 + 2 == 4 and not 2 + 2 == 5 and 2 * 2 == 2 + 2 +True +``` + + + +### if Statements + +```python +if name == 'Alice': + print('Hi, Alice.') +``` + + + +### else Statements + +```python +name = 'Bob' +if name == 'Alice': + print('Hi, Alice.') +else: + print('Hello, stranger.') +``` + + + +### elif Statements + +```python +name = 'Bob' +age = 5 +if name == 'Alice': + print('Hi, Alice.') +elif age < 12: + print('You are not Alice, kiddo.') +``` + +```python +name = 'Bob' +age = 30 +if name == 'Alice': + print('Hi, Alice.') +elif age < 12: + print('You are not Alice, kiddo.') +else: + print('You are neither Alice nor a little kid.') +``` + + + +### while Loop Statements + +```python +spam = 0 +while spam < 5: + print('Hello, world.') + spam = spam + 1 +``` + + + +### break Statements + + If the execution reaches a break statement, it immediately exits the while loop’s clause: + +```python +while True: + print('Please type your name.') + name = input() + if name == 'your name': + break +print('Thank you!') +``` + + + +### continue Statements + +When the program execution reaches a continue statement, the program execution immediately jumps back to the start of the loop. + +```python +while True: + print('Who are you?') + name = input() + if name != 'Joe': + continue + print('Hello, Joe. What is the password? (It is a fish.)') + password = input() + if password == 'swordfish': + break +print('Access granted.') +``` + + + +### for Loops and the range() Function + +```python +>>> print('My name is') +>>> for i in range(5): +>>> print('Jimmy Five Times ({})'.format(str(i))) +My name is +Jimmy Five Times (0) +Jimmy Five Times (1) +Jimmy Five Times (2) +Jimmy Five Times (3) +Jimmy Five Times (4) +``` + +The *range()* function can also be called with three arguments. The first two arguments will be the start and stop values, and the third will be the step argument. The step is the amount that the variable is increased by after each iteration. + +```python +>>> for i in range(0, 10, 2): +>>> print(i) +0 +2 +4 +6 +8 +``` + +You can even use a negative number for the step argument to make the for loop count down instead of up. + +```python +>>> for i in range(5, -1, -1): +>>> print(i) +5 +4 +3 +2 +1 +0 +``` + +### For else statement + +This allows to specify a statement to execute in case of the full loop has been executed. Only +useful when a `break` condition can occur in the loop: + +```python +>>> for i in [1, 2, 3, 4, 5]: +>>> if i == 3: +>>> break +>>> else: +>>> print("only executed when no item of the list is equal to 3") +``` + + + +### Importing Modules + +```python +import random +for i in range(5): + print(random.randint(1, 10)) +``` + +```python +import random, sys, os, math +``` + +```python +from random import *. +``` + + + +### Ending a Program Early with sys.exit() + +```python +import sys + +while True: + print('Type exit to exit.') + response = input() + if response == 'exit': + sys.exit() + print('You typed {}.'.format(response)) +``` + + + +## Functions + +```python +>>> def hello(name): +>>> print('Hello {}'.format(name)) +>>> +>>> hello('Alice') +>>> hello('Bob') +Hello Alice +Hello Bob +``` + + + +### Return Values and return Statements + +When creating a function using the def statement, you can specify what the return value should be with a return statement. A return statement consists of the following: + +- The return keyword. + +- The value or expression that the function should return. + +```python +import random +def getAnswer(answerNumber): + if answerNumber == 1: + return 'It is certain' + elif answerNumber == 2: + return 'It is decidedly so' + elif answerNumber == 3: + return 'Yes' + elif answerNumber == 4: + return 'Reply hazy try again' + elif answerNumber == 5: + return 'Ask again later' + elif answerNumber == 6: + return 'Concentrate and ask again' + elif answerNumber == 7: + return 'My reply is no' + elif answerNumber == 8: + return 'Outlook not so good' + elif answerNumber == 9: + return 'Very doubtful' + +r = random.randint(1, 9) +fortune = getAnswer(r) +print(fortune) +``` + + + +### The None Value + +```python +>>> spam = print('Hello!') +Hello! +``` + +```python +>>> spam is None +True +``` + +Note: never compare to `None` with the `==` operator. Always use `is`. + + + +### Keyword Arguments and print() + +```python +>>> print('Hello', end='') +>>> print('World') +HelloWorld +``` + +```python +>>> print('cats', 'dogs', 'mice') +cats dogs mice +``` + +```python +>>> print('cats', 'dogs', 'mice', sep=',') +cats,dogs,mice +``` + + + +### Local and Global Scope + +- Code in the global scope cannot use any local variables. + +- However, a local scope can access global variables. + +- Code in a function’s local scope cannot use variables in any other local scope. + +- You can use the same name for different variables if they are in different scopes. That is, there can be a local variable named spam and a global variable also named spam. + + + +### The global Statement + +If you need to modify a global variable from within a function, use the global statement: + +```python +>>> def spam(): +>>> global eggs +>>> eggs = 'spam' +>>> +>>> eggs = 'global' +>>> spam() +>>> print(eggs) +spam +``` + +There are four rules to tell whether a variable is in a local scope or global scope: + +1. If a variable is being used in the global scope (that is, outside of all functions), then it is always a global variable. + +1. If there is a global statement for that variable in a function, it is a global variable. + +1. Otherwise, if the variable is used in an assignment statement in the function, it is a local variable. + +1. But if the variable is not used in an assignment statement, it is a global variable. + + + +## Exception Handling + +### Basic exception handling + +```python +>>> def spam(divideBy): +>>> try: +>>> return 42 / divideBy +>>> except ZeroDivisionError as e: +>>> print('Error: Invalid argument: {}'.format(e)) +>>> +>>> print(spam(2)) +>>> print(spam(12)) +>>> print(spam(0)) +>>> print(spam(1)) +21.0 +3.5 +Error: Invalid argument: division by zero +None +42.0 +``` + + + +### Final code in exception handling + +Code inside the `finally` section is always executed, no matter if an exception has been raised or +not, and even if an exception is not caught. + +```python +>>> def spam(divideBy): +>>> try: +>>> return 42 / divideBy +>>> except ZeroDivisionError as e: +>>> print('Error: Invalid argument: {}'.format(e)) +>>> finally: +>>> print("-- division finished --") +>>> print(spam(12)) +>>> print(spam(0)) +21.0 +-- division finished -- +3.5 +-- division finished -- +Error: Invalid argument: division by zero +-- division finished -- +None +-- division finished -- +42.0 +-- division finished -- +``` + + + +## Lists + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] + +>>> spam +['cat', 'bat', 'rat', 'elephant'] +``` + + + +### Getting Individual Values in a List with Indexes + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] +>>> spam[0] +'cat' +``` + +```python +>>> spam[1] +'bat' +``` + +```python +>>> spam[2] +'rat' +``` + +```python +>>> spam[3] +'elephant' +``` + + + +### Negative Indexes + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] +>>> spam[-1] +'elephant' +``` + +```python +>>> spam[-3] +'bat' +``` + +```python +>>> 'The {} is afraid of the {}.'.format(spam[-1], spam[-3]) +'The elephant is afraid of the bat.' +``` + + + +### Getting Sublists with Slices + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] +>>> spam[0:4] +['cat', 'bat', 'rat', 'elephant'] +``` + +```python +>>> spam[1:3] +['bat', 'rat'] +``` + +```python +>>> spam[0:-1] +['cat', 'bat', 'rat'] +``` + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] +>>> spam[:2] +['cat', 'bat'] +``` + +```python +>>> spam[1:] +['bat', 'rat', 'elephant'] +``` + +Slicing the complete list will perform a copy: + +```python +>>> spam2 = spam[:] +['cat', 'bat', 'rat', 'elephant'] +>>> spam.append('dog') +>>> spam +['cat', 'bat', 'rat', 'elephant', 'dog'] +>>> spam2 +['cat', 'bat', 'rat', 'elephant'] +``` + + + +### Getting a List’s Length with len() + +```python +>>> spam = ['cat', 'dog', 'moose'] +>>> len(spam) +3 +``` + + + +### Changing Values in a List with Indexes + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] +>>> spam[1] = 'aardvark' + +>>> spam +['cat', 'aardvark', 'rat', 'elephant'] + +>>> spam[2] = spam[1] + +>>> spam +['cat', 'aardvark', 'aardvark', 'elephant'] + +>>> spam[-1] = 12345 + +>>> spam +['cat', 'aardvark', 'aardvark', 12345] +``` + + + +### List Concatenation and List Replication + +```python +>>> [1, 2, 3] + ['A', 'B', 'C'] +[1, 2, 3, 'A', 'B', 'C'] + +>>> ['X', 'Y', 'Z'] * 3 +['X', 'Y', 'Z', 'X', 'Y', 'Z', 'X', 'Y', 'Z'] + +>>> spam = [1, 2, 3] + +>>> spam = spam + ['A', 'B', 'C'] + +>>> spam +[1, 2, 3, 'A', 'B', 'C'] +``` + + + +### Removing Values from Lists with del Statements + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] +>>> del spam[2] +>>> spam +['cat', 'bat', 'elephant'] +``` + +```python +>>> del spam[2] +>>> spam +['cat', 'bat'] +``` + + + +### Using for Loops with Lists + +```python +>>> supplies = ['pens', 'staplers', 'flame-throwers', 'binders'] +>>> for i, supply in enumerate(supplies): +>>> print('Index {} in supplies is: {}'.format(str(i), supply)) +Index 0 in supplies is: pens +Index 1 in supplies is: staplers +Index 2 in supplies is: flame-throwers +Index 3 in supplies is: binders +``` + + + +### Looping Through Multiple Lists with zip() + +```python +>>> name = ['Pete', 'John', 'Elizabeth'] +>>> age = [6, 23, 44] +>>> for n, a in zip(name, age): +>>> print('{} is {} years old'.format(n, a)) +Pete is 6 years old +John is 23 years old +Elizabeth is 44 years old +``` + +### The in and not in Operators + +```python +>>> 'howdy' in ['hello', 'hi', 'howdy', 'heyas'] +True +``` + +```python +>>> spam = ['hello', 'hi', 'howdy', 'heyas'] +>>> 'cat' in spam +False +``` + +```python +>>> 'howdy' not in spam +False +``` + +```python +>>> 'cat' not in spam +True +``` + + + +### The Multiple Assignment Trick + +The multiple assignment trick is a shortcut that lets you assign multiple variables with the values in a list in one line of code. So instead of doing this: + +```python +>>> cat = ['fat', 'orange', 'loud'] + +>>> size = cat[0] + +>>> color = cat[1] + +>>> disposition = cat[2] +``` + +You could type this line of code: + +```python +>>> cat = ['fat', 'orange', 'loud'] + +>>> size, color, disposition = cat +``` + +The multiple assignment trick can also be used to swap the values in two variables: + +```python +>>> a, b = 'Alice', 'Bob' +>>> a, b = b, a +>>> print(a) +'Bob' +``` + +```python +>>> print(b) +'Alice' +``` + + + +### Augmented Assignment Operators + +| Operator | Equivalent | +| ----------- | ----------------- | +| `spam += 1` | `spam = spam + 1` | +| `spam -= 1` | `spam = spam - 1` | +| `spam *= 1` | `spam = spam * 1` | +| `spam /= 1` | `spam = spam / 1` | +| `spam %= 1` | `spam = spam % 1` | + +Examples: + +```python +>>> spam = 'Hello' +>>> spam += ' world!' +>>> spam +'Hello world!' + +>>> bacon = ['Zophie'] +>>> bacon *= 3 +>>> bacon +['Zophie', 'Zophie', 'Zophie'] +``` + + + +### Finding a Value in a List with the index() Method + +```python +>>> spam = ['Zophie', 'Pooka', 'Fat-tail', 'Pooka'] + +>>> spam.index('Pooka') +1 +``` + + + +### Adding Values to Lists with the append() and insert() Methods + +**append()**: + +```python +>>> spam = ['cat', 'dog', 'bat'] + +>>> spam.append('moose') + +>>> spam +['cat', 'dog', 'bat', 'moose'] +``` + +**insert()**: + +```python +>>> spam = ['cat', 'dog', 'bat'] + +>>> spam.insert(1, 'chicken') + +>>> spam +['cat', 'chicken', 'dog', 'bat'] +``` + + + +### Removing Values from Lists with remove() + +```python +>>> spam = ['cat', 'bat', 'rat', 'elephant'] + +>>> spam.remove('bat') + +>>> spam +['cat', 'rat', 'elephant'] +``` + +If the value appears multiple times in the list, only the first instance of the value will be removed. + + + +### Sorting the Values in a List with the sort() Method + +```python +>>> spam = [2, 5, 3.14, 1, -7] +>>> spam.sort() +>>> spam +[-7, 1, 2, 3.14, 5] +``` + +```python +>>> spam = ['ants', 'cats', 'dogs', 'badgers', 'elephants'] +>>> spam.sort() +>>> spam +['ants', 'badgers', 'cats', 'dogs', 'elephants'] +``` + +You can also pass True for the reverse keyword argument to have sort() sort the values in reverse order: + +```python +>>> spam.sort(reverse=True) +>>> spam +['elephants', 'dogs', 'cats', 'badgers', 'ants'] +``` + +If you need to sort the values in regular alphabetical order, pass str. lower for the key keyword argument in the sort() method call: + +```python +>>> spam = ['a', 'z', 'A', 'Z'] +>>> spam.sort(key=str.lower) +>>> spam +['a', 'A', 'z', 'Z'] +``` + +You can use the built-in function `sorted` to return a new list: + +```python +>>> spam = ['ants', 'cats', 'dogs', 'badgers', 'elephants'] +>>> sorted(spam) +['ants', 'badgers', 'cats', 'dogs', 'elephants +``` + + + +### Tuple Data Type + +```python +>>> eggs = ('hello', 42, 0.5) +>>> eggs[0] +'hello' +``` + +```python +>>> eggs[1:3] +(42, 0.5) +``` + +```python +>>> len(eggs) +3 +``` + +The main way that tuples are different from lists is that tuples, like strings, are immutable. + + + +### Converting Types with the list() and tuple() Functions + +```python +>>> tuple(['cat', 'dog', 5]) +('cat', 'dog', 5) +``` + +```python +>>> list(('cat', 'dog', 5)) +['cat', 'dog', 5] +``` + +```python +>>> list('hello') +['h', 'e', 'l', 'l', 'o'] +``` + + + +## Dictionaries and Structuring Data + +Example Dictionary: + +```python +myCat = {'size': 'fat', 'color': 'gray', 'disposition': 'loud'} +``` + + + +### The keys(), values(), and items() Methods + +values(): + +```python +>>> spam = {'color': 'red', 'age': 42} +>>> for v in spam.values(): +>>> print(v) +red +42 +``` + +keys(): + +```python +>>> for k in spam.keys(): +>>> print(k) +color +age +``` + +items(): + +```python +>>> for i in spam.items(): +>>> print(i) +('color', 'red') +('age', 42) +``` + +Using the keys(), values(), and items() methods, a for loop can iterate over the keys, values, or key-value pairs in a dictionary, respectively. + +```python + +>>> spam = {'color': 'red', 'age': 42} +>>> +>>> for k, v in spam.items(): +>>> print('Key: {} Value: {}'.format(k, str(v))) +Key: age Value: 42 +Key: color Value: red +``` + + + +### Checking Whether a Key or Value Exists in a Dictionary + +```python +>>> spam = {'name': 'Zophie', 'age': 7} +``` + +```python +>>> 'name' in spam.keys() +True +``` + +```python +>>> 'Zophie' in spam.values() +True +``` + +```python +>>> # You can omit the call to keys() when checking for a key +>>> 'color' in spam +False +``` + +```python +>>> 'color' not in spam +True +``` + + + +### The get() Method + +```python +>>> picnic_items = {'apples': 5, 'cups': 2} + +>>> 'I am bringing {} cups.'.format(str(picnic_items.get('cups', 0))) +'I am bringing 2 cups.' +``` + +```python +>>> 'I am bringing {} eggs.'.format(str(picnic_items.get('eggs', 0))) +'I am bringing 0 eggs.' +``` + + + +### The setdefault() Method + +Let's consider this code: + +```python +spam = {'name': 'Pooka', 'age': 5} + +if 'color' not in spam: + spam['color'] = 'black' +``` + +Using `setdefault` we could write the same code more succinctly: + +```python +>>> spam = {'name': 'Pooka', 'age': 5} +>>> spam.setdefault('color', 'black') +'black' +``` + +```python +>>> spam +{'color': 'black', 'age': 5, 'name': 'Pooka'} +``` + +```python +>>> spam.setdefault('color', 'white') +'black' +``` + +```python +>>> spam +{'color': 'black', 'age': 5, 'name': 'Pooka'} +``` + + + +### Pretty Printing + +```python +>>> import pprint +>>> +>>> message = 'It was a bright cold day in April, and the clocks were striking +>>> thirteen.' +>>> count = {} +>>> +>>> for character in message: +>>> count.setdefault(character, 0) +>>> count[character] = count[character] + 1 +>>> +>>> pprint.pprint(count) +{' ': 13, + ',': 1, + '.': 1, + 'A': 1, + 'I': 1, + 'a': 4, + 'b': 1, + 'c': 3, + 'd': 3, + 'e': 5, + 'g': 2, + 'h': 3, + 'i': 6, + 'k': 2, + 'l': 3, + 'n': 4, + 'o': 2, + 'p': 1, + 'r': 5, + 's': 3, + 't': 6, + 'w': 2, + 'y': 1} +``` + + + +### Merge two dictionaries + +```python +# in Python 3.5+: +>>> x = {'a': 1, 'b': 2} +>>> y = {'b': 3, 'c': 4} +>>> z = {**x, **y} +>>> z +{'c': 4, 'a': 1, 'b': 3} + +# in Python 2.7 +>>> z = dict(x, **y) +>>> z +{'c': 4, 'a': 1, 'b': 3} +``` + +## sets + +From the Python 3 [documentation](https://docs.python.org/3/tutorial/datastructures.html) + +> 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. + +### Initializing a set + +There are two ways to create sets: using curly braces `{}` and the bult-in function `set()` + +```python +>>> s = {1, 2, 3} +>>> s = set([1, 2, 3]) +``` + +When creating an empty set, be sure to not use the curly braces `{}` or you will get an empty dictionary instead. + +```python +>>> s = {} +>>> type(s) + +``` + +### sets: unordered collections of unique elements + +A set automatically remove all the duplicate values. + +```python +>>> s = {1, 2, 3, 2, 3, 4} +>>> s +{1, 2, 3, 4} +``` + +And as an unordered data type, they can't be indexed. + +```python +>>> s = {1, 2, 3} +>>> s[0] +Traceback (most recent call last): + File "", line 1, in +TypeError: 'set' object does not support indexing +>>> +``` + +### set add() and update() + +Using the `add()` method we can add a single element to the set. + +```python +>>> s = {1, 2, 3} +>>> s.add(4) +>>> s +{1, 2, 3, 4} +``` + +And with `update()`, multiple ones . + +```python +>>> s = {1, 2, 3} +>>> s.update([2, 3, 4, 5, 6]) +>>> s +{1, 2, 3, 4, 5, 6} # remember, sets automatically remove duplicates +``` + +### set remove() and discard() + +Both methods will remove an element from the set, but `remove()` will raise a `key error` if the value doesn't exist. + +```python +>>> s = {1, 2, 3} +>>> s.remove(3) +>>> s +{1, 2} +>>> s.remove(3) +Traceback (most recent call last): + File "", line 1, in +KeyError: 3 +``` + +`discard()` won't raise any errors. + +```python +>>> s = {1, 2, 3} +>>> s.discard(3) +>>> s +{1, 2} +>>> s.discard(3) +>>> +``` + +### set union() + +`union()` or `|` will create a new set that contains all the elements from the sets provided. + +```python +>>> s1 = {1, 2, 3} +>>> s2 = {3, 4, 5} +>>> s1.union(s2) # or 's1 | s2' +{1, 2, 3, 4, 5} +``` + +### set intersection + +`intersection` or `&` will return a set containing only the elements that are common to all of them. + +```python +>>> s1 = {1, 2, 3} +>>> s2 = {2, 3, 4} +>>> s3 = {3, 4, 5} +>>> s1.intersection(s2, s3) # or 's1 & s2 & s3' +{3} +``` + +### set difference + +`difference` or `-` will return only the elements that are in one of the sets. + +```python +>>> s1 = {1, 2, 3} +>>> s2 = {2, 3, 4} +>>> s1.difference(s2) # or 's1 - s2' +{1} +``` + +### set symetric_difference + +`symetric_difference` or `^` will return all the elements that are not common between them. + +```python +>>> s1 = {1, 2, 3} +>>> s2 = {2, 3, 4} +>>> s1.symmetric_difference(s2) # or 's1 ^ s2' +{1, 4} +``` + + + +## itertools Module + +The *itertools* module is a colection of tools intented to be fast and use memory efficiently when handling iterators (like [lists](#lists) or [dictionaries](#dictionaries-and-structuring-data)). + +From the official [Python 3.x documentation](https://docs.python.org/3/library/itertools.html): + +> The module standardizes a core set of fast, memory efficient tools that are useful by themselves or in combination. Together, they form an “iterator algebra” making it possible to construct specialized tools succinctly and efficiently in pure Python. + +The *itertools* module comes in the standard library and must be imported. + +The [operator](https://docs.python.org/3/library/operator.html) module will also be used. This module is not necessary when using itertools, but needed for some of the examples below. + + + +### accumulate() + +Makes an iterator that returns the results of a function. + +```python +itertools.accumulate(iterable[, func]) +``` + +Example: + +```python +>>> data = [1, 2, 3, 4, 5] +>>> result = itertools.accumulate(data, operator.mul) +>>> for each in result: +>>> print(each) +1 +2 +6 +24 +120 +``` + +The operator.mul takes two numbers and multiplies them: + +```python +operator.mul(1, 2) +2 +operator.mul(2, 3) +6 +operator.mul(6, 4) +24 +operator.mul(24, 5) +120 +``` + +Passing a function is optional: + +```python +>>> data = [5, 2, 6, 4, 5, 9, 1] +>>> result = itertools.accumulate(data) +>>> for each in result: +>>> print(each) +5 +7 +13 +17 +22 +31 +32 +``` + +If no function is designated the items will be summed: + +```python +5 +5 + 2 = 7 +7 + 6 = 13 +13 + 4 = 17 +17 + 5 = 22 +22 + 9 = 31 +31 + 1 = 32 +``` + + + +### combinations() + +Takes an iterable and a integer. This will create all the unique combination that have r members. + +```python +itertools.combinations(iterable, r) +``` + +Example: + +```python +>>> shapes = ['circle', 'triangle', 'square',] +>>> result = itertools.combinations(shapes, 2) +>>> for each in result: +>>> print(each) +('circle', 'triangle') +('circle', 'square') +('triangle', 'square') +``` + + + +### combinations_with_replacement() + +Just like combinations(), but allows individual elements to be repeated more than once. + +```python +itertools.combinations_with_replacement(iterable, r) +``` + +Example: + +```python +>>> shapes = ['circle', 'triangle', 'square'] +>>> result = itertools.combinations_with_replacement(shapes, 2) +>>> for each in result: +>>> print(each) +('circle', 'circle') +('circle', 'triangle') +('circle', 'square') +('triangle', 'triangle') +('triangle', 'square') +('square', 'square') +``` + + + +### count() + +Makes an iterator that returns evenly spaced values starting with number start. + +```python +itertools.count(start=0, step=1) +``` + +Example: + +```python +>>> for i in itertools.count(10,3): +>>> print(i) +>>> if i > 20: +>>> break +10 +13 +16 +19 +22 +``` + + + +### cycle() + +This function cycles through an iterator endlessly. + +```python +itertools.cycle(iterable) +``` + +Example: + +```python +>>> colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet'] +>>> for color in itertools.cycle(colors): +>>> print(color) +red +orange +yellow +green +blue +violet +red +orange +``` + +When reached the end of the iterable it start over again from the beginning. + + + +### chain() + +Take a series of iterables and return them as one long iterable. + +```python +itertools.chain(*iterables) +``` + +Example: + +```python +>>> colors = ['red', 'orange', 'yellow', 'green', 'blue'] +>>> shapes = ['circle', 'triangle', 'square', 'pentagon'] +>>> result = itertools.chain(colors, shapes) +>>> for each in result: +>>> print(each) +red +orange +yellow +green +blue +circle +triangle +square +pentagon +``` + + + +### compress() + +Filters one iterable with another. + +```python +itertools.compress(data, selectors) +``` + +Example: + +```python +>>> shapes = ['circle', 'triangle', 'square', 'pentagon'] +>>> selections = [True, False, True, False] +>>> result = itertools.compress(shapes, selections) +>>> for each in result: +>>> print(each) +circle +square +``` + + + +### dropwhile() + +Make an iterator that drops elements from the iterable as long as the predicate is true; afterwards, returns every element. + +```python +itertools.dropwhile(predicate, iterable) +``` + +Example: + +```python +>>> data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1] +>>> result = itertools.dropwhile(lambda x: x<5, data) +>>> for each in result: +>>> print(each) +5 +6 +7 +8 +9 +10 +1 +``` + + + +### filterfalse() + +Makes an iterator that filters elements from iterable returning only those for which the predicate is False. + +```python +itertools.filterfalse(predicate, iterable) +``` + +Example: + +```python +>>> data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +>>> result = itertools.filterfalse(lambda x: x<5, data) +>>> for each in result: +>>> print(each) +5 +6 +7 +8 +9 +10 +``` + + + +### groupby() + +Simply put, this function groups things together. + +```python +itertools.groupby(iterable, key=None) +``` + +Example: + +```python +>>> robots = [{ + 'name': 'blaster', + 'faction': 'autobot' +}, { + 'name': 'galvatron', + 'faction': 'decepticon' +}, { + 'name': 'jazz', + 'faction': 'autobot' +}, { + 'name': 'metroplex', + 'faction': 'autobot' +}, { + 'name': 'megatron', + 'faction': 'decepticon' +}, { + 'name': 'starcream', + 'faction': 'decepticon' +}] +>>> for key, group in itertools.groupby(robots, key=lambda x: x['faction']): +>>> print(key) +>>> print(list(group)) +autobot +[{'name': 'blaster', 'faction': 'autobot'}] +decepticon +[{'name': 'galvatron', 'faction': 'decepticon'}] +autobot +[{'name': 'jazz', 'faction': 'autobot'}, {'name': 'metroplex', 'faction': 'autobot'}] +decepticon +[{'name': 'megatron', 'faction': 'decepticon'}, {'name': 'starcream', 'faction': 'decepticon'}] +``` + + + +### islice() + +This function is very much like slices. This allows you to cut out a piece of an iterable. + +```python +itertools.islice(iterable, start, stop[, step]) +``` + +Example: + +```python +>>> colors = ['red', 'orange', 'yellow', 'green', 'blue',] +>>> few_colors = itertools.islice(colors, 2) +>>> for each in few_colors: +>>> print(each) +red +orange +``` + + + +### permutations() + +```python +itertools.permutations(iterable, r=None) +``` + +Example: + +```python +>>> alpha_data = ['a', 'b', 'c'] +>>> result = itertools.permutations(alpha_data) +>>> for each in result: +>>> print(each) +('a', 'b', 'c') +('a', 'c', 'b') +('b', 'a', 'c') +('b', 'c', 'a') +('c', 'a', 'b') +('c', 'b', 'a') +``` + + + +### product() + +Creates the cartesian products from a series of iterables. + +```python +>>> num_data = [1, 2, 3] +>>> alpha_data = ['a', 'b', 'c'] +>>> result = itertools.product(num_data, alpha_data) +>>> for each in result: + print(each) +(1, 'a') +(1, 'b') +(1, 'c') +(2, 'a') +(2, 'b') +(2, 'c') +(3, 'a') +(3, 'b') +(3, 'c') +``` + + + +### repeat() + +This function will repeat an object over and over again. Unless, there is a times argument. + +```python +itertools.repeat(object[, times]) +``` + +Example: + +```python +>>> for i in itertools.repeat("spam", 3): + print(i) +spam +spam +spam +``` + + + +### starmap() + +Makes an iterator that computes the function using arguments obtained from the iterable. + +```python +itertools.starmap(function, iterable) +``` + +Example: + +```python +>>> data = [(2, 6), (8, 4), (7, 3)] +>>> result = itertools.starmap(operator.mul, data) +>>> for each in result: +>>> print(each) +12 +32 +21 +``` + + + +### takewhile() + +The opposite of dropwhile(). Makes an iterator and returns elements from the iterable as long as the predicate is true. + +```python +itertools.takwwhile(predicate, iterable) +``` + +Example: + +```python +>>> data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1] +>>> result = itertools.takewhile(lambda x: x<5, data) +>>> for each in result: +>>> print(each) +1 +2 +3 +4 +``` + + + +### tee() + +Return n independent iterators from a single iterable. + +```python +itertools.tee(iterable, n=2) +``` + +Example: + +```python +>>> colors = ['red', 'orange', 'yellow', 'green', 'blue'] +>>> alpha_colors, beta_colors = itertools.tee(colors) +>>> for each in alpha_colors: +>>> print(each) +red +orange +yellow +green +blue +``` + +```python +>>> colors = ['red', 'orange', 'yellow', 'green', 'blue'] +>>> alpha_colors, beta_colors = itertools.tee(colors) +>>> for each in beta_colors: +>>> print(each) +red +orange +yellow +green +blue +``` + + + +### zip_longest() + +Makes an iterator that aggregates elements from each of the iterables. If the iterables are of uneven length, missing values are filled-in with fillvalue. Iteration continues until the longest iterable is exhausted. + +```python +itertools.zip_longest(*iterables, fillvalue=None) +``` + +Example: + +```python +>>> colors = ['red', 'orange', 'yellow', 'green', 'blue',] +>>> data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,] +>>> for each in itertools.zip_longest(colors, data, fillvalue=None): +>>> print(each) +('red', 1) +('orange', 2) +('yellow', 3) +('green', 4) +('blue', 5) +(None, 6) +(None, 7) +(None, 8) +(None, 9) +(None, 10) +``` + + + +## Comprehensions + +### List comprehension + +```python +>>> a = [1, 3, 5, 7, 9, 11] + +>>> [i - 1 for i in a] +[0, 2, 4, 6, 8, 10] +``` + +### Set comprehension + +```python +>>> b = {"abc", "def"} +>>> {s.upper() for s in b} +{"ABC", "DEF} +``` + +### Dict comprehension + +```python +>>> c = {'name': 'Pooka', 'age': 5} +>>> {v: k for k, v in c.items()} +{'Pooka': 'name', 5: 'age'} +``` + +A List comprehension can be generated from a dictionary: + +```python +>>> c = {'name': 'Pooka', 'first_name': 'Oooka'} +>>> ["{}:{}".format(k.upper(), v.upper()) for k, v in c.items()] +['NAME:POOKA', 'FIRST_NAME:OOOKA'] +``` + +## Manipulating Strings + +### Escape Characters + +| Escape character | Prints as | +| ---------------- | -------------------- | +| `\'` | Single quote | +| `\"` | Double quote | +| `\t` | Tab | +| `\n` | Newline (line break) | +| `\\` | Backslash | + +Example: + +```python +>>> print("Hello there!\nHow are you?\nI\'m doing fine.") +Hello there! +How are you? +I'm doing fine. +``` + + + +### Raw Strings + +A raw string completely ignores all escape characters and prints any backslash that appears in the string. + +```python +>>> print(r'That is Carol\'s cat.') +That is Carol\'s cat. +``` + +Note: mostly used for regular expression definition (see `re` package) + + + +### Multiline Strings with Triple Quotes + +```python +>>> print('''Dear Alice, +>>> +>>> Eve's cat has been arrested for catnapping, cat burglary, and extortion. +>>> +>>> Sincerely, +>>> Bob''') +Dear Alice, + +Eve's cat has been arrested for catnapping, cat burglary, and extortion. + +Sincerely, +Bob +``` + +To keep a nicer flow in your code, you can use the `dedent` function from the `textwrap` standard package. + +```python +>>> from textwrap import dedent +>>> +>>> def my_function(): +>>> print(''' +>>> Dear Alice, +>>> +>>> Eve's cat has been arrested for catnapping, cat burglary, and extortion. +>>> +>>> Sincerely, +>>> Bob +>>> ''').strip() +``` + +This generates the same string than before. + + + +### Indexing and Slicing Strings + + H e l l o w o r l d ! + 0 1 2 3 4 5 6 7 8 9 10 11 + +```python +>>> spam = 'Hello world!' + +>>> spam[0] +'H' +``` + +```python +>>> spam[4] +'o' +``` + +```python +>>> spam[-1] +'!' +``` + +Slicing: + +```python + +>>> spam[0:5] +'Hello' +``` + +```python +>>> spam[:5] +'Hello' +``` + +```python +>>> spam[6:] +'world!' +``` + +```python +>>> spam[6:-1] +'world' +``` + +```python +>>> spam[:-1] +'Hello world' +``` + +```python +>>> spam[::-1] +'!dlrow olleH' +``` + +```python +>>> spam = 'Hello world!' +>>> fizz = spam[0:5] +>>> fizz +'Hello' +``` + + + +### The in and not in Operators with Strings + +```python +>>> 'Hello' in 'Hello World' +True +``` + +```python +>>> 'Hello' in 'Hello' +True +``` + +```python +>>> 'HELLO' in 'Hello World' +False +``` + +```python +>>> '' in 'spam' +True +``` + +```python +>>> 'cats' not in 'cats and dogs' +False +``` + +### The in and not in Operators with list + +```python +>>> a = [1, 2, 3, 4] +>>> 5 in a +False +``` + +```python +>>> 2 in a +True +``` + + + +### The upper(), lower(), isupper(), and islower() String Methods + +`upper()` and `lower()`: + +```python +>>> spam = 'Hello world!' +>>> spam = spam.upper() +>>> spam +'HELLO WORLD!' +``` + +```python +>>> spam = spam.lower() +>>> spam +'hello world!' +``` + +isupper() and islower(): + +```python +>>> spam = 'Hello world!' +>>> spam.islower() +False +``` + +```python +>>> spam.isupper() +False +``` + +```python +>>> 'HELLO'.isupper() +True +``` + +```python +>>> 'abc12345'.islower() +True +``` + +```python +>>> '12345'.islower() +False +``` + +```python +>>> '12345'.isupper() +False +``` + + + +### The isX String Methods + +- **isalpha()** returns True if the string consists only of letters and is not blank. +- **isalnum()** returns True if the string consists only of lettersand numbers and is not blank. +- **isdecimal()** returns True if the string consists only ofnumeric characters and is not blank. +- **isspace()** returns True if the string consists only of spaces,tabs, and new-lines and is not blank. +- **istitle()** returns True if the string consists only of wordsthat begin with an uppercase letter followed by onlylowercase letters. + + + +### The startswith() and endswith() String Methods + +```python +>>> 'Hello world!'.startswith('Hello') +True +``` + +```python +>>> 'Hello world!'.endswith('world!') +True +``` + +```python +>>> 'abc123'.startswith('abcdef') +False +``` + +```python +>>> 'abc123'.endswith('12') +False +``` + +```python +>>> 'Hello world!'.startswith('Hello world!') +True +``` + +```python +>>> 'Hello world!'.endswith('Hello world!') +True +``` + + + +### The join() and split() String Methods + +join(): + +```python +>>> ', '.join(['cats', 'rats', 'bats']) +'cats, rats, bats' +``` + +```python +>>> ' '.join(['My', 'name', 'is', 'Simon']) +'My name is Simon' +``` + +```python +>>> 'ABC'.join(['My', 'name', 'is', 'Simon']) +'MyABCnameABCisABCSimon' +``` + +split(): + +```python +>>> 'My name is Simon'.split() +['My', 'name', 'is', 'Simon'] +``` + +```python +>>> 'MyABCnameABCisABCSimon'.split('ABC') +['My', 'name', 'is', 'Simon'] +``` + +```python +>>> 'My name is Simon'.split('m') +['My na', 'e is Si', 'on'] +``` + + + +### Justifying Text with rjust(), ljust(), and center() + +rjust() and ljust(): + +```python +>>> 'Hello'.rjust(10) +' Hello' +``` + +```python +>>> 'Hello'.rjust(20) +' Hello' +``` + +```python +>>> 'Hello World'.rjust(20) +' Hello World' +``` + +```python +>>> 'Hello'.ljust(10) +'Hello ' +``` + +An optional second argument to rjust() and ljust() will specify a fill character other than a space character. Enter the following into the interactive shell: + +```python +>>> 'Hello'.rjust(20, '*') +'***************Hello' +``` + +```python +>>> 'Hello'.ljust(20, '-') +'Hello---------------' +``` + +center(): + +```python +>>> 'Hello'.center(20) +' Hello ' +``` + +```python +>>> 'Hello'.center(20, '=') +'=======Hello========' +``` + + + +### Removing Whitespace with strip(), rstrip(), and lstrip() + +```python +>>> spam = ' Hello World ' +>>> spam.strip() +'Hello World' +``` + +```python +>>> spam.lstrip() +'Hello World ' +``` + +```python +>>> spam.rstrip() +' Hello World' +``` + +```python +>>> spam = 'SpamSpamBaconSpamEggsSpamSpam' +>>> spam.strip('ampS') +'BaconSpamEggs' +``` + + + +### Copying and Pasting Strings with the pyperclip Module (need pip install) + +```python +>>> import pyperclip + +>>> pyperclip.copy('Hello world!') + +>>> pyperclip.paste() +'Hello world!' +``` + + + +## String Formatting + +### % operator + +```python +>>> name = 'Pete' +>>> 'Hello %s' % name +"Hello Pete" +``` + +We can use the `%x` format specifier to convert an int value to a string: + +```python +>>> num = 5 +>>> 'I have %x apples' % num +"I have 5 apples" +``` + +Note: For new code, using [str.format](#string-formatting-strformat) or [f-strings](#formatted-string-literals-or-f-strings-python-36) (Python 3.6+) is strongly recommended over the `%` operator. + + + +### String Formatting (str.format) + +Python 3 introduced a new way to do string formatting that was later back-ported to Python 2.7. This makes the syntax for string formatting more regular. + +```python +>>> name = 'John' +>>> age = 20' + +>>> "Hello I'm {}, my age is {}".format(name, age) +"Hello I'm John, my age is 20" +``` + +```python +>>> "Hello I'm {0}, my age is {1}".format(name, age) +"Hello I'm John, my age is 20" +``` + +The official [Python 3.x documentation](https://docs.python.org/3/library/stdtypes.html?highlight=sprintf#printf-style-string-formatting) recommend `str.format` over the `%` operator: + +> The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly). Using the newer formatted string literals or the str.format() interface helps avoid these errors. These alternatives also provide more powerful, flexible and extensible approaches to formatting text. + + + +### Lazy string formatting + +You would only use `%s` string formatting on functions that can do lazy parameters evaluation, +the most common being logging: + +Prefer: + +```python +>>> name = "alice" +>>> logging.debug("User name: %s", name) +``` + +Over: + +```python +>>> logging.debug("User name: {}".format(name)) +``` + +Or: + +```python +>>> logging.debug("User name: " + name) +``` + + + +### Formatted String Literals or f-strings (Python 3.6+) + +```python +>>> name = 'Elizabeth' +>>> f'Hello {name}!' +'Hello Elizabeth! +``` + +It is even possible to do inline arithmetic with it: + +```python +>>> a = 5 +>>> b = 10 +>>> f'Five plus ten is {a + b} and not {2 * (a + b)}.' +'Five plus ten is 15 and not 30.' +``` + + + +### Template Strings + + A simpler and less powerful mechanism, but it is recommended when handling format strings generated by users. Due to their reduced complexity template strings are a safer choice. + +```python +>>> from string import Template +>>> name = 'Elizabeth' +>>> t = Template('Hey $name!') +>>> t.substitute(name=name) +'Hey Elizabeth!' +``` + + + +## Regular Expressions + +1. Import the regex module with `import re`. +1. Create a Regex object with the `re.compile()` function. (Remember to use a raw string.) +1. Pass the string you want to search into the Regex object’s `search()` method. This returns a `Match` object. +1. Call the Match object’s `group()` method to return a string of the actual matched text. + +All the regex functions in Python are in the re module: + +```python +>>> import re +``` + + + +### Matching Regex Objects + +```python +>>> phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') + +>>> mo = phone_num_regex.search('My number is 415-555-4242.') + +>>> print('Phone number found: {}'.format(mo.group())) +Phone number found: 415-555-4242 +``` + + + +### Grouping with Parentheses + +```python +>>> phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') + +>>> mo = phone_num_regex.search('My number is 415-555-4242.') + +>>> mo.group(1) +'415' + +>>> mo.group(2) +'555-4242' + +>>> mo.group(0) +'415-555-4242' + +>>> mo.group() +'415-555-4242' +``` + +To retrieve all the groups at once: use the groups() method—note the plural form for the name. + +```python +>>> mo.groups() +('415', '555-4242') + +>>> area_code, main_number = mo.groups() + +>>> print(area_code) +415 + +>>> print(main_number) +555-4242 +``` + + + +### Matching Multiple Groups with the Pipe + +The | character is called a pipe. You can use it anywhere you want to match one of many expressions. For example, the regular expression r'Batman|Tina Fey' will match either 'Batman' or 'Tina Fey'. + +```python +>>> hero_regex = re.compile (r'Batman|Tina Fey') + +>>> mo1 = hero_regex.search('Batman and Tina Fey.') + +>>> mo1.group() +'Batman' + +>>> mo2 = hero_regex.search('Tina Fey and Batman.') + +>>> mo2.group() +'Tina Fey' +``` + +You can also use the pipe to match one of several patterns as part of your regex: + +```python +>>> bat_regex = re.compile(r'Bat(man|mobile|copter|bat)') + +>>> mo = bat_regex.search('Batmobile lost a wheel') + +>>> mo.group() +'Batmobile' + +>>> mo.group(1) +'mobile' +``` + + + +### Optional Matching with the Question Mark + +The ? character flags the group that precedes it as an optional part of the pattern. + +```python +>>> bat_regex = re.compile(r'Bat(wo)?man') +>>> mo1 = bat_regex.search('The Adventures of Batman') +>>> mo1.group() +'Batman' + +>>> mo2 = bat_regex.search('The Adventures of Batwoman') +>>> mo2.group() +'Batwoman' +``` + + + +### Matching Zero or More with the Star + +The * (called the star or asterisk) means “match zero or more”—the group that precedes the star can occur any number of times in the text. + +```python +>>> bat_regex = re.compile(r'Bat(wo)*man') +>>> mo1 = bat_regex.search('The Adventures of Batman') +>>> mo1.group() +'Batman' + +>>> mo2 = bat_regex.search('The Adventures of Batwoman') +>>> mo2.group() +'Batwoman' + +>>> mo3 = bat_regex.search('The Adventures of Batwowowowoman') +>>> mo3.group() +'Batwowowowoman' +``` + + + +### Matching One or More with the Plus + +While * means “match zero or more,” the + (or plus) means “match one or more”. The group preceding a plus must appear at least once. It is not optional: + +```python +>>> bat_regex = re.compile(r'Bat(wo)+man') +>>> mo1 = bat_regex.search('The Adventures of Batwoman') +>>> mo1.group() +'Batwoman' +``` + +```python +>>> mo2 = bat_regex.search('The Adventures of Batwowowowoman') +>>> mo2.group() +'Batwowowowoman' +``` + +```python +>>> mo3 = bat_regex.search('The Adventures of Batman') +>>> mo3 is None +True +``` + + + +### Matching Specific Repetitions with Curly Brackets + +If you have a group that you want to repeat a specific number of times, follow the group in your regex with a number in curly brackets. For example, the regex (Ha){3} will match the string 'HaHaHa', but it will not match 'HaHa', since the latter has only two repeats of the (Ha) group. + +Instead of one number, you can specify a range by writing a minimum, a comma, and a maximum in between the curly brackets. For example, the regex (Ha){3,5} will match 'HaHaHa', 'HaHaHaHa', and 'HaHaHaHaHa'. + +```python +>>> ha_regex = re.compile(r'(Ha){3}') +>>> mo1 = ha_regex.search('HaHaHa') +>>> mo1.group() +'HaHaHa' +``` + +```python +>>> mo2 = ha_regex.search('Ha') +>>> mo2 is None +True +``` + + + +### Greedy and Nongreedy Matching + +Python’s regular expressions are greedy by default, which means that in ambiguous situations they will match the longest string possible. The non-greedy version of the curly brackets, which matches the shortest string possible, has the closing curly bracket followed by a question mark. + +```python +>>> greedy_ha_regex = re.compile(r'(Ha){3,5}') +>>> mo1 = greedy_ha_regex.search('HaHaHaHaHa') +>>> mo1.group() +'HaHaHaHaHa' +``` + +```python +>>> nongreedy_ha_regex = re.compile(r'(Ha){3,5}?') +>>> mo2 = nongreedy_ha_regex.search('HaHaHaHaHa') +>>> mo2.group() +'HaHaHa' +``` + + + +### The findall() Method + +In addition to the search() method, Regex objects also have a findall() method. While search() will return a Match object of the first matched text in the searched string, the findall() method will return the strings of every match in the searched string. + +```python +>>> phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # has no groups + +>>> phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000') +['415-555-9999', '212-555-0000'] +``` + +To summarize what the findall() method returns, remember the following: + +- When called on a regex with no groups, such as \d-\d\d\d-\d\d\d\d, the method findall() returns a list of ng matches, such as ['415-555-9999', '212-555-0000']. + +- When called on a regex that has groups, such as (\d\d\d)-d\d)-(\d\ d\d\d), the method findall() returns a list of es of strings (one string for each group), such as [('415', ', '9999'), ('212', '555', '0000')]. + + + +### Making Your Own Character Classes + +There are times when you want to match a set of characters but the shorthand character classes (\d, \w, \s, and so on) are too broad. You can define your own character class using square brackets. For example, the character class [aeiouAEIOU] will match any vowel, both lowercase and uppercase. + +```python +>>> vowel_regex = re.compile(r'[aeiouAEIOU]') + +>>> vowel_regex.findall('Robocop eats baby food. BABY FOOD.') +['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O'] +``` + +You can also include ranges of letters or numbers by using a hyphen. For example, the character class [a-zA-Z0-9] will match all lowercase letters, uppercase letters, and numbers. + +By placing a caret character (^) just after the character class’s opening bracket, you can make a negative character class. A negative character class will match all the characters that are not in the character class. For example, enter the following into the interactive shell: + +```python +>>> consonant_regex = re.compile(r'[^aeiouAEIOU]') + +>>> consonant_regex.findall('Robocop eats baby food. BABY FOOD.') +['R', 'b', 'c', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' +', 'B', 'B', 'Y', ' ', 'F', 'D', '.'] +``` + + + +### The Caret and Dollar Sign Characters + +- You can also use the caret symbol (^) at the start of a regex to indicate that a match must occur at the beginning of the searched text. + +- Likewise, you can put a dollar sign ($) at the end of the regex to indicate the string must end with this regex pattern. + +- And you can use the ^ and $ together to indicate that the entire string must match the regex—that is, it’s not enough for a match to be made on some subset of the string. + +The r'^Hello' regular expression string matches strings that begin with 'Hello': + +```python +>>> begins_with_hello = re.compile(r'^Hello') + +>>> begins_with_hello.search('Hello world!') +<_sre.SRE_Match object; span=(0, 5), match='Hello'> + +>>> begins_with_hello.search('He said hello.') is None +True +``` + +The r'\d$' regular expression string matches strings that end with a numeric character from 0 to 9: + +```python +>>> whole_string_is_num = re.compile(r'^\d+$') + +>>> whole_string_is_num.search('1234567890') +<_sre.SRE_Match object; span=(0, 10), match='1234567890'> + +>>> whole_string_is_num.search('12345xyz67890') is None +True + +>>> whole_string_is_num.search('12 34567890') is None +True +``` + + + +### The Wildcard Character + +The . (or dot) character in a regular expression is called a wildcard and will match any character except for a newline: + +```python +>>> at_regex = re.compile(r'.at') + +>>> at_regex.findall('The cat in the hat sat on the flat mat.') +['cat', 'hat', 'sat', 'lat', 'mat'] +``` + + + +### Matching Everything with Dot-Star + +```python +>>> name_regex = re.compile(r'First Name: (.*) Last Name: (.*)') + +>>> mo = name_regex.search('First Name: Al Last Name: Sweigart') + +>>> mo.group(1) +'Al' +``` + +```python +>>> mo.group(2) +'Sweigart' +``` + +The dot-star uses greedy mode: It will always try to match as much text as possible. To match any and all text in a nongreedy fashion, use the dot, star, and question mark (.*?). The question mark tells Python to match in a nongreedy way: + +```python +>>> nongreedy_regex = re.compile(r'<.*?>') +>>> mo = nongreedy_regex.search(' for dinner.>') +>>> mo.group() +'' +``` + +```python +>>> greedy_regex = re.compile(r'<.*>') +>>> mo = greedy_regex.search(' for dinner.>') +>>> mo.group() +' for dinner.>' +``` + + + +### Matching Newlines with the Dot Character + +The dot-star will match everything except a newline. By passing re.DOTALL as the second argument to re.compile(), you can make the dot character match all characters, including the newline character: + +```python +>>> no_newline_regex = re.compile('.*') +>>> no_newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group() +'Serve the public trust.' +``` + +```python +>>> newline_regex = re.compile('.*', re.DOTALL) +>>> newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group() +'Serve the public trust.\nProtect the innocent.\nUphold the law.' +``` + + + +### Review of Regex Symbols + +| Symbol | Matches | +| ------------------------ | ------------------------------------------------------------ | +| `?` | zero or one of the preceding group. | +| `*` | zero or more of the preceding group. | +| `+` | one or more of the preceding group. | +| `{n}` | exactly n of the preceding group. | +| `{n,}` | n or more of the preceding group. | +| `{,m}` | 0 to m of the preceding group. | +| `{n,m}` | at least n and at most m of the preceding p. | +| `{n,m}?` or `*?` or `+?` | performs a nongreedy match of the preceding p. | +| `^spam` | means the string must begin with spam. | +| `spam$` | means the string must end with spam. | +| `.` | any character, except newline characters. | +| `\d`, `\w`, and `\s` | a digit, word, or space character, ectively. | +| `\D`, `\W`, and `\S` | anything except a digit, word, or space acter, respectively. | +| `[abc]` | any character between the brackets (such as a, b, ). | +| `[^abc]` | any character that isn’t between the brackets. | + + + +### Case-Insensitive Matching + +To make your regex case-insensitive, you can pass re.IGNORECASE or re.I as a second argument to re.compile(): + +```python +>>> robocop = re.compile(r'robocop', re.I) + +>>> robocop.search('Robocop is part man, part machine, all cop.').group() +'Robocop' +``` + +```python +>>> robocop.search('ROBOCOP protects the innocent.').group() +'ROBOCOP' +``` + +```python +>>> robocop.search('Al, why does your programming book talk about robocop so much?').group() +'robocop' +``` + + + +### Substituting Strings with the sub() Method + +The sub() method for Regex objects is passed two arguments: + +1. The first argument is a string to replace any matches. +1. The second is the string for the regular expression. + +The sub() method returns a string with the substitutions applied: + +```python +>>> names_regex = re.compile(r'Agent \w+') + +>>> names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.') +'CENSORED gave the secret documents to CENSORED.' +``` + +Another example: + +```python +>>> agent_names_regex = re.compile(r'Agent (\w)\w*') + +>>> agent_names_regex.sub(r'\1****', 'Agent Alice told Agent Carol that Agent Eve knew Agent Bob was a double agent.') +A**** told C**** that E**** knew B**** was a double agent.' +``` + + + +### Managing Complex Regexes + +To tell the re.compile() function to ignore whitespace and comments inside the regular expression string, “verbose mode” can be enabled by passing the variable re.VERBOSE as the second argument to re.compile(). + +Now instead of a hard-to-read regular expression like this: + +```python +phone_regex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)') +``` + +you can spread the regular expression over multiple lines with comments like this: + +```python +phone_regex = re.compile(r'''( + (\d{3}|\(\d{3}\))? # area code + (\s|-|\.)? # separator + \d{3} # first 3 digits + (\s|-|\.) # separator + \d{4} # last 4 digits + (\s*(ext|x|ext.)\s*\d{2,5})? # extension + )''', re.VERBOSE) +``` + + + +## Handling File and Directory Paths + +There are two main modules in Python that deals with path manipulation. +One is the `os.path` module and the other is the `pathlib` module. +The `pathlib` module was added in Python 3.4, offering an object-oriented way +to handle file system paths. + + + +### Backslash on Windows and Forward Slash on OS X and Linux + +On Windows, paths are written using backslashes (\) as the separator between +folder names. On Unix based operating system such as macOS, Linux, and BSDs, +the forward slash (/) is used as the path separator. Joining paths can be +a headache if your code needs to work on different platforms. + +Fortunately, Python provides easy ways to handle this. We will showcase +how to deal with this with both `os.path.join` and `pathlib.Path.joinpath` + +Using `os.path.join` on Windows: + +```python +>>> import os + +>>> os.path.join('usr', 'bin', 'spam') +'usr\\bin\\spam' +``` + +And using `pathlib` on \*nix: + +```python +>>> from pathlib import Path + +>>> print(Path('usr').joinpath('bin').joinpath('spam')) +usr/bin/spam +``` + +`pathlib` also provides a shortcut to joinpath using the `/` operator: + +```python +>>> from pathlib import Path + +>>> print(Path('usr') / 'bin' / 'spam') +usr/bin/spam +``` + +Notice the path separator is different between Windows and Unix based operating +system, that's why you want to use one of the above methods instead of +adding strings together to join paths together. + +Joining paths is helpful if you need to create different file paths under +the same directory. + +Using `os.path.join` on Windows: + +```python +>>> my_files = ['accounts.txt', 'details.csv', 'invite.docx'] + +>>> for filename in my_files: +>>> print(os.path.join('C:\\Users\\asweigart', filename)) +C:\Users\asweigart\accounts.txt +C:\Users\asweigart\details.csv +C:\Users\asweigart\invite.docx +``` + +Using `pathlib` on \*nix: + +```python +>>> my_files = ['accounts.txt', 'details.csv', 'invite.docx'] +>>> home = Path.home() +>>> for filename in my_files: +>>> print(home / filename) +/home/asweigart/accounts.txt +/home/asweigart/details.csv +/home/asweigart/invite.docx +``` + + + +### The Current Working Directory + +Using `os` on Windows: + +```python +>>> import os + +>>> os.getcwd() +'C:\\Python34' +>>> os.chdir('C:\\Windows\\System32') + +>>> os.getcwd() +'C:\\Windows\\System32' +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> from os import chdir + +>>> print(Path.cwd()) +/home/asweigart + +>>> chdir('/usr/lib/python3.6') +>>> print(Path.cwd()) +/usr/lib/python3.6 +``` + + + +### Creating New Folders + +Using `os` on Windows: + +```python +>>> import os +>>> os.makedirs('C:\\delicious\\walnut\\waffles') +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> cwd = Path.cwd() +>>> (cwd / 'delicious' / 'walnut' / 'waffles').mkdir() +Traceback (most recent call last): + File "", line 1, in + File "/usr/lib/python3.6/pathlib.py", line 1226, in mkdir + self._accessor.mkdir(self, mode) + File "/usr/lib/python3.6/pathlib.py", line 387, in wrapped + return strfunc(str(pathobj), *args) +FileNotFoundError: [Errno 2] No such file or directory: '/home/asweigart/delicious/walnut/waffles' +``` + +Oh no, we got a nasty error! The reason is that the 'delicious' directory does +not exist, so we cannot make the 'walnut' and the 'waffles' directories under +it. To fix this, do: + +```python +>>> from pathlib import Path +>>> cwd = Path.cwd() +>>> (cwd / 'delicious' / 'walnut' / 'waffles').mkdir(parents=True) +``` + +And all is good :) + + + +### Absolute vs. Relative Paths + +There are two ways to specify a file path. + +- An absolute path, which always begins with the root folder +- A relative path, which is relative to the program’s current working directory + +There are also the dot (.) and dot-dot (..) folders. These are not real folders but special names that can be used in a path. A single period (“dot”) for a folder name is shorthand for “this directory.” Two periods (“dot-dot”) means “the parent folder.” + + + +### Handling Absolute and Relative Paths + +To see if a path is an absolute path: + +Using `os.path` on \*nix: + +```python +>>> import os +>>> os.path.isabs('/') +True +>>> os.path.isabs('..') +False +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> Path('/').is_absolute() +True +>>> Path('..').is_absolute() +False +``` + +You can extract an absolute path with both `os.path` and `pathlib` + +Using `os.path` on \*nix: + +```python +>>> import os +>>> os.getcwd() +'/home/asweigart' +>>> os.path.abspath('..') +'/home' +``` + +Using `pathlib` on \*nix: + +```python +from pathlib import Path +print(Path.cwd()) +/home/asweigart +print(Path('..').resolve()) +/home +``` + +You can get a relative path from a starting path to another path. + +Using `os.path` on \*nix: + +```python +>>> import os +>>> os.path.relpath('/etc/passwd', '/') +'etc/passwd' +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> print(Path('/etc/passwd').relative_to('/')) +etc/passwd +``` + + + +### Checking Path Validity + +Checking if a file/directory exists: + +Using `os.path` on \*nix: + +```python +import os +>>> os.path.exists('.') +True +>>> os.path.exists('setup.py') +True +>>> os.path.exists('/etc') +True +>>> os.path.exists('nonexistentfile') +False +``` + +Using `pathlib` on \*nix: + +```python +from pathlib import Path +>>> Path('.').exists() +True +>>> Path('setup.py').exists() +True +>>> Path('/etc').exists() +True +>>> Path('nonexistentfile').exists() +False +``` + +Checking if a path is a file: + +Using `os.path` on \*nix: + +```python +>>> import os +>>> os.path.isfile('setup.py') +True +>>> os.path.isfile('/home') +False +>>> os.path.isfile('nonexistentfile') +False +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> Path('setup.py').is_file() +True +>>> Path('/home').is_file() +False +>>> Path('nonexistentfile').is_file() +False +``` + +Checking if a path is a directory: + +Using `os.path` on \*nix: + +```python +>>> import os +>>> os.path.isdir('/') +True +>>> os.path.isdir('setup.py') +False +>>> os.path.isdir('/spam') +False +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> Path('/').is_dir() +True +>>> Path('setup.py').is_dir() +False +>>> Path('/spam').is_dir() +False +``` + + + +### Finding File Sizes and Folder Contents + +Getting a file's size in bytes: + +Using `os.path` on Windows: + +```python +>>> import os +>>> os.path.getsize('C:\\Windows\\System32\\calc.exe') +776192 +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> stat = Path('/bin/python3.6').stat() +>>> print(stat) # stat contains some other information about the file as well +os.stat_result(st_mode=33261, st_ino=141087, st_dev=2051, st_nlink=2, st_uid=0, +--snip-- +st_gid=0, st_size=10024, st_atime=1517725562, st_mtime=1515119809, st_ctime=1517261276) +>>> print(stat.st_size) # size in bytes +10024 +``` + +Listing directory contents using `os.listdir` on Windows: + +```python +>>> import os +>>> os.listdir('C:\\Windows\\System32') +['0409', '12520437.cpx', '12520850.cpx', '5U877.ax', 'aaclient.dll', +--snip-- +'xwtpdui.dll', 'xwtpw32.dll', 'zh-CN', 'zh-HK', 'zh-TW', 'zipfldr.dll'] +``` + +Listing directory contents using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> for f in Path('/usr/bin').iterdir(): +>>> print(f) +... +/usr/bin/tiff2rgba +/usr/bin/iconv +/usr/bin/ldd +/usr/bin/cache_restore +/usr/bin/udiskie +/usr/bin/unix2dos +/usr/bin/t1reencode +/usr/bin/epstopdf +/usr/bin/idle3 +... +``` + +To find the total size of all the files in this directory: + +**WARNING**: Directories themselves also have a size! So you might want to +check for whether a path is a file or directory using the methods in the methods discussed in the above section! + +Using `os.path.getsize()` and `os.listdir()` together on Windows: + +```python +>>> import os +>>> total_size = 0 + +>>> for filename in os.listdir('C:\\Windows\\System32'): + total_size = total_size + os.path.getsize(os.path.join('C:\\Windows\\System32', filename)) + +>>> print(total_size) +1117846456 +``` + +Using `pathlib` on \*nix: + +```python +>>> from pathlib import Path +>>> total_size = 0 + +>>> for sub_path in Path('/usr/bin').iterdir(): +... total_size += sub_path.stat().st_size +>>> +>>> print(total_size) +1903178911 +``` + + + +### Copying Files and Folders + +The shutil module provides functions for copying files, as well as entire folders. + +```python +>>> import shutil, os + +>>> os.chdir('C:\\') + +>>> shutil.copy('C:\\spam.txt', 'C:\\delicious') + 'C:\\delicious\\spam.txt' + +>>> shutil.copy('eggs.txt', 'C:\\delicious\\eggs2.txt') + 'C:\\delicious\\eggs2.txt' +``` + +While shutil.copy() will copy a single file, shutil.copytree() will copy an entire folder and every folder and file contained in it: + +```python +>>> import shutil, os + +>>> os.chdir('C:\\') + +>>> shutil.copytree('C:\\bacon', 'C:\\bacon_backup') +'C:\\bacon_backup' +``` + + + +### Moving and Renaming Files and Folders + +```python +>>> import shutil +>>> shutil.move('C:\\bacon.txt', 'C:\\eggs') +'C:\\eggs\\bacon.txt' +``` + +The destination path can also specify a filename. In the following example, the source file is moved and renamed: + +```python +>>> shutil.move('C:\\bacon.txt', 'C:\\eggs\\new_bacon.txt') +'C:\\eggs\\new_bacon.txt' +``` + + If there is no eggs folder, then move() will rename bacon.txt to a file named eggs. + +```python +>>> shutil.move('C:\\bacon.txt', 'C:\\eggs') +'C:\\eggs' +``` + + + +### Permanently Deleting Files and Folders + +- Calling os.unlink(path) or Path.unlink() will delete the file at path. + +- Calling os.rmdir(path) or Path.rmdir() will delete the folder at path. This folder must be empty of any files or folders. + +- Calling shutil.rmtree(path) will remove the folder at path, and all files and folders it contains will also be deleted. + + + +### Safe Deletes with the send2trash Module + + You can install this module by running pip install send2trash from a Terminal window. + +```python +>>> import send2trash + +>>> with open('bacon.txt', 'a') as bacon_file: # creates the file +... bacon_file.write('Bacon is not a vegetable.') +25 + +>>> send2trash.send2trash('bacon.txt') +``` + + + +### Walking a Directory Tree + +```python +>>> import os +>>> +>>> for folder_name, subfolders, filenames in os.walk('C:\\delicious'): +>>> print('The current folder is {}'.format(folder_name)) +>>> +>>> for subfolder in subfolders: +>>> print('SUBFOLDER OF {}: {}'.format(folder_name, subfolder)) +>>> for filename in filenames: +>>> print('FILE INSIDE {}: {}'.format(folder_name, filename)) +>>> +>>> print('') +The current folder is C:\delicious +SUBFOLDER OF C:\delicious: cats +SUBFOLDER OF C:\delicious: walnut +FILE INSIDE C:\delicious: spam.txt + +The current folder is C:\delicious\cats +FILE INSIDE C:\delicious\cats: catnames.txt +FILE INSIDE C:\delicious\cats: zophie.jpg + +The current folder is C:\delicious\walnut +SUBFOLDER OF C:\delicious\walnut: waffles + +The current folder is C:\delicious\walnut\waffles +FILE INSIDE C:\delicious\walnut\waffles: butter.txt +``` + + + +`pathlib` provides a lot more functionality than the ones listed above, +like getting file name, getting file extension, reading/writing a file without +manually opening it, etc. Check out the +[official documentation](https://docs.python.org/3/library/pathlib.html) +if you want to know more! + +## Reading and Writing Files + +### The File Reading/Writing Process + +To read/write to a file in Python, you will want to use the `with` +statement, which will close the file for you after you are done. + + + +### Opening and reading files with the open() function + +```python +>>> with open('C:\\Users\\your_home_folder\\hello.txt') as hello_file: +... hello_content = hello_file.read() +>>> hello_content +'Hello World!' + +>>> # Alternatively, you can use the *readlines()* method to get a list of string values from the file, one string for each line of text: + +>>> with open('sonnet29.txt') as sonnet_file: +... sonnet_file.readlines() +[When, in disgrace with fortune and men's eyes,\n', ' I all alone beweep my +outcast state,\n', And trouble deaf heaven with my bootless cries,\n', And +look upon myself and curse my fate,'] + +>>> # You can also iterate through the file line by line: +>>> with open('sonnet29.txt') as sonnet_file: +... for line in sonnet_file: # note the new line character will be included in the line +... print(line, end='') + +When, in disgrace with fortune and men's eyes, +I all alone beweep my outcast state, +And trouble deaf heaven with my bootless cries, +And look upon myself and curse my fate, +``` + + + +### Writing to Files + +```python +>>> with open('bacon.txt', 'w') as bacon_file: +... bacon_file.write('Hello world!\n') +13 + +>>> with open('bacon.txt', 'a') as bacon_file: +... bacon_file.write('Bacon is not a vegetable.') +25 + +>>> with open('bacon.txt') as bacon_file: +... content = bacon_file.read() + +>>> print(content) +Hello world! +Bacon is not a vegetable. +``` + + + +### Saving Variables with the shelve Module + +To save variables: + +```python +>>> import shelve + +>>> cats = ['Zophie', 'Pooka', 'Simon'] +>>> with shelve.open('mydata') as shelf_file: +... shelf_file['cats'] = cats +``` + +To open and read variables: + +```python +>>> with shelve.open('mydata') as shelf_file: +... print(type(shelf_file)) +... print(shelf_file['cats']) + +['Zophie', 'Pooka', 'Simon'] +``` + +Just like dictionaries, shelf values have keys() and values() methods that will return list-like values of the keys and values in the shelf. Since these methods return list-like values instead of true lists, you should pass them to the list() function to get them in list form. + +```python +>>> with shelve.open('mydata') as shelf_file: +... print(list(shelf_file.keys())) +... print(list(shelf_file.values())) +['cats'] +[['Zophie', 'Pooka', 'Simon']] +``` + + + +### Saving Variables with the pprint.pformat() Function + +```python +>>> import pprint + +>>> cats = [{'name': 'Zophie', 'desc': 'chubby'}, {'name': 'Pooka', 'desc': 'fluffy'}] + +>>> pprint.pformat(cats) +"[{'desc': 'chubby', 'name': 'Zophie'}, {'desc': 'fluffy', 'name': 'Pooka'}]" + +>>> with open('myCats.py', 'w') as file_obj: +... file_obj.write('cats = {}\n'.format(pprint.pformat(cats))) +83 +``` + + + +### Reading ZIP Files + +```python +>>> import zipfile, os + +>>> os.chdir('C:\\') # move to the folder with example.zip +>>> with zipfile.ZipFile('example.zip') as example_zip: +... print(example_zip.namelist()) +... spam_info = example_zip.getinfo('spam.txt') +... print(spam_info.file_size) +... print(spam_info.compress_size) +... print('Compressed file is %sx smaller!' % (round(spam_info.file_size / spam_info.compress_size, 2))) + +['spam.txt', 'cats/', 'cats/catnames.txt', 'cats/zophie.jpg'] +13908 +3828 +'Compressed file is 3.63x smaller!' +``` + + + +### Extracting from ZIP Files + +The extractall() method for ZipFile objects extracts all the files and folders from a ZIP file into the current working directory. + +```python +>>> import zipfile, os + +>>> os.chdir('C:\\') # move to the folder with example.zip + +>>> with zipfile.ZipFile('example.zip') as example_zip: +... example_zip.extractall() +``` + +The extract() method for ZipFile objects will extract a single file from the ZIP file. Continue the interactive shell example: + +```python +>>> with zipfile.ZipFile('example.zip') as example_zip: +... print(example_zip.extract('spam.txt')) +... print(example_zip.extract('spam.txt', 'C:\\some\\new\\folders')) +'C:\\spam.txt' +'C:\\some\\new\\folders\\spam.txt' +``` + + + +### Creating and Adding to ZIP Files + +```python +>>> import zipfile + +>>> with zipfile.ZipFile('new.zip', 'w') as new_zip: +... new_zip.write('spam.txt', compress_type=zipfile.ZIP_DEFLATED) +``` + +This code will create a new ZIP file named new.zip that has the compressed contents of spam.txt. + + + +## JSON, YAML and configuration files + +### JSON + +Open a JSON file with: + +```python +import json +with open("filename.json", "r") as f: + content = json.loads(f.read()) +``` + +Write a JSON file with: + +```python +import json + +content = {"name": "Joe", "age": 20} +with open("filename.json", "w") as f: + f.write(json.dumps(content, indent=2)) +``` + + + +### YAML + +Compared to JSON, YAML allows a much better humain maintainance and gives ability to add comments. +It is a convinient choice for configuration files where human will have to edit. + +There are two main librairies allowing to access to YAML files: + +- [PyYaml](https://pypi.python.org/pypi/PyYAML) +- [Ruamel.yaml](https://pypi.python.org/pypi/ruamel.yaml) + +Install them using `pip install` in your virtual environment. + +The first one it easier to use but the second one, Ruamel, implements much better the YAML +specification, and allow for example to modify a YAML content without altering comments. + +Open a YAML file with: + +```python +from ruamel.yaml import YAML + +with open("filename.yaml") as f: + yaml=YAML() + yaml.load(f) +``` + + + +### Anyconfig + +[Anyconfig](https://pypi.python.org/pypi/anyconfig) is a very handy package allowing to abstract completly the underlying configuration file format. It allows to load a Python dictionary from JSON, YAML, TOML, and so on. + +Install it with: + +```bash +pip install anyconfig +``` + +Usage: + +```python +import anyconfig + +conf1 = anyconfig.load("/path/to/foo/conf.d/a.yml") +``` + + + +## Debugging + +### Raising Exceptions + +Exceptions are raised with a raise statement. In code, a raise statement consists of the following: + +- The raise keyword +- A call to the Exception() function +- A string with a helpful error message passed to the Exception() function + +```python +>>> raise Exception('This is the error message.') +Traceback (most recent call last): + File "", line 1, in + raise Exception('This is the error message.') +Exception: This is the error message. +``` + +Often it’s the code that calls the function, not the function itself, that knows how to handle an expection. So you will commonly see a raise statement inside a function and the try and except statements in the code calling the function. + +```python +def box_print(symbol, width, height): + if len(symbol) != 1: + raise Exception('Symbol must be a single character string.') + if width <= 2: + raise Exception('Width must be greater than 2.') + if height <= 2: + raise Exception('Height must be greater than 2.') + print(symbol * width) + for i in range(height - 2): + print(symbol + (' ' * (width - 2)) + symbol) + print(symbol * width) +for sym, w, h in (('*', 4, 4), ('O', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)): + try: + box_print(sym, w, h) + except Exception as err: + print('An exception happened: ' + str(err)) +``` + + + +### Getting the Traceback as a String + +The traceback is displayed by Python whenever a raised exception goes unhandled. But can also obtain it as a string by calling traceback.format_exc(). This function is useful if you want the information from an exception’s traceback but also want an except statement to gracefully handle the exception. You will need to import Python’s traceback module before calling this function. + +```python +>>> import traceback + +>>> try: +>>> raise Exception('This is the error message.') +>>> except: +>>> with open('errorInfo.txt', 'w') as error_file: +>>> error_file.write(traceback.format_exc()) +>>> print('The traceback info was written to errorInfo.txt.') +116 +The traceback info was written to errorInfo.txt. +``` + +The 116 is the return value from the write() method, since 116 characters were written to the file. The traceback text was written to errorInfo.txt. + + Traceback (most recent call last): + File "", line 2, in + Exception: This is the error message. + + + +### Assertions + +An assertion is a sanity check to make sure your code isn’t doing something obviously wrong. These sanity checks are performed by assert statements. If the sanity check fails, then an AssertionError exception is raised. In code, an assert statement consists of the following: + +- The assert keyword +- A condition (that is, an expression that evaluates to True or False) +- A comma +- A string to display when the condition is False + +```python +>>> pod_bay_door_status = 'open' + +>>> assert pod_bay_door_status == 'open', 'The pod bay doors need to be "open".' + +>>> pod_bay_door_status = 'I\'m sorry, Dave. I\'m afraid I can\'t do that.' + +>>> assert pod_bay_door_status == 'open', 'The pod bay doors need to be "open".' + +Traceback (most recent call last): + File "", line 1, in + assert pod_bay_door_status == 'open', 'The pod bay doors need to be "open".' +AssertionError: The pod bay doors need to be "open". +``` + +In plain English, an assert statement says, “I assert that this condition holds true, and if not, there is a bug somewhere in the program.” Unlike exceptions, your code should not handle assert statements with try and except; if an assert fails, your program should crash. By failing fast like this, you shorten the time between the original cause of the bug and when you first notice the bug. This will reduce the amount of code you will have to check before finding the code that’s causing the bug. + +Disabling Assertions + +Assertions can be disabled by passing the -O option when running Python. + + + +### Logging + +To enable the logging module to display log messages on your screen as your program runs, copy the following to the top of your program (but under the #! python shebang line): + +```python +import logging + +logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s- %(message)s') +``` + +Say you wrote a function to calculate the factorial of a number. In mathematics, factorial 4 is 1 × 2 × 3 × 4, or 24. Factorial 7 is 1 × 2 × 3 × 4 × 5 × 6 × 7, or 5,040. Open a new file editor window and enter the following code. It has a bug in it, but you will also enter several log messages to help yourself figure out what is going wrong. Save the program as factorialLog.py. + +```python +>>> import logging +>>> +>>> logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s- %(message)s') +>>> +>>> logging.debug('Start of program') +>>> +>>> def factorial(n): +>>> +>>> logging.debug('Start of factorial(%s)' % (n)) +>>> total = 1 +>>> +>>> for i in range(1, n + 1): +>>> total *= i +>>> logging.debug('i is ' + str(i) + ', total is ' + str(total)) +>>> +>>> logging.debug('End of factorial(%s)' % (n)) +>>> +>>> return total +>>> +>>> print(factorial(5)) +>>> logging.debug('End of program') +2015-05-23 16:20:12,664 - DEBUG - Start of program +2015-05-23 16:20:12,664 - DEBUG - Start of factorial(5) +2015-05-23 16:20:12,665 - DEBUG - i is 0, total is 0 +2015-05-23 16:20:12,668 - DEBUG - i is 1, total is 0 +2015-05-23 16:20:12,670 - DEBUG - i is 2, total is 0 +2015-05-23 16:20:12,673 - DEBUG - i is 3, total is 0 +2015-05-23 16:20:12,675 - DEBUG - i is 4, total is 0 +2015-05-23 16:20:12,678 - DEBUG - i is 5, total is 0 +2015-05-23 16:20:12,680 - DEBUG - End of factorial(5) +0 +2015-05-23 16:20:12,684 - DEBUG - End of program +``` + + + +### Logging Levels + +Logging levels provide a way to categorize your log messages by importance. There are five logging levels, described in Table 10-1 from least to most important. Messages can be logged at each level using a different logging function. + +| Level | Logging Function | Description | +| ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| `DEBUG` | `logging.debug()` | The lowest level. Used for small details. Usually you care about these messages only when diagnosing problems. | +| `INFO` | `logging.info()` | Used to record information on general events in your program or confirm that things are working at their point in the program. | +| `WARNING` | `logging.warning()` | Used to indicate a potential problem that doesn’t prevent the program from working but might do so in the future. | +| `ERROR` | `logging.error()` | Used to record an error that caused the program to fail to do something. | +| `CRITICAL` | `logging.critical()` | The highest level. Used to indicate a fatal error that has caused or is about to cause the program to stop running entirely. | + + + +### Disabling Logging + +After you’ve debugged your program, you probably don’t want all these log messages cluttering the screen. The logging.disable() function disables these so that you don’t have to go into your program and remove all the logging calls by hand. + +```python +>>> import logging + +>>> logging.basicConfig(level=logging.INFO, format=' %(asctime)s -%(levelname)s - %(message)s') + +>>> logging.critical('Critical error! Critical error!') +2015-05-22 11:10:48,054 - CRITICAL - Critical error! Critical error! + +>>> logging.disable(logging.CRITICAL) + +>>> logging.critical('Critical error! Critical error!') + +>>> logging.error('Error! Error!') +``` + + + +### Logging to a File + +Instead of displaying the log messages to the screen, you can write them to a text file. The logging.basicConfig() function takes a filename keyword argument, like so: + +```python +import logging + +logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') +``` + + + +## Lambda Functions + +This function: + +```python +>>> def add(x, y): + return x + y + +>>> add(5, 3) +8 +``` + +Is equivalent to the *lambda* function: + +```python +>>> add = lambda x, y: x + y +>>> add(5, 3) +8 +``` + +It's not even need to bind it to a name like add before: + +```python +>>> (lambda x, y: x + y)(5, 3) +8 +``` + +Like regular nested functions, lambdas also work as lexical closures: + +```python +>>> def make_adder(n): + return lambda x: x + n + +>>> plus_3 = make_adder(3) +>>> plus_5 = make_adder(5) + +>>> plus_3(4) +7 +>>> plus_5(4) +9 +``` + +Note: lambda can only evaluate an expression, like a single line of code. + + + +## Ternary Conditional Operator + +Many programming languages have a ternary operator, which define a conditional expression. The most common usage is to make a terse simple conditional assignment statement. In other words, it offers one-line code to evaluate the first expression if the condition is true, otherwise it evaluates the second expression. + + if else + +Example: + +```python +>>> age = 15 + +>>> print('kid' if age < 18 else 'adult') +kid +``` + +Ternary operators can be chained: + +```python +>>> age = 15 + +>>> print('kid' if age < 13 else 'teenager' if age < 18 else 'adult') +teenager +``` + +The code above is equivalent to: + +```python +if age < 18: + if age < 13: + print('kid') + else: + print('teenager') +else: + print('adult') +``` + + + +## args and kwargs + +The names ```args and kwargs``` are arbitrary - the important thing are the ```*``` and ```**``` operators. They can mean: + +1. In a function declaration, ```*``` means “pack all remaining positional arguments into a tuple named ``”, while ```**``` is the same for keyword arguments (except it uses a dictionary, not a tuple). + +2. In a function call, ```*``` means “unpack tuple or list named `` to positional arguments at this position”, while ```**``` is the same for keyword arguments. + +For example you can make a function that you can use to call any other function, no matter what parameters it has: + +```python +def forward(f, *args, **kwargs): + return f(*args, **kwargs) +``` + +Inside forward, args is a tuple (of all positional arguments except the first one, because we specified it - the f), kwargs is a dict. Then we call f and unpack them so they become normal arguments to f. + +You use ```*args``` when you have an indefinite amount of positional arguments. + +```python +>>> def fruits(*args): +>>> for fruit in args: +>>> print(fruit) + +>>> fruits("apples", "bananas", "grapes") + +"apples" +"bananas" +"grapes" +``` + +Similarly, you use ```**kwargs``` when you have an indefinite number of keyword arguments. + +```python +>>> def fruit(**kwargs): +>>> for key, value in kwargs.items(): +>>> print("{0}: {1}".format(key, value)) + +>>> fruit(name = "apple", color = "red") + +name: apple +color: red +``` + +```python +>>> def show(arg1, arg2, *args, kwarg1=None, kwarg2=None, **kwargs): +>>> print(arg1) +>>> print(arg2) +>>> print(args) +>>> print(kwarg1) +>>> print(kwarg2) +>>> print(kwargs) + +>>> data1 = [1,2,3] +>>> data2 = [4,5,6] +>>> data3 = {'a':7,'b':8,'c':9} + +>>> show(*data1,*data2, kwarg1="python",kwarg2="cheatsheet",**data3) +1 +2 +(3, 4, 5, 6) +python +cheatsheet +{'a': 7, 'b': 8, 'c': 9} + +>>> show(*data1, *data2, **data3) +1 +2 +(3, 4, 5, 6) +None +None +{'a': 7, 'b': 8, 'c': 9} + +# If you do not specify ** for kwargs +>>> show(*data1, *data2, *data3) +1 +2 +(3, 4, 5, 6, "a", "b", "c") +None +None +{} +``` + +### Things to Remember(args) + +1. Functions can accept a variable number of positional arguments by using ```*args``` in the def statement. +2. You can use the items from a sequence as the positional arguments for a function with the ```*``` operator. +3. Using the ```*``` operator with a generator may cause your program to run out of memory and crash. +4. Adding new positional parameters to functions that accept ```*args``` can introduce hard-to-find bugs. + +### Things to Remember(kwargs) + +1. Function arguments can be specified by position or by keyword. +2. Keywords make it clear what the purpose of each argument is when it would be confusing with only positional arguments. +3. Keyword arguments with default values make it easy to add new behaviors to a function, especially when the function has existing callers. +4. Optional keyword arguments should always be passed by keyword instead of by position. + + + +## Context Manager + +While Python's context managers are widely used, few understand the purpose behind their use. These statements, commonly used with reading and writing files, assist the application in conserving system memory and improve resource management by ensuring specific resources are only in use for certain processes. + +### with statement + +A context manager is an object that is notified when a context (a block of code) starts and ends. You commonly use one with the with statement. It takes care of the notifying. + +For example, file objects are context managers. When a context ends, the file object is closed automatically: + +```python +>>> with open(filename) as f: +>>> file_contents = f.read() + +# the open_file object has automatically been closed. +``` + +Anything that ends execution of the block causes the context manager's exit method to be called. This includes exceptions, and can be useful when an error causes you to prematurely exit from an open file or connection. Exiting a script without properly closing files/connections is a bad idea, that may cause data loss or other problems. By using a context manager you can ensure that precautions are always taken to prevent damage or loss in this way. + +### Writing your own contextmanager using generator syntax + +It is also possible to write a context manager using generator syntax thanks to the ```contextlib.contextmanager``` decorator: + +```python +>>> import contextlib +>>> @contextlib.contextmanager +... def context_manager(num): +... print('Enter') +... yield num + 1 +... print('Exit') +>>> with context_manager(2) as cm: +... # the following instructions are run when the 'yield' point of the context +... # manager is reached. +... # 'cm' will have the value that was yielded +... print('Right in the middle with cm = {}'.format(cm)) +Enter +Right in the middle with cm = 3 +Exit + +>>> +``` + + + +## `__main__` Top-level script environment + +`__main__` is the name of the scope in which top-level code executes. +A module’s __name__ is set equal to `__main__` when read from standard input, a script, or from an interactive prompt. + +A module can discover whether or not it is running in the main scope by checking its own `__name__`, which allows a common idiom for conditionally executing code in a module when it is run as a script or with `python -m` but not when it is imported: + +```python +>>> if __name__ == "__main__": +... # execute only if run as a script +... main() +``` + +For a package, the same effect can be achieved by including a __main__.py module, the contents of which will be executed when the module is run with -m + +For example we are developing script which is designed to be used as module, we should do: + +```python +>>> # Python program to execute function directly +>>> def add(a, b): +... return a+b +... +>>> add(10, 20) # we can test it by calling the function save it as calculate.py +30 +>>> # Now if we want to use that module by importing we have to comment out our call, +>>> # Instead we can write like this in calculate.py +>>> if __name__ == "__main__": +... add(3, 5) +... +>>> import calculate +>>> calculate.add(3, 5) +8 +``` + +### Advantages + +1. Every Python module has it’s `__name__` defined and if this is `__main__`, it implies that the module is being run standalone by the user and we can do corresponding appropriate actions. +2. If you import this script as a module in another script, the __name__ is set to the name of the script/module. +3. Python files can act as either reusable modules, or as standalone programs. +4. if `__name__ == “main”:` is used to execute some code only if the file was run directly, and not imported. + + + +## setup.py + +The setup script is the centre of all activity in building, distributing, and installing modules using the Distutils. The main purpose of the setup script is to describe your module distribution to the Distutils, so that the various commands that operate on your modules do the right thing. + +The `setup.py` file is at the heart of a Python project. It describes all of the metadata about your project. There a quite a few fields you can add to a project to give it a rich set of metadata describing the project. However, there are only three required fields: name, version, and packages. The name field must be unique if you wish to publish your package on the Python Package Index (PyPI). The version field keeps track of different releases of the project. The packages field describes where you’ve put the Python source code within your project. + +This allows you to easily install Python packages. Often it's enough to write: + +```bash +python setup.py install +``` + +and module will install itself. + +Our initial setup.py will also include information about the license and will re-use the README.txt file for the long_description field. This will look like: + +```python +>>> from distutils.core import setup +>>> setup( +... name='pythonCheatsheet', +... version='0.1', +... packages=['pipenv',], +... license='MIT', +... long_description=open('README.txt').read(), +... ) +``` + +Find more information visit [http://docs.python.org/install/index.html](http://docs.python.org/install/index.html). + + + +## Dataclasses + +`Dataclasses` are python classes but are suited for storing data objects. +This module provides a decorator and functions for automatically adding generated special methods such as `__init__()` and `__repr__()` to user-defined classes. + +### Features + +1. They store data and represent a certain data type. Ex: A number. For people familiar with ORMs, a model instance is a data object. It represents a specific kind of entity. It holds attributes that define or represent the entity. + +2. They can be compared to other objects of the same type. Ex: A number can be greater than, less than, or equal to another number. + +Python 3.7 provides a decorator dataclass that is used to convert a class into a dataclass. + +python 2.7 + +```python +>>> class Number: +... def __init__(self, val): +... self.val = val +... +>>> obj = Number(2) +>>> obj.val +2 +``` + +with dataclass + +```python +>>> @dataclass +... class Number: +... val: int +... +>>> obj = Number(2) +>>> obj.val +2 +``` + + + +### Default values + +It is easy to add default values to the fields of your data class. + +```python +>>> @dataclass +... class Product: +... name: str +... count: int = 0 +... price: float = 0.0 +... +>>> obj = Product("Python") +>>> obj.name +Python +>>> obj.count +0 +>>> obj.price +0.0 +``` + +### Type hints + +It is mandatory to define the data type in dataclass. However, If you don't want specify the datatype then, use ```typing.Any```. + +```python +>>> from dataclasses import dataclass +>>> from typing import Any + +>>> @dataclass +... class WithoutExplicitTypes: +... name: Any +... value: Any = 42 +... +``` + + + +## Virtual Environment + +The use of a Virtual Environment is to test python code in encapsulated environments and to also avoid filling the base Python installation with libraries we might use for only one project. + + + +### virtualenv + +1. Install virtualenv + + pip install virtualenv + +1. Install virtualenvwrapper-win (Windows) + + pip install virtualenvwrapper-win + +Usage: + +1. Make a Virtual Environment + + mkvirtualenv HelloWold + + Anything we install now will be specific to this project. And available to the projects we connect to this environment. + +1. Set Project Directory + + To bind our virtualenv with our current working directory we simply enter: + + setprojectdir . + +1. Deactivate + + To move onto something else in the command line type ‘deactivate’ to deactivate your environment. + + deactivate + + Notice how the parenthesis disappear. + +1. Workon + + Open up the command prompt and type ‘workon HelloWold’ to activate the environment and move into your root project folder + + workon HelloWold + + + +### poetry + +> [Poetry](https://poetry.eustace.io/) is a tool for dependency management and packaging in Python. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you. + +1. Install Poetry + + pip install --user poetry + +2. Create a new project + + poetry new my-project + + This will create a my-project directory: + + my-project + ├── pyproject.toml + ├── README.rst + ├── poetry_demo + │ └── __init__.py + └── tests + ├── __init__.py + └── test_poetry_demo.py + + The pyproject.toml file will orchestrate your project and its dependencies: + + [tool.poetry] + name = "my-project" + version = "0.1.0" + description = "" + authors = ["your name "] + + [tool.poetry.dependencies] + python = "*" + + [tool.poetry.dev-dependencies] + pytest = "^3.4" + +3. Packages + + To add dependencies to your project, you can specify them in the tool.poetry.dependencies section: + + [tool.poetry.dependencies] + pendulum = "^1.4" + + Also, instead of modifying the pyproject.toml file by hand, you can use the add command and it will automatically find a suitable version constraint. + + $ poetry add pendulum + + To install the dependencies listed in the pyproject.toml: + + poetry install + + To remove dependencies: + + poetry remove pendulum + +For more information, check the [documentation](https://poetry.eustace.io/docs/). + + + +### pipenv + +> [Pipenv](https://pipenv.readthedocs.io/en/latest/) is a tool that aims to bring the best of all packaging worlds (bundler, composer, npm, cargo, yarn, etc.) to the Python world. Windows is a first-class citizen, in our world. + +1. Install pipenv + + pip install pipenv + +1. Enter your Project directory and install the Packages for your project + + cd my_project + pipenv install + + Pipenv will install your package and create a Pipfile for you in your project’s directory. The Pipfile is used to track which dependencies your project needs in case you need to re-install them. + +1. Uninstall Packages + + pipenv uninstall + +1. Activate the Virtual Environment associated with your Python project + + pipenv shell + +1. Exit the Virtual Environment + + exit + +Find more information and a video in [docs.pipenv.org](https://docs.pipenv.org/). + + + +### anaconda + +[Anaconda](https://anaconda.org/) is another popular tool to manage python packages. + +> Where packages, notebooks, projects and environments are shared. +Your place for free public conda package hosting. + +Usage: + +1. Make a Virtual Environment + + conda create -n HelloWorld + +2. To use the Virtual Environment, activate it by: + + conda activate HelloWorld + + Anything installed now will be specific to the project HelloWorld + +3. Exit the Virtual Environment + + conda deactivate + + + diff --git a/chap-2.md b/chap-2.md new file mode 100644 index 0000000..c717901 --- /dev/null +++ b/chap-2.md @@ -0,0 +1,42 @@ +# Lexical matters +## Lines + + * Python does what you want it to do most of the time so that you only have to add extra characters some of the time. + * Statement separator is a semi-colon, but is only needed when there is more than one statement on a line. And, writing more than one statement on the same line is considered bad form. + * Continuation lines -- A back-slash as last character of the line makes the following line a continuation of the current line. But, note that an opening "context" (parenthesis, square bracket, or curly bracket) makes the back-slash unnecessary. + +## Comments + + * Everything after "#" on a line is ignored. No block comments, but doc strings are a comment in quotes at the beginning of a module, class, method or function. Also, editors with support for Python often provide the ability to comment out selected blocks of code, usually with "##". +## Names and tokens + + * Allowed characters: a-z A-Z 0-9 underscore, and must begin with a letter or underscore. + * Names and identifiers are case sensitive. + * Identifiers can be of unlimited length. + * Special names, customizing, etc. -- Usually begin and end in double underscores. + * Special name classes -- Single and double underscores. + * Single leading single underscore -- Suggests a "private" method or variable name. Not imported by "from module import *". + * Single trailing underscore -- Use it to avoid conflicts with Python keywords. + * Double leading underscores -- Used in a class definition to cause name mangling (weak hiding). But, not often used. + * Naming conventions -- Not rigid, but: + * Modules and packages -- all lower case. + * Globals and constants -- Upper case. + * Classes -- Bumpy caps with initial upper. + * Methods and functions -- All lower case with words separated by underscores. + * Local variables -- Lower case (with underscore between words) or bumpy caps with initial lower or your choice. + * Good advice -- Follow the conventions used in the code on which you are working. + * Names/variables in Python do not have a type. Values have types. + +## Blocks and indentation + + * Python represents block structure and nested block structure with indentation, not with begin and end brackets. + * The empty block -- Use the pass no-op statement. + +### Benefits of the use of indentation to indicate structure: + + * Reduces the need for a coding standard. Only need to specify that indentation is 4 spaces and no hard tabs. + * Reduces inconsistency. Code from different sources follow the same indentation style. It has to. + * Reduces work. Only need to get the indentation correct, not both indentation and brackets. + * Reduces clutter. Eliminates all the curly brackets. + * If it looks correct, it is correct. Indentation cannot fool the reader. + diff --git a/chap-3.md b/chap-3.md new file mode 100644 index 0000000..db2a38e --- /dev/null +++ b/chap-3.md @@ -0,0 +1,21 @@ +# Data Types in Python + * Everything in Python programming is an object, and each object has its own unique identity(a type and a value). + + * There are many native(built-in) data types available in Python. + +## Some important are: + + * Numbers: An are integers (such as 1, 2, 3…), floats (such as 2.6, 4.8 etc), fractions (such as ½. ¾ etc) or even complex numbers. + - int (signed integer) + - float + - long + - complex + * Sequences: + - Strings: Sequence of Unicode characters, like an HTML document. + - Bytes/Byte array: Any type of file. + - Lists: An ordered sequence of values. + - Tuples: An ordered immutable sequence of values. + + * Boolean: Holds either true or false values. + * Sets: An unordered container of values. + * Dictionaries: An key-paired values set in an unordered way. diff --git a/class-sample-1.md b/class-sample-1.md new file mode 100644 index 0000000..ba022b2 --- /dev/null +++ b/class-sample-1.md @@ -0,0 +1,107 @@ +Defining Class And Object + +A class is a technique to group functions and data members and put them in a container so that they can be accessed later by using dot (.) operator. Objects are the basic runtime entities of object-oriented programming. It defines the instance of a class. Objects get their variables and functions from classes and the class we will be creating are the templates made to create the object. +Object-Oriented Terminologies + + class: Classes are a user-defined data type that is used to encapsulate data and associated functions together. It also helps in binding data together into a single unit. + Data Member: A variable or memory location name that holds value to does a specific task within a program. + Member Function: They are the functions; usually a block of a code snippet that is written to re-use it. + Instance variable: A variable that is defined inside a method of a class. + Function Overloading: This technique is used to assign multiple tasks to a single function & the tasks are performed based on the number of argument or the type of argument the function has. + Operator Overloading: It is the technique of assigning multiple function/tasks to a particular operator. + Inheritance: It is the process of acquiring the properties of one class to another i.e. one class can acquire the properties of another. + Instantiation: It is the technique of creating an instance of a class. + +Program For Class In Python +Example: + +#!/usr/bin/python + +class karl : + varabl = 'Hello' + +def function(self) : + print "This is a message Hello" + +Another program to explain functions inside a class: +Example: + +#!/usr/bin/python + +class karl(object) : + def __init__(self, x, y): + self.x = x + self.y = y + + def sample(self) : + print " This is just a sample code" + +In the above code, we created a class name karl using ‘class’ keyword. And two functions are used namely __init__() (for setting the instance variable) & sample(). Classes are used instead of modules because programmers can take this class ‘karl’ & use it or modify it as many times as we want & each one won’t interfere with each other. Importing a module brings the entire program into use. +Creating Objects (Instance Of A Class) + +Let’s see an example to show how to create an object: +Example: + +#!/usr/bin/pythonclass student: +class student: + def __init__(self, roll, name): + self.r = roll + self.n = name + print ((self.n)) + +#... +stud1 = student(1, "Alex") +stud2 = student(2, "Karlos") + +print (("Data successfully stored in variables")) + +Output: + +Alex +Karlos +Data successfully stored in variables + +Accessing Object Variables + +We can access the object’s variable using dot (.) operator. +The syntax is: + +my object_name.variable_name + +Example: + +print object.varabl + +Accessing Attributes + +Object attributes can also be accessed by using dot operator. +Example: + +stud1.display() +stud2.display() + +print " total number of students are: %d" % student.i + +Use Of Pre-defined Functions + +Instead of using general statements to access attributes, programmers can use the following functions: + + getattr( obj, name [,default] ) : used to access object’s attribute. + + hasattr( object, name): used for checking whether the attribute exists or not. + + setattr( obj, name, value ) : set or create an attribute, if doesn’t exist. + + delattr( obj, name ) : used to delete an attribute. + +Built-In Class Attributes + +All the Python built-in class attributes can be accessed using dot (.) operator like other attributes. + +The built-in class attributes are: + + __dict__: This attribute is a dictionary that contains class’s-namespace. + __doc__: Used for class documentation string. + __name__: used as class-name. + __module__: used to define module name for the class in which it is defined. In interactive mode it is __main__. + __bases__: An empty tuple containing the base-class. diff --git a/code1/python-/abc.txt b/code1/python-/abc.txt new file mode 100644 index 0000000..afbf254 --- /dev/null +++ b/code1/python-/abc.txt @@ -0,0 +1,5 @@ +aaa +aab +aac +aad +aae diff --git a/code1/python-/letters-game.py b/code1/python-/letters-game.py new file mode 100644 index 0000000..2f3fca6 --- /dev/null +++ b/code1/python-/letters-game.py @@ -0,0 +1,17 @@ +# LETTERGAME +# Pick a random word from a list +# Let's us guess a letter of the word and show the position of the letter in the word + +import random + +word_list = ["carrot", "apple", "cucumber"] + +# main function +def lettergame(): + #takes a random word from the list + word = random.choice(word_list) + print(word) + print(len(word)) + +# Run lettergame +lettergame() diff --git a/code1/python-/numbers-game.py b/code1/python-/numbers-game.py new file mode 100644 index 0000000..5524491 --- /dev/null +++ b/code1/python-/numbers-game.py @@ -0,0 +1,32 @@ +# NUMBERS GAME +# Author: Bram Pauwelyn + +from random import randint + +# Generate a random number +random_number = randint(0, 100) +i = 0 + +# Asks the user to guess the number +print("Guess a number between 0 and 100") + + +# Checks if number is correct +while i < 10: + guess = input('> ') + # IF number is correct, congratulations + if guess == random_number: + print("congratulations") + break + + # Else number is smaller + elif guess < random_number: + print("You have to guess higher") + i += 1 + continue + + # Else + else: + print("You have to guess lower") + continue + i += 1 diff --git a/code1/python-/read.py b/code1/python-/read.py new file mode 100644 index 0000000..b96729d --- /dev/null +++ b/code1/python-/read.py @@ -0,0 +1,11 @@ +import sys + +list = ['item1', 'item2', 'item3'] + +def show(): + with open("test.txt") as file: + for line in file: + print(line) + + +show() diff --git a/code1/python-/request.py b/code1/python-/request.py new file mode 100644 index 0000000..7604b7d --- /dev/null +++ b/code1/python-/request.py @@ -0,0 +1,14 @@ +import sys +import requests + +#open file +with open("abc.txt") as file: + #loop through file and print out json response + for line in file: + r = requests.get(line) + print(line) + if r.ok: + print(r.json()) + else: + print(r.ok) + print('niet gelukt') diff --git a/code1/python-/shopping-list.py b/code1/python-/shopping-list.py new file mode 100644 index 0000000..cc2668c --- /dev/null +++ b/code1/python-/shopping-list.py @@ -0,0 +1,33 @@ +#REMINDERS APP +shopping_list = [] + +# Print shopping_list +def printer(): + print("Here's your list") + print('\n') + for item in shopping_list: + print(item) + + +#Instructions +print("Add Items to your shopping list") +print("Type DONE if you're ready adding items") +print("TYPE HELP to show the different commands") + +#Input +while True: + new_item = raw_input("> ") + + if new_item == 'HELP': + print("LIST OF COMMANDS") + print("TYPE DONE if you're ready adding items") + print("TYPE HELP to show the different commands") + print("TYPE SHOW to show the items in your shopping list") + + if new_item == 'SHOW': + printer() + + if new_item == 'DONE': + break + + shopping_list.append(new_item) diff --git a/code1/python-/write.py b/code1/python-/write.py new file mode 100644 index 0000000..fd62ff8 --- /dev/null +++ b/code1/python-/write.py @@ -0,0 +1,11 @@ +import sys + +list = ['item1', 'item2', 'item3'] + +def remember(): + #open file + file = open("test.txt", "a") + for item in list: + file.write(item+"\n") + +remember() diff --git a/code1/python-oops/.gitignore b/code1/python-oops/.gitignore new file mode 100644 index 0000000..8b4c4a9 --- /dev/null +++ b/code1/python-oops/.gitignore @@ -0,0 +1,63 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints +.idea/ \ No newline at end of file diff --git a/code1/python-oops/README.md b/code1/python-oops/README.md new file mode 100644 index 0000000..527e7b6 --- /dev/null +++ b/code1/python-oops/README.md @@ -0,0 +1,7 @@ +# python-oops +Python Objects Oriented Concepts Explained with Examples + +* [Encapsulation](/encapsulation.md) +* [Method Overloading](/method_overloading.md) +* [inheritance](/inheritance.md) +* [Multiple inheritance (MRO)](/multiple_inheritance.md) \ No newline at end of file diff --git a/code1/python-oops/code/bst.py b/code1/python-oops/code/bst.py new file mode 100644 index 0000000..e233301 --- /dev/null +++ b/code1/python-oops/code/bst.py @@ -0,0 +1,135 @@ +# Binary Search Tree in Python + +class Node: + def __init__(self, val): + self.value = val + self.leftChild = None + self.rightChild = None + + def __str__(self): + return str(self.value) + '-'+str(self.leftChild) +'-'+ str(self.rightChild) + + + def insert(self, data): + if self.value == data: + return False + + elif self.value > data: + if self.leftChild: + return self.leftChild.insert(data) + else: + self.leftChild = Node(data) + return True + + else: + if self.rightChild: + return self.rightChild.insert(data) + else: + self.rightChild = Node(data) + return True + + def preorder(self): + if self: + print (str(self.value)) + if self.leftChild: + self.leftChild.preorder() + if self.rightChild: + self.rightChild.preorder() + + + def postorder(self): + if self: + if self.leftChild: + self.leftChild.postorder() + if self.rightChild: + self.rightChild.postorder() + print (str(self.value)) + + def inorder(self): + if self: + if self.leftChild: + self.leftChild.inorder() + print (str(self.value)) + if self.rightChild: + self.rightChild.inorder() + + def lboundary(self): + if self: + if self.leftChild: + print(self.value) + self.leftChild.lboundary() + else: + # dont print if leaf + if self.leftChild and self.rightChild: + print(self.value) + + def leafs(self): + if self: + if self.leftChild: + self.leftChild.leafs() + if self.leftChild is None and self.rightChild is None : + print(self.value) + if self.rightChild: + self.rightChild.leafs() + + def rboundary(self): + if self: + if self.rightChild: + #print(self.value) + self.rightChild.rboundary() + else: + # dont print if leaf + if self.leftChild and self.rightChild: + print(self.value) + + +class Tree: + def __init__(self): + self.root = None + + def insert(self, data): + if self.root: + return self.root.insert(data) + else: + self.root = Node(data) + return True + + def preorder(self): + if self.root is not None: + print("PreOrder") + self.root.preorder() + + def postorder(self): + if self.root is not None: + print("PostOrder") + self.root.postorder() + + def inorder(self): + if self.root is not None: + print("InOrder") + self.root.inorder() + + def boundary(self): + if self.root is not None: + print("Boundary") + self.root.lboundary() + self.root.leafs() + self.root.rboundary() + # self.root.lefts() + # # print all lefts + # self.root.leafs() + # self.root.rights() + + + +def main(): + bst = Tree() + for i in [20,8,22,4,12,10,14,25, 27]: + bst.insert(i) + #bst.insert(7) + # bst.preorder() + # bst.postorder() + # bst.inorder() + bst.boundary() + +main() \ No newline at end of file diff --git a/code1/python-oops/code/bst1.py b/code1/python-oops/code/bst1.py new file mode 100644 index 0000000..7e188ec --- /dev/null +++ b/code1/python-oops/code/bst1.py @@ -0,0 +1,232 @@ +# Binary Search Tree in Python + +class Node: + def __init__(self, val): + self.value = val + self.leftChild = None + self.rightChild = None + + def insert(self, data): + if self.value == data: + return False + + elif self.value > data: + if self.leftChild: + return self.leftChild.insert(data) + else: + self.leftChild = Node(data) + return True + + else: + if self.rightChild: + return self.rightChild.insert(data) + else: + self.rightChild = Node(data) + return True + + # def find(self, data): + # if(self.value == data): + # return True + # elif self.value > data: + # if self.leftChild: + # return self.leftChild.find(data) + # else: + # return False + # else: + # if self.rightChild: + # return self.rightChild.find(data) + # else: + # return False + + # def getSize(self): + # if self.leftChild and self.rightChild: + # return 1 + self.leftChild.getSize() + self.rightChild.getSize() + # elif self.leftChild: + # return 1 + self.leftChild.getSize() + # elif self.rightChild: + # return 1 + self.rightChild.getSize() + # else: + # return 1 + + # def getHeight(self): + # if self.leftChild and self.rightChild: + # return 1 + max(self.leftChild.getHeight(), self.rightChild.getHeight()) + # elif self.leftChild: + # return 1 + self.leftChild.getHeight() + # elif self.rightChild: + # return 1 + self.rightChild.getHeight() + # else: + # return 1 + + def preorder(self): + if self: + print (str(self.value)) + if self.leftChild: + self.leftChild.preorder() + if self.rightChild: + self.rightChild.preorder() + + def postorder(self): + if self: + if self.leftChild: + self.leftChild.postorder() + if self.rightChild: + self.rightChild.postorder() + print (str(self.value)) + + def inorder(self): + if self: + if self.leftChild: + self.leftChild.inorder() + print (str(self.value)) + if self.rightChild: + self.rightChild.inorder() + +class Tree: + def __init__(self): + self.root = None + + def insert(self, data): + if self.root: + return self.root.insert(data) + else: + self.root = Node(data) + return True + + # def find(self, data): + # if self.root: + # return self.root.find(data) + # else: + # return False + + # def getHeight(self): + # if self.root: + # return self.root.getHeight() + # else: + # return 0 + + # def getSize(self): + # if self.root: + # return self.root.getSize() + # else: + # return 0 + + # def remove(self, data): + # # empty tree + # if self.root is None: + # return False + + # # data is in root node + # elif self.root.value == data: + # if self.root.leftChild is None and self.root.rightChild is None: + # self.root = None + # elif self.root.leftChild and self.root.rightChild is None: + # self.root = self.root.leftChild + # elif self.root.leftChild is None and self.root.rightChild: + # self.root = self.root.rightChild + # elif self.root.leftChild and self.root.rightChild: + # delNodeParent = self.root + # delNode = self.root.rightChild + # while delNode.leftChild: + # delNodeParent = delNode + # delNode = delNode.leftChild + + # self.root.value = delNode.value + # if delNode.rightChild: + # if delNodeParent.value > delNode.value: + # delNodeParent.leftChild = delNode.rightChild + # elif delNodeParent.value < delNode.value: + # delNodeParent.rightChild = delNode.rightChild + # else: + # if delNode.value < delNodeParent.value: + # delNodeParent.leftChild = None + # else: + # delNodeParent.rightChild = None + + # return True + + # parent = None + # node = self.root + + # # find node to remove + # while node and node.value != data: + # parent = node + # if data < node.value: + # node = node.leftChild + # elif data > node.value: + # node = node.rightChild + + # # case 1: data not found + # if node is None or node.value != data: + # return False + + # # case 2: remove-node has no children + # elif node.leftChild is None and node.rightChild is None: + # if data < parent.value: + # parent.leftChild = None + # else: + # parent.rightChild = None + # return True + + # # case 3: remove-node has left child only + # elif node.leftChild and node.rightChild is None: + # if data < parent.value: + # parent.leftChild = node.leftChild + # else: + # parent.rightChild = node.leftChild + # return True + + # # case 4: remove-node has right child only + # elif node.leftChild is None and node.rightChild: + # if data < parent.value: + # parent.leftChild = node.rightChild + # else: + # parent.rightChild = node.rightChild + # return True + + # # case 5: remove-node has left and right children + # else: + # delNodeParent = node + # delNode = node.rightChild + # while delNode.leftChild: + # delNodeParent = delNode + # delNode = delNode.leftChild + + # node.value = delNode.value + # if delNode.rightChild: + # if delNodeParent.value > delNode.value: + # delNodeParent.leftChild = delNode.rightChild + # elif delNodeParent.value < delNode.value: + # delNodeParent.rightChild = delNode.rightChild + # else: + # if delNode.value < delNodeParent.value: + # delNodeParent.leftChild = None + # else: + # delNodeParent.rightChild = None + + def preorder(self): + if self.root is not None: + print("PreOrder") + self.root.preorder() + + def postorder(self): + if self.root is not None: + print("PostOrder") + self.root.postorder() + + def inorder(self): + if self.root is not None: + print("InOrder") + self.root.inorder() + +def main(): + bst = Tree() + print(bst.insert(10)) + print(bst.insert(5)) + bst.insert(2) + bst.insert(7) + # bst.preorder() + # bst.postorder() + bst.inorder() + +main() \ No newline at end of file diff --git a/code1/python-oops/code/test.py b/code1/python-oops/code/test.py new file mode 100644 index 0000000..e3e8a2b --- /dev/null +++ b/code1/python-oops/code/test.py @@ -0,0 +1,109 @@ +# lis = [10, 12, 10, 15, -1, 7, 6,5, 4, 2, 1, 1, 1] + +# target = 11 + +# res = [] +# for key, value in enumerate(lis): +# cmp_lis = lis[key+1:] +# for j in cmp_lis: +# if value + j == target: +# res.append([value, j]) + +# print(res) +# print(len(res)) +# #print(value, cmp_lis) + +class Tree: + def __init__(self): + self.root = None + + def insert(self, val): + if self.root: + return self.root.insert(val) + else: + self.root = Node(val) + return True + + def preorder(self): + if self.root is not None: + print("Inorder") + self.root.preorder() + + def postorder(self): + if self.root is not None: + print("postorder") + self.root.postorder() + + def inorder(self): + if self.root is not None: + print("inorder") + self.root.inorder() + + +class Node: + def __init__(self, val): + self.value = val + self.left_child = None + self.right_child = None + + + def insert(self, data): + if self.value == data: + return False + + elif self.value > data: + if self.left_child: + self.left_child.insert(data) + return True + else: + self.left_child = Node(data) + return True + else: + if self.right_child: + self.right_child.insert(data) + return True + else: + self.right_child = Node(data) + return True + + def preorder(self): + if self: + print(str(self.value)) + if self.left_child: + self.left_child.preorder() + + if self.right_child: + self.right_child.preorder() + + def postorder(self): + if self: + if self.left_child: + self.left_child.postorder() + + if self.right_child: + self.right_child.postorder() + + print(str(self.value)) + + def inorder(self): + if self: + if self.left_child: + self.left_child.inorder() + + print(str(self.value)) + + if self.right_child: + self.right_child.inorder() + + + + +# bst = Tree() +# for i in [1,2,3,4,5]: +# bst.insert(i) + +# bst.preorder() +st = "abhishek" +print(st.reverse()) + + diff --git a/code1/python-oops/encapsulation.md b/code1/python-oops/encapsulation.md new file mode 100644 index 0000000..d2210b3 --- /dev/null +++ b/code1/python-oops/encapsulation.md @@ -0,0 +1,88 @@ +> Encapsulation +> In an object oriented python program, you can restrict access to methods and +> variables. This can prevent the data from being modified by accident and is +> known as encapsulation. Let's start with an example. + +```python +class Mobile: + + def __init__(self): + self.__update_software() + + def switch_on(self): + print 'driving' + + def __update_software(self): + print 'updating software' + +smobile = Mobile() +smobile.switch_on() +#smobile.__updateSoftware() not accesible from object. + +print('-'*100) +``` +> Explanation +> The private method __update_software() can only be called within the class +> itself. It can never be called from outside the class. + + +> Private variables +> Variables can be private which can be useful on many occasions. +> Objects can hold crucial data for your application and you do not want that +> data to be changeable from anywhere in the code. An example: + +```python +class Mobile: + + __os = 'andriod' + __name = "" + + def __init__(self): + self.__os = 'andriod' + self.__name = "Supercar" + + def switch_on(self): + print 'Mobile Os is ' + str(self.__os) + +smobile = Mobile() +smobile.switch_on() +smobile.__os = 'ios' > will not change variable because its private +smobile.switch_on() +print('-'*100) +``` +> If you want to change the value of a private variable, a setter method is used. +> This is simply a method that sets the value of a private variable. + +```python +class Mobile: + + __os = 'andriod' + __name = "" + + def __init__(self): + self.__os = 'andriod' + self.__name = "Supercar" + + def switch_on(self): + print 'Mobile Os is ' + str(self.__os) + + def set_os(self, os): + self.__os = os + +smobile = Mobile() +smobile.switch_on() +smobile.set_os('ios') +smobile.switch_on() + +print('-'*100) +``` + +> Summary +> To summarize, in Python there are: + +Type | Description +--- | --- +public methods | accessible from anywhere +private methods | accessible only in their own class. starts with two underscores +public variables | accessible from anywhere +private variables | accesible only in their own class or by a method if defined. starts with two underscores diff --git a/code1/python-oops/images/C3_linearization_example.svg.png b/code1/python-oops/images/C3_linearization_example.svg.png new file mode 100644 index 0000000..459a4d8 Binary files /dev/null and b/code1/python-oops/images/C3_linearization_example.svg.png differ diff --git a/code1/python-oops/inheritance.md b/code1/python-oops/inheritance.md new file mode 100644 index 0000000..f46f8c4 --- /dev/null +++ b/code1/python-oops/inheritance.md @@ -0,0 +1,76 @@ +# inheritance + > Classes can inherit functionality from other classes, let’s take a + > look at how that works. We start with a basic class: + + ```python +class User: + name = "" + + def __init__(self, name): + self.name = name + + def printName(self): + print "Name = " + self.name + +brian = User("brian") +brian.printName() + ``` + + > This creates one instance called brian which outputs its given name. + > Add another class called Programmer. + + +```python +class Programmer(User): + + def __init__(self, name): + self.name = name + def doPython(self): + print "Programming Python" +``` + + + +> This looks very much like a standard class except than User is given in the +> parameters. This means all functionality of the class User is accesible in the +> Programmer class. + +### Full example of Python inheritance: + +```python +class User: + name = "" + + def __init__(self, name): + self.name = name + + def printName(self): + print "Name = " + self.name + +class Programmer(User): + def __init__(self, name): + self.name = name + + def doPython(self): + print "Programming Python" + +brian = User("brian") +brian.printName() + +diana = Programmer("Diana") +diana.printName() +diana.doPython() +``` + + +> The output: +```python +Name = brian +Name = Diana +Programming Python +``` + +> Brian is an instance of User and can only access the method printName. +> Diana is an instance of Programmer, a class with inheritance from User, +> and can access both the methods in Programmer and User. + diff --git a/code1/python-oops/method_overloading.md b/code1/python-oops/method_overloading.md new file mode 100644 index 0000000..7975150 --- /dev/null +++ b/code1/python-oops/method_overloading.md @@ -0,0 +1,43 @@ + +> In Python you can define a method in such a way that there are multiple ways to call +> it. This is known as method overloading. We do that by setting default values of +> variables. Let us do an example: + +```python +class Mobile: + + def get_brand(self, brand=None): + + if brand is not None: + print brand + else: + print 'Generic ' + +# Create instance +obj = Mobile() + +# Call the method +obj.get_brand() + +# Call the method with a parameter +obj.get_brand('Samsung') + +``` + +#### Output: + +> Hello + +> Hello Samsung + +> To clarify method overloading, we can now call the method sayHello() in two ways: + +```python +obj.get_brand() +obj.get_brand('Samsung') +``` + +> We created a method that can be called with fewer arguments than it is defined +> to allow. We are not limited to two variables, your method could have more +> variables which are optional. + diff --git a/code1/python-oops/multiple_inheritance.md b/code1/python-oops/multiple_inheritance.md new file mode 100644 index 0000000..43e14a9 --- /dev/null +++ b/code1/python-oops/multiple_inheritance.md @@ -0,0 +1,121 @@ +# Multiple Inheritance +> In languages that use multiple inheritance, the order in which base classes are +> searched when looking for a method is often called the Method Resolution Order, +> or MRO. + + +* [Explanation by Guido](http://python-history.blogspot.com/2010/06/method-resolution-order.html) + +> lets take an example + +```python + +class O(): pass + +class A(O): pass + +class B(O): pass + +class C(O): pass + +class D(O): pass + +class E(O): pass + +class K1(A, B, C): pass + +class K2(D, B, E): pass + +class K3(D, A): pass + +class Z(K1, K2, K3): pass + +``` + + +Image +![alt text][C3_linearization_example] + +> the linearization of Z is computed as + +``` +L(O) := [O] // the linearization of O is trivially the singleton list [O], because O has no parents + +L(A) := [A] + merge(L(O), [O]) // the linearization of A is A plus the merge of its parents' linearizations with the list of parents... + = [A] + merge([O], [O]) + = [A, O] // ...which simply prepends A to its single parent's linearization + +L(B) := [B, O] // linearizations of B, C, D and E are computed similar to that of A +L(C) := [C, O] +L(D) := [D, O] +L(E) := [E, O] + +L(K1) := [K1] + merge(L(A), L(B), L(C), [A, B, C]) // first, find the linearizations of K1's parents, L(A), L(B), and L(C), and merge them with the parent list [A, B, C] + = [K1] + merge([A, O], [B, O], [C, O], [A, B, C]) // class A is a good candidate for the first merge step, because it only appears as the head of the first and last lists + = [K1, A] + merge([O], [B, O], [C, O], [B, C]) // class O is not a good candidate for the next merge step, because it also appears in the tails of list 2 and 3, but... + = [K1, A, B] + merge([O], [O], [C, O], [C]) // ...class B qualified, and so does class C; class O still appears in the tail of list 3 + = [K1, A, B, C] + merge([O], [O], [O]) // finally, class O is a valid candidate, which also exhausts all remaining lists + = [K1, A, B, C, O] + +L(K2) := [K2] + merge(L(D), L(B), L(E), [D, B, E]) + = [K2] + merge([D, O], [B, O], [E, O], [D, B, E]) // select D + = [K2, D] + merge([O], [B, O], [E, O], [B, E]) // fail O, select B + = [K2, D, B] + merge([O], [O], [E, O], [E]) // fail O, select E + = [K2, D, B, E] + merge([O], [O], [O]) // select O + = [K2, D, B, E, O] + +L(K3) := [K3] + merge(L(D), L(A), [D, A]) + = [K3] + merge([D, O], [A, O], [D, A]) // select D + = [K3, D] + merge([O], [A, O], [A]) // fail O, select A + = [K3, D, A] + merge([O], [O]) // select O + = [K3, D, A, O] + +L(Z) := [Z] + merge(L(K1), L(K2), L(K3), [K1, K2, K3]) + = [Z] + merge([K1, A, B, C, O], [K2, D, B, E, O], [K3, D, A, O], [K1, K2, K3]) // select K1 + = [Z, K1] + merge([A, B, C, O], [K2, D, B, E, O], [K3, D, A, O], [K2, K3]) // fail A, select K2 + = [Z, K1, K2] + merge([A, B, C, O], [D, B, E, O], [K3, D, A, O], [K3]) // fail A, fail D, select K3 + = [Z, K1, K2, K3] + merge([A, B, C, O], [D, B, E, O], [D, A, O]) // fail A, select D + = [Z, K1, K2, K3, D] + merge([A, B, C, O], [B, E, O], [A, O]) // select A + = [Z, K1, K2, K3, D, A] + merge([B, C, O], [B, E, O], [O]) // select B + = [Z, K1, K2, K3, D, A, B] + merge([C, O], [E, O], [O]) // select C + = [Z, K1, K2, K3, D, A, B, C] + merge([O], [E, O], [O]) // fail O, select E + = [Z, K1, K2, K3, D, A, B, C, E] + merge([O], [O], [O]) // select O + = [Z, K1, K2, K3, D, A, B, C, E, O] // done +``` + +## Example demonstrated in Python + +```python +class A(object): pass + +class B(object): pass + +class C(object): pass + +class D(object): pass + +class E(object): pass + +class K1(A, B, C): pass + +class K2(D, B, E): pass + +class K3(D, A): pass + +class Z(K1, K2, K3): pass + +Z.mro() +``` +> output +```python +[, , , , , , , , , ] + +[Z, K1, K2, K3, D, A, B, C, E, ] + + +``` +[References][wiki_link] + + +[C3_linearization_example]: images/C3_linearization_example.svg.png "Logo Title Text 2" +[wiki_link]: https://en.wikipedia.org/wiki/C3_linearization#example \ No newline at end of file diff --git a/code1/samples/Bike.py b/code1/samples/Bike.py new file mode 100644 index 0000000..3710618 --- /dev/null +++ b/code1/samples/Bike.py @@ -0,0 +1,32 @@ +class Bike(object): + def __init__(self, price, max_speed): + self.price = price + self.max_speed = max_speed + self.miles = 0 + + def displayInfo(self): + print 'Price: $' + str(self.price) + print 'Max Speed: ' + self.max_speed + print 'Miles: ' + str(self.miles) + + def ride(self): + print 'Riding' + self.miles += 10 + return self + + def reverse(self): + print 'Reversing' + self.miles -= 5 + if self.miles < 0: + self.miles = 0 + return self + +bike1 = Bike(200, '25mph') +bike2 = Bike(300, '40mph') +bike3 = Bike(500, '50mph') + +bike1.ride().ride().ride().reverse().displayInfo() + +bike2.ride().ride().reverse().reverse().displayInfo() + +bike3.reverse().reverse().reverse().displayInfo() \ No newline at end of file diff --git a/code1/samples/CallCenter.py b/code1/samples/CallCenter.py new file mode 100644 index 0000000..4b15d58 --- /dev/null +++ b/code1/samples/CallCenter.py @@ -0,0 +1,56 @@ +class Call(object): + def __init__(self, id, name, number, time, reason): + self.id = id + self.name = name + self.number = number + self.time = time + self.reason = reason + + def display(self): + print "Call ID: {}\nCaller name: {}\nCaller phone number: {}\nTime of call: {}\nReason for call: {}".format(self.id, self.name, self.number, self.time, self.reason) + + +class CallCenter(object): + def __init__(self, calls=[]): + self.calls = calls + self.queue_size = len(self.calls) + + def add(self, call): + self.calls.append(call) + return self + + def remove(self): + self.calls.pop(0) + return self + + def remove_number(self, number): + for i, call in enumerate(self.calls): + if call.number == number: + self.calls.pop(i) + break + return self + + def sort_calls(self): + def numeric_compare(x, y): + if x - y > 0: + return 1 + elif x - y < 0: + return -1 + else: + return 0 + self.calls.sort(cmp=numeric_compare, key=lambda call: call.time) + return self + + def info(self): + for call in self.calls: + print "Name: {}\nPhone number: {}".format(call.name, call.number) + print "Length of queue: {}".format(len(self.calls)) + + +call1 = Call(1, 'Charlie', 5555555, 9.5, 'complaint') +call2 = Call(2, 'Emily', 7777777, 10, 'compliment') +call3 = Call(3, 'Bobby', 7777777, 9, 'compliment') + +cc = CallCenter() +cc.add(call1).add(call2).add(call3) +cc.sort_calls().info() \ No newline at end of file diff --git a/code1/samples/Car.py b/code1/samples/Car.py new file mode 100644 index 0000000..31d8b9d --- /dev/null +++ b/code1/samples/Car.py @@ -0,0 +1,25 @@ +class Car(object): + def __init__(self, price, speed, fuel, mileage): + self.price = price + self.speed = speed + self.fuel = fuel + self.mileage = mileage + if price > 10000: + self.tax = 0.15 + else: + self.tax = 0.12 + self.display_all() + + def display_all(self): + print 'Price:', self.price + print 'Speed:', self.speed + print 'Fuel:', self.fuel + print 'Mileage:', self.mileage + print 'Tax:', self.tax + +car1 = Car(2000, '35mph', 'Full', '15mpg') +car2 = Car(2000, '5mph', 'Not Full', '105mpg') +car3 = Car(2000, '15mph', 'Kind of Full', '95mpg') +car4 = Car(2000, '25mph', 'Full', '25mpg') +car5 = Car(2000, '45mph', 'Empty', '25mpg') +car6 = Car(20000000, '35mph', 'Empty', '15mpg') \ No newline at end of file diff --git a/code1/samples/Hospital.py b/code1/samples/Hospital.py new file mode 100644 index 0000000..c30ebb2 --- /dev/null +++ b/code1/samples/Hospital.py @@ -0,0 +1,61 @@ +class Patient(object): + def __init__(self, id_number, name, allergies=[], bed_number='none'): + self.id_number = id_number + self.name = name + self.allergies = allergies + self.bed_number = bed_number + + +class Hospital(object): + def __init__(self, name, capacity, patients=[]): + self.name = name + self.capacity = capacity + self.patients = patients + self.open_bed_numbers = range(self.capacity) + for i in range(len(self.patients)): + self.patients[i].bed_number = i + self.open_bed_numbers.pop(0) + + def admit(self, patient): + if len(self.patients) >= self.capacity: + print 'Sorry, the hospital is full! :(' + else: + patient.bed_number = self.open_bed_numbers.pop(0) + self.patients.append(patient) + print 'Patient ID #{} was successfully admitted'.format(patient.id_number) + return self + + def discharge(self, patient_id_number): + for i, patient in enumerate(self.patients): + if patient.id_number == patient_id_number: + self.open_bed_numbers.append(patient.bed_number) + patient.bed_number = 'none' + self.patients.pop(i) + print 'Patient ID #{} was discharged'.format(patient.id_number) + break + return self + + +p0 = Patient(0, 'charlie') +p1 = Patient(1, 'emily') +p2 = Patient(2, 'bobby') +p3 = Patient(3, 'anna') +patients = [p0, p1, p2] + +hospital = Hospital('Awesome Hospital', 6, patients) +print hospital.patients[0].name +print hospital.patients[0].bed_number +print 'open hospital beds:', hospital.open_bed_numbers + +print '\n' + +hospital.admit(p3) +print 'open hospital beds:', hospital.open_bed_numbers +print 'admitted patient name: ', hospital.patients[3].name +print 'admitted patient bed number: ', hospital.patients[3].bed_number + +print '\n' + +hospital.discharge(2) +print '# of patients:', len(hospital.patients) +print 'open hospital beds:', hospital.open_bed_numbers \ No newline at end of file diff --git a/code1/samples/MathDojo.py b/code1/samples/MathDojo.py new file mode 100644 index 0000000..cfca8ea --- /dev/null +++ b/code1/samples/MathDojo.py @@ -0,0 +1,26 @@ +class MathDojo(object): + def __init__(self): + self.result = 0 + + def add(self, *args): + for item in args: + if type(item) == list or type(item) == tuple: + for i in range(0, len(item)): + self.result += item[i] + else: + self.result += item + return self + + def subtract(self, *args): + for item in args: + if type(item) == list or type(item) == tuple: + for i in range(0, len(item)): + self.result -= item[i] + else: + self.result -= item + return self + + +print MathDojo().add(2).add(2, 5).subtract(3, 2).result + +print MathDojo().add([1],3,4).add([3, 5, 7, 8], [2, 4.3, 1.25]).subtract(2, [2,3], [1.1, 2.3]).result \ No newline at end of file diff --git a/code1/samples/Product.py b/code1/samples/Product.py new file mode 100644 index 0000000..2b5b52e --- /dev/null +++ b/code1/samples/Product.py @@ -0,0 +1,35 @@ +class Product(object): + def __init__(self, price, name, weight, brand, cost): + self.price = price + self.name = name + self.weight = weight + self.brand = brand + self.cost = cost + self.status = 'for sale' + + def sell(self): + self.status = 'sold' + return self + + def add_tax(self, tax): + return self.price * (1 + tax) + + def return_product(self, reason): + if reason == 'defective': + self.status = 'defective' + self.price = 0 + elif reason == 'in box like new': + self.status = 'for sale' + elif reason == 'opened': + self.status = 'used' + self.price *= 0.8 + return self + + def display_info(self) + print 'Price: $' + str(self.price) + print 'Name: ' + self.name + print 'Weight: ' + str(self.weight) + print 'Brand: ' + self.brand + print 'Cost: $' + str(self.cost) + print 'Status: ' + self.status + return self \ No newline at end of file diff --git a/code1/samples/Store.py b/code1/samples/Store.py new file mode 100644 index 0000000..f948c9e --- /dev/null +++ b/code1/samples/Store.py @@ -0,0 +1,54 @@ +class Product(object): + def __init__(self, price, name, weight, brand, cost): + self.price = price + self.name = name + self.weight = weight + self.brand = brand + self.cost = cost + self.status = 'for sale' + + def sell(self): + self.status = 'sold' + return self + + def add_tax(self, tax): + return self.price * (1 + tax) + + def return_product(self, reason): + if reason == 'defective': + self.status = 'defective' + self.price = 0 + elif reason == 'in box like new': + self.status = 'for sale' + elif reason == 'opened': + self.status = 'used' + self.price *= 0.8 + return self + + def display_info(self) + print 'Price: $' + str(self.price) + print 'Name: ' + self.name + print 'Weight: ' + str(self.weight) + print 'Brand: ' + self.brand + print 'Cost: $' + str(self.cost) + print 'Status: ' + self.status + return self + +class Store(object): + def __init__(self, products, address, owner): + self.products = products + self.address = address + self.owner = owner + + def add_product(self, product): + self.products.append(product) + return self + + def remove_product(self, product): + self.products.remove(product) + return self + + def inventory(self): + for product in self.products: + display_info(product) + return self \ No newline at end of file diff --git a/code1/samples/Underscore.py b/code1/samples/Underscore.py new file mode 100644 index 0000000..78b3119 --- /dev/null +++ b/code1/samples/Underscore.py @@ -0,0 +1,45 @@ +class Underscore(object): + def map(self, ls, func): + output = [] + for item in ls: + output.append(func(item)) + return output + + def reduce(self, ls, func, memo): + output = memo + for item in ls: + output = func(output, item) + return output + + def find(self, ls, func): + for item in ls: + if func(item): + return item + return None + + def filter(self, ls, func): + output = [] + for item in ls: + if func(item): + output.append(item) + return output + + def reject(self, ls, func): + output = [] + for item in ls: + if not func(item): + output.append(item) + return output + + +_ = Underscore() + +print _.filter([1, 2, 3, 4, 5, 6], lambda x: x % 2 == 0) + +print _.map([1,2,3,4,5,6], lambda x: x*5) + +print _.find([1,2,3,4,5,6], lambda x: x%2 == 0) + +print _.reject([1, 2, 3, 4, 5, 6], lambda x: x % 2 == 0) + +print _.reduce([1,2,3], lambda memo, x: memo + x, 0) \ No newline at end of file diff --git a/code1/samples/bank_inheritance.py b/code1/samples/bank_inheritance.py new file mode 100644 index 0000000..61ae3b0 --- /dev/null +++ b/code1/samples/bank_inheritance.py @@ -0,0 +1,73 @@ +class BankAccount: + def __init__(self, interest_rate=.02): + self.balance = 0 + self.interest_rate = interest_rate + + def deposit(self, amount): + if amount < 0: + return False + self.balance += amount + return self.balance + + def withdraw(self, amount): + if amount < 0: + return False + self.balance -= amount + return amount + + def accumulate_interest(self): + self.balance += self.balance * self.interest_rate + +class ChildrensAccount(BankAccount): + def __init__(self): + self.interest_rate = 0 + super().__init__(self.interest_rate) + + def accumulate_interest(self): + self.balance += 10 + +class OverdraftAccount(BankAccount): + def __init__(self, interest_rate=.02, overdraft_penalty=40): + super().__init__(interest_rate) + self.overdraft_penalty = overdraft_penalty + + def withdraw(self, amount): + if amount > self.balance: + self.balance -= self.overdraft_penalty + return False + + # use the inherited withdraw method if the user + # has enough money. + return super().withdraw(amount) + + def accumulate_interest(self): + if (self.balance <= 0): + return False + + return super().accumulate_interest() + +basic_account = BankAccount() +basic_account.deposit(600) +print("Basic account has ${}".format(basic_account.balance)) +basic_account.withdraw(17) +print("Basic account has ${}".format(basic_account.balance)) +basic_account.accumulate_interest() +print("Basic account has ${}".format(basic_account.balance)) +print() + +childs_account = ChildrensAccount() +childs_account.deposit(34) +print("Child's account has ${}".format(childs_account.balance)) +childs_account.withdraw(17) +print("Child's account has ${}".format(childs_account.balance)) +childs_account.accumulate_interest() +print("Child's account has ${}".format(childs_account.balance)) +print() + +overdraft_account = OverdraftAccount() +overdraft_account.deposit(12) +print("Overdraft account has ${}".format(overdraft_account.balance)) +overdraft_account.withdraw(17) +print("Overdraft account has ${}".format(overdraft_account.balance)) +overdraft_account.accumulate_interest() +print("Overdraft account has ${}".format(overdraft_account.balance)) diff --git a/code1/samples/phone_inheritance.py b/code1/samples/phone_inheritance.py new file mode 100644 index 0000000..ba7b1ab --- /dev/null +++ b/code1/samples/phone_inheritance.py @@ -0,0 +1,79 @@ +class Phone: + def __init__(self, phone_number): + self.number = phone_number + + def call(self, other_number): + print("Calling {} from {}.".format(other_number, self.number)) + + def text(self, other_number, msg): + print("Sending text from {} to {}:".format(self.number, other_number)) + print(msg); + +class IPhone(Phone): + def __init__(self, phone_number): + super().__init__(phone_number) + self.fingerprint = None + + def set_fingerprint(self, fingerprint): + self.fingerprint = fingerprint + + def unlock(self, fingerprint=None): + if (self.fingerprint == None): + print("Phone unlocked because no fingerprint has not been set.") + + if (fingerprint == self.fingerprint): + print("Phone unlocked. Fingerprint matches.") + else: + print("Phone locked. Fingerprint doesn't match.") + +class Android(Phone): + def __init__(self, phone_number): + super().__init__(phone_number) + self.keyboard = "Default" + + def set_keyboard(self, keyboard): + self.keyboard = keyboard + +moms_number = "888-3434" +old_phone = Phone("555-1314") +iphone = IPhone("555-0001") +android = Android("555-1337") + +# All phones have the call method defined in Phone +old_phone.call(moms_number) +iphone.call(moms_number) +android.call(moms_number) +print() + +# All phones have the text method defined in Phone +old_phone.text(moms_number, "Hey Mom. I want a new phone.") +iphone.text(moms_number, "Thanks for the iPhone, Mom!") +android.text(moms_number, "Mom, I saved up and bought an Android.") +print() + +# made up strings representing "fingerprints" +my_fingerprint = "swirl whorl whorl" +other_fingerprint = "spiral whorl swirl" + +# the iphone has access to it's own unlock and set_fingerprint methods. +iphone.unlock() + +iphone.set_fingerprint(my_fingerprint) +iphone.unlock(other_fingerprint) +iphone.unlock(my_fingerprint) +print() + +# the Android starts with the default keyboard and users can change +# it to use a custom Swype keyboard +print("The Android phone is using the {} keyboard".format(android.keyboard)) +android.set_keyboard("Swype") +print("The Android phone is using the {} keyboard".format(android.keyboard)) + +# This code will break because sub-classes only have extra functionality +# defined in their own class. iPhones don't get Android stuff and vice versa. +# The iPhone doesn't have access to the set_keyboard method. +# The Android doesn't have access to the set_fingerprint or unlock methods. +# +# This code will crash the program if uncommented: +# iphone.set_keyboard("iphones can't change keyboards") +# android.set_fingerprint("androids don't have fingerprint unlock") diff --git a/config_parser b/config_parser new file mode 100644 index 0000000..c5e879b --- /dev/null +++ b/config_parser @@ -0,0 +1,17 @@ + +from configparser import ConfigParser + +parser = ConfigParser() +parser.read('database.config') + +print(parser.get('database_config', 'url')) + + +[database_config] +url = http://localhost:3306/mysql/ +username = root +; Consider hashing this password rather than +; keeping it as plain-text here +password = MY_PASSWORD + + diff --git a/decorator05.py b/decorator05.py new file mode 100644 index 0000000..4d13024 --- /dev/null +++ b/decorator05.py @@ -0,0 +1,20 @@ +def my_decorator(some_function): + + def wrapper(): + + print("Something is happening before some_function() is called.") + + some_function() + + print("Something is happening after some_function() is called.") + + return wrapper + + +def just_some_function(): + print("Wheee!") + + +just_some_function = my_decorator(just_some_function) + +just_some_function() diff --git a/decorator06.py b/decorator06.py new file mode 100644 index 0000000..ae13945 --- /dev/null +++ b/decorator06.py @@ -0,0 +1,24 @@ +def my_decorator(some_function): + + def wrapper(): + + num = 10 + + if num == 10: + print("Yes!") + else: + print("No!") + + some_function() + + print("Something is happening after some_function() is called.") + + return wrapper + + +def just_some_function(): + print("Wheee!") + +just_some_function = my_decorator(just_some_function) + +just_some_function() diff --git a/decorator07.py b/decorator07.py new file mode 100644 index 0000000..165e82a --- /dev/null +++ b/decorator07.py @@ -0,0 +1,20 @@ +def my_decorator(some_function): + + def wrapper(): + + num = 10 + + if num == 10: + print("Yes!") + else: + print("No!") + + some_function() + + print("Something is happening after some_function() is called.") + + return wrapper + + +if __name__ == "__main__": + my_decorator() diff --git a/decorator08.py b/decorator08.py new file mode 100644 index 0000000..566782a --- /dev/null +++ b/decorator08.py @@ -0,0 +1,8 @@ +from decorator07 import my_decorator + + +@my_decorator +def just_some_function(): + print("Wheee!") + +just_some_function() diff --git a/decorator09.py b/decorator09.py new file mode 100644 index 0000000..45102fd --- /dev/null +++ b/decorator09.py @@ -0,0 +1,27 @@ +import time + + +def timing_function(some_function): + + """ + Outputs the time a function takes + to execute. + """ + + def wrapper(): + t1 = time.time() + some_function() + t2 = time.time() + return "Time it took to run the function: " + str((t2 - t1)) + "\n" + return wrapper + + +@timing_function +def my_function(): + num_list = [] + for num in (range(0, 10000)): + num_list.append(num) + print("\nSum of all the numbers: " + str((sum(num_list)))) + + +print(my_function()) diff --git a/decorators.txt b/decorators.txt new file mode 100644 index 0000000..93cd2bf --- /dev/null +++ b/decorators.txt @@ -0,0 +1,139 @@ + +def hello_decorator(original_fn) + def decorator_fn(): + print("Hello from new") + original_fn() # original function must be invoked + return decorator_fn + +@hello_decorator +def hello(): + print("Hello from original") + + + +from time import sleep + +def delayed_func(func): + """Return `func`, delayed by 10 seconds.""" + def wrapper(): + print("Waiting for ten seconds...") + sleep(10) + # Call the function that was passed in + func() + + return wrapper + + +@delayed_func +def print_phrase(): + print("Fresh Hacks Every Day") + + + + + +import datetime +import time +from app_config import log + +def log_performance(func): + def wrapper(*args, **kwargs): + datetime_now = datetime.datetime.now() + log.debug(f"Function {func.__name__} being called at {datetime_now}") + start_time = time.time() + + result = func(*args, **kwargs) + + log.debug(f"Took {time.time() - start_time} seconds") + return result + return wrapper + + +@log_performance +def calculate_squares(n): + """Calculate the squares of the numbers 0 to n.""" + for i in range(n): + i_squared = i**2 + + + + + + + + + import datetime +import time +from app_config import log + +def log_performance(func): + def wrapper(*args, **kwargs): + datetime_now = datetime.datetime.now() + log.debug(f"Function {func.__name__} being called at {datetime_now}") + start_time = time.time() + + result = func(*args, **kwargs) + + log.debug(f"Took {time.time() - start_time} seconds") + return result + return wrapper + + +@log_performance +def calculate_squares(n): + """Calculate the squares of the numbers 0 to n.""" + for i in range(n): + i_squared = i**2 + + + +#Function with arguments + +def add_decorator(original_fn): + def decorator_fn(): + print("Hello from new") + original_fn() + return decorator_fn + +@add_decorator +def add(x, y): + print(x + y) + + + + +def add_decorator(n): + def decorator_fn(original_fn): + def wrapper_fn(*args, **kwargs): + result = original_fn(*args, **kwargs) + print(result+n) + return result + n + return wrapper_fn + return decorator_fn + +@add_decorator(2) +def add(x, y): + return x + y + + + +###uses execption handling + + +def exception_handler(original_fn): + def decorator_fn(*args, **kwargs): + try: + return original_fn(*args, **kwargs) + except Exception as err: + print(err) + return decorator_fn + +@exception_handler +def add(x, y): + sum = x + y + print(sum) + return sum + + + + diff --git a/diagram-2.png b/diagram-2.png new file mode 100644 index 0000000..ed3dc56 Binary files /dev/null and b/diagram-2.png differ diff --git a/diagram-3.png b/diagram-3.png new file mode 100644 index 0000000..cf3ed7a Binary files /dev/null and b/diagram-3.png differ diff --git a/diagram-4.png b/diagram-4.png new file mode 100644 index 0000000..c2d426f Binary files /dev/null and b/diagram-4.png differ diff --git a/examples.md b/examples.md new file mode 100644 index 0000000..269d7b6 --- /dev/null +++ b/examples.md @@ -0,0 +1,1822 @@ +Python + +Resources +========= + +This tutorial is mostly borrowing from: + +- Guido's Python tutorial: http://docs.python.org/tutorial + +Also useful: + +- Dive into Python book: http://www.diveintopython.net + +Interactive mode +================ + +- Type ``python`` at the command line. You should get:: + + Python 2.6 (#1, Feb 28 2007, 00:02:06) + Type "help", "copyright", "credits" or "license" for more information. + >>> + +Interactive mode (2) +==================== + +- Doesn't work? +- On Windows, try ``C:\python26\python.exe`` +- Or set the path: ``set path=%path%;C:\python26`` + +Interactive mode (3) +==================== + +- To get out, type ``Ctrl-Z`` or ``Ctrl-D``. + +Interactive mode (4) +==================== + +Our first program:: + + >>> world_is_flat = 1 + >>> if world_is_flat: + ... print "Don't fall off!" + ... + Don't fall off! + +Python as a calculator +====================== + +Use Python as a calculator:: + + >>> 2+2 + 4 + >>> 2+2 # a comment on the same line as code + 4 + +Python as a calc (2) +==================== + +The operators ``+``, ``-``, ``*``, ``/``, ``(`` and ``)`` work just +like in most other languages:: + + >>> 50-5*6 + 20 + >>> (50-5)*6 + 270 + +Python as a calc (3) +==================== + +Integer division returns the floor (before Python 3.x):: + + >>> 7/3 + 2 + >>> 7/-3 + -3 + +Python as a calc (4) +==================== + +The equal sign (``=``) is used to assign a value to a variable:: + + >>> width = 20 + >>> height = 5*9 + >>> width * height + 900 + +Python as a calc (5) +==================== + +Floating point operations:: + + >>> 3 * 3.75 / 1.5 + 7.5 + >>> 7.0 / 2 + 3.5 + +Python as a calc (6) +==================== + +The last printed expression is assigned to the variable ``_``:: + + >>> tax = 12.5 / 100 + >>> price = 100.50 + >>> price * tax + 12.5625 + +Python as a calc (7) +==================== + +:: + + >>> _ + 12.5625 + >>> price + _ + 113.0625 + >>> round(_, 2) + 113.06 + +Strings +======= + +:: + + >>> 'spam eggs' + 'spam eggs' + >>> 'doesn\'t' + "doesn't" + >>> '"Yes," he said.' + '"Yes," he said.' + +Strings (2) +=========== + +:: + + >>> print """ + ... Usage: thingy [OPTIONS] + ... -h Display this usage message + ... -H hostname Hostname to connect to + ... """ + Usage: thingy [OPTIONS] + -h Display this usage message + -H hostname Hostname to connect to + +Strings (3) +=========== + +``+`` and ``*`` operator:: + + >>> word = 'Help' + 'A' + >>> word + 'HelpA' + >>> '<' + word*5 + '>' + '' + +Strings (4) +=========== + +Indexing works like in C. There's no separate character type:: + + >>> word + 'HelpA' + >>> word[4] + 'A' + +Strings (5) +=========== + +Substrings using the *slice notation*:: + + >>> word[0:2] + 'He' + >>> word[2:4] + 'lp' + +Strings (6) +=========== + +Slice indexes have useful defaults:: + + >>> word[:2] # The first two characters + 'He' + >>> word[2:] # Everything except \ + ... # the first two characters + 'lpA' + +Strings (7) +=========== + +Python strings are *immutable*. Assigning to an indexed position +results in an error:: + + >>> word[0] = 'x' + Traceback (most recent call last): + File "", line 1, in ? + TypeError: 'str' object does not support item assignment + +Strings (8) +=========== + +However, creating a new string is easy and efficient:: + + >>> 'x' + word[1:] + 'xelpA' + >>> 'Splat' + word[4] + 'SplatA' + +Strings (9) +=========== + +Out-of-range slice indices are handled gracefully:: + + >>> word[1:100] + 'elpA' + >>> word[10:] + '' + >>> word[2:1] + '' + +Strings (10) +============ + +Indices may be negative numbers, to start counting from the right:: + + >>> word[-1] # The last character + 'A' + >>> word[-2] # The last-but-one character + 'p' + +Strings (11) +============ + +:: + + >>> word[-2:] # The last two characters + 'pA' + >>> word[:-2] # Everything except the last two characters + 'Hel' + >>> word[-100:] # Out-of-range index is truncated + 'HelpA' + +Strings (12) +============ + +The built-in function ``len`` returns the length of a string:: + + >>> s = 'supercalifragilisticexpialidocious' + >>> len(s) + 34 + +Lists +===== + +- one of Python's *compound data types* +- can be written as a list of comma-seprated values (items) between + square brackets +- items need not have the same type + +:: + + >>> a = ['spam', 'eggs', 100, 1234] + >>> a + ['spam', 'eggs', 100, 1234] + +Lists (2) +========= + +Like strings, lists support index operations:: + + >>> a[0] + 'spam' + >>> a[3] + 1234 + >>> a[-2] + 100 + +Lists (3) +========= + +Lists can be sliced and concatenated:: + + >>> a[1:-1] + ['eggs', 100] + >>> a[1:-1] + ['eggs', 100] + >>> a[:2] + ['bacon', 2*2] + ['spam', 'eggs', 'bacon', 4] + >>> 2*a[:2] + ['Boo!'] + ['spam', 'eggs', 'spam', 'eggs', 'Boo!'] + +Lists (4) +========= + +Unlike with strings, it's possible to change individual elements of a +list:: + + >>> a + ['spam', 'eggs', 100, 1234] + >>> a[2] = a[2] + 23 + >>> a + ['spam', 'eggs', 123, 1234] + +Lists (5) +========= + +Assignments to slices is also possible:: + + >>> # Replace some items: + ... a[0:2] = [1, 12] + >>> a + [1, 12, 123, 1234] + >>> # Remove some: + ... a[0:2] = [] + >>> a + [123, 1234] + +Lists (6) +========= + +:: + + >>> # Insert some: + ... a[1:1] = ['bletch', 'xyzzy'] + >>> a + [123, 'bletch', 'xyzzy', 1234] + >>> # Insert (a copy of) itself: + ... a[:0] = a + >>> a + [123, 'bletch', 'xyzzy', 1234, 123, 'bletch', 'xyzzy', 1234] + +Lists (7) +========= + +The built-in function ``len`` also works with lists:: + + >>> a = ['a', 'b', 'c', 'd'] + >>> len(a) + 4 + +Lists (8) +========= + +It's possible to nest lists:: + + >>> q = [2, 3] + >>> p = [1, q, 4] + >>> len(p) + 3 + >>> p[1] + [2, 3] + +Lists (9) +========= + +:: + + >>> p[1].append('xtra') + >>> p + [1, [2, 3, 'xtra'], 4] + >>> q + [2, 3, 'xtra'] + +Note that ``p[1]`` and ``q`` refer to the same object! + +First Steps Towards Programming +=============================== + +Fibonacci series; the sum of two elements defines the next:: + + >>> a, b = 0, 1 + >>> while b < 1000: + ... print b, + ... a, b = b, a+b + ... + 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 + +First Steps Towards Programming (2) +=================================== + +- *Multiple assignment*: the expressions on the right-hand are all + evaluated first before any assignment takes place. + +- The ``while`` loop executes as long as the condition remains true. + +First Steps Towards Programming (3) +=================================== + +- Standard comparison operators work like in C: ``<``, ``>``, ``==``, + ``<=``, ``>=``, ``!=``. + +- Any non-zero value is true, like in C. Zero is false. + +- The condition may also be a string or list value; in fact any + sequence. An empty sequence is false, a non-empty sequence is true. + +First Steps Towards Programming (4) +=================================== + +:: + + >>> s = [1, 2] + >>> len(s) >= 3 + False + >>> if s: + ... print "Non-empty" + ... print "Length:", len(s) + Non-empty + Length: 2 + +First Steps Towards Programming (5) +=================================== + +- The *body* of the loop is *indented*: indentation is how Python + groups statements. In practice your editor will help with + indenting. + +- The ``print`` statement writes the value of the expression it is + given. A trailing comma avoids newline after the input. + +``If`` statment +=============== + +:: + + >>> x = 42 + >>> if x < 0: + ... x = 0 + ... print 'Negative changed to zero' + ... elif x == 0: + ... print 'Zero' + ... elif x == 1: + ... print 'Single' + ... else: + ... print 'More' + ... + More + +``If`` statement (2) +==================== + +- There can be zero or more ``elif`` parts and the ``else`` part is + optional. + +- ``elif`` is short for *else if* + +- An ``if`` ... ``elif`` ... ``elif`` ... sequence is a substitute for + the ``switch`` or ``case`` statements found in other languages. + +``For`` statement +================= + +:: + + >>> # Measure some strings: + ... a = ['cat', 'window', 'defenestrate'] + >>> for x in a: + ... print x, len(x) + ... + cat 3 + window 6 + defenestrate 12 + +``For`` statement (2) +===================== + +- May be different to what you're used to. + +- The ``for`` statement in Python always iterates over items of any + sequence in the order that they appear in the sequence. + +``For`` statement (3) +===================== + +It is not safe to modify the sequence being iterated over. Make a +copy if you need to:: + + >>> for x in a[:]: # make a slice copy + ... if len(x) > 6: a.insert(0, x) + ... + >>> a + ['defenestrate', 'cat', 'window', 'defenestrate'] + +``range`` function +================== + +The ``range`` function generates lists containing arithmetic +progressions:: + + >>> range(10) # end point is never part of the list + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> range(5, 10) # range starts at 5 + [5, 6, 7, 8, 9] + >>> range(0, 10, 3) # increment is 3 + [0, 3, 6, 9] + +``break`` and ``continue`` statments +==================================== + +- The ``break`` statement breaks out of the smallest enclosing ``for`` + or ``while`` loop. + +- The ``continue`` statement continues with the next iteration of the + loop. + +- That is, both statements work just like in C. + +``break`` and ``continue`` statments (2) +======================================== + +:: + + <<< a + ['defenestrate', 'cat', 'window', 'defenestrate'] + <<< f = raw_input("Find: ") + Find: window + <<< for x in a: + ... if f == x: + ... print "Found", f + ... break + ... + Found window + +``else`` clauses on loops +========================= + +Loop statements may have an ``else`` clause that's executed when the +loop was **not** terminated by a ``break``. + +``else`` clauses on loops (2) +============================= + +:: + + >>> f = 'dog' + >>> for x in a: + ... if f == x: + ... print "Found", f + ... break + ... else: + ... print f, "not found" + dog not found + +Exercise: Prime numbers +======================= + +Write a program that for each number between 2 and 9: + +- prints the number itself if it's a prime number +- prints two factors that multiplied together equal the number + +Exercise: Prime numbers (2) +=========================== + +- You'll use ``for``, ``range``, ``break``, and ``else``. + +- You'll need the modulo operator ``%``:: + + >>> 9 / 4 + 2 + >>> 9 % 4 + 1 + +Exercise: Prime numbers (3) +=========================== + +:: + + >>> for n in range(2, 10): + ... for x in range(2, n): + ... if n % x == 0: + ... print n, 'equals', + ... print x, '*', n/x + ... break + ... else: # inner loop fell through + ... print n, 'is a prime number' + ... + 2 is a prime number + 3 is a prime number + 4 equals 2 * 2 + 5 is a prime number + 6 equals 2 * 3 + 7 is a prime number + 8 equals 2 * 4 + 9 equals 3 * 3 + +Functions +========= + +:: + + >>> def replace(s, fro, to): + ... for i in range(len(s)): + ... if s[i] == fro: + ... s[i] = to + + >>> k = ['one', 'two'] + >>> replace(k, 'one', 'three') + >>> k + ['three', 'two'] + +Functions (2) +============= + +- The ``def`` keyword introduces a function definition, followed by + the function name and parameters in parantheses. + +- The function body starts at the next line and is indented. + +- Values in Python are *always* object references. + +Functions (3) +============= + +- Defining a function introduces the name in the current symbol table. + +- The function can be assigned to another name:: + + >>> r = replace + >>> r(k, 'three', 1) + >>> k + [1, 'two'] + +Exercise: Fibonacci function +============================ + +Write a function that writes the Fibonacci series to an arbitrary +boundary. + +Exercise: Fibonacci function (2) +================================ + +:: + + >>> def fib(n): + ... """Print Fibonacci series up to n.""" + ... a, b = 0, 1 + ... while b < n: + ... print b, + ... a, b = b, a+b + ... + >>> # Now call the function we just defined: + ... fib(2000) + 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 + +Functions (4) +============= + +Isn't ``fib`` really a procedure since it returns no value? Python +functions always have a default return value, which is ``None``. It's +not printed by the interactive mode:: + + >>> fib(0) + >>> print fib(0) + None + +Functions (5) +============= + +Use the ``return`` keyword to return a value from a function:: + + >>> def absadd(a, b): + ... return abs(a + b) + >>> absadd(-5, -10) + 15 + >>> a = absadd(absadd(-5, -10), -100) + >>> a + 85 + +Exercise: Fibonacci function (2) +================================ + +Modify your Fibonacci function to return a list instead of printing +numbers. + +You'll use the ``append`` *method* of lists:: + + >>> l = [1, 5] + >>> l.append('Nine') + >>> l + [1, 5, 'Nine'] + +Exercise: Fibonacci function (3) +================================ + +:: + + >>> def fib2(n): + ... """Fibonacci series up to n.""" + ... result = [] + ... a, b = 0, 1 + ... while b < n: + ... result.append(b) + ... a, b = b, a+b + ... return result + +Exercise: ``replace`` +===================== + +Write a function ``replace2`` that works like the ``replace`` we +defined earlier with the exception that it doesn't modify the list +that we pass to it, but returns a new list. + +Exercise: ``replace`` (2) +========================= + +:: + + >>> def replace2(s, fro, to): + ... s = s[:] # copy + ... for i in range(len(s)): + ... if s[i] == fro: + ... s[i] = to + ... return s + >>> s = ['spam', 'eggs'] + >>> replace2(s, 'eggs', 'butter') + ['spam', 'butter'] + >>> s + ['spam', 'eggs'] + +Functions (6) +============= + +:: + + >>> f100 = fib2(100) + >>> f100 + [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] + +Function arguments +================== + +It's possible to define functions with a variable number of arguments, +like ``range``:: + + >>> range(5) + [0, 1, 2, 3, 4] + >>> range(1, 5) + [1, 2, 3, 4] + >>> range(1, 6, 2) + [1, 3, 5] + +Function arguments (2) +====================== + +*Default argument values* are very useful:: + + >>> def replace3(s, fro, to, count=0): + ... s = s[:] + ... for i in range(len(s)): + ... if s[i] == fro: + ... s[i] = to + ... count = count - 1 + ... if count == 0: break + ... return s + +Function arguments (3) +====================== + +:: + + >>> replace3(['one', 'two', 'one', 'two'], + ... 'one', 'three') + ['three', 'two', 'three', 'two'] + >>> replace3(['one', 'two', 'one', 'two'], + ... 'one', 'three', 1) + ['three', 'two', 'one', 'two'] + +Exercise: ``ask_ok`` +==================== + +:: + + >>> def ask_ok(prompt, retries=4, + ... complaint='Yes or no, plz!'): + ... """Prompt the user for yes or no. + ... Return True for positive answer or + ... False for negative answer. User will + ... be prompted a maximum of ``retries`` + ... times before ``IOException`` is + ... raised. + ... """ + +Exercise: ``ask_ok`` (2) +======================== + +Your program should: + +- print the ``prompt`` +- ask the user for input (``raw_input``) only ``retries`` times (``while``) +- print the ``complaint`` if the user gave wrong input +- return ``True`` or ``False`` or raise an error if no valid input was + given + +Exercise: ``ask_ok`` (3) +======================== + +How to *raise an exception*? :: + + >>> input = 'N' + >>> if input == 'yes': + ... value = True + ... elif input == 'no': + ... value = False + ... else: + ... raise ValueError("Invalid input!") + Traceback (most recent call last): + ValueError: Invalid input! + +Exercise: ``ask_ok`` (4) +======================== + +**Extra**: Add tolerance so that the user can enter any of *y*, *ye*, +and *yes* to answer positively, and any of *n*, *no*, *nope* to answer +negatively. For this, you may want to try the ``in`` operator:: + + >>> 'one' in ['two', 'three'] + False + +Exercise: ``ask_ok`` (5) +======================== + +.. class:: tiny +.. + + >>> def ask_ok(prompt, retries=4, + ... complaint='Yes or no, please!'): + ... while True: + ... ok = raw_input(prompt) + ... if ok in ('y', 'ye', 'yes'): + ... return True + ... if ok in ('n', 'no', 'nope'): + ... return False + ... retries = retries - 1 + ... if retries < 0: + ... raise IOError('refusenik user') + ... print complaint + +Function arguments (4) +====================== + +The function can be called like this: + +.. class:: tiny + +- ``ask_ok('Do you really want to quit?')`` +- ``ask_ok('OK to overwrite the file?', 2)`` +- ``ask_ok('OK to overwrite the file?', 2, 'yes or no')`` +- ``ask_ok('Continue? (y/n)', complaint='Come on!')`` +- ``ask_ok('Continue? (y/n)', complaint='y/n', retries=10)`` + +Function arguments (5) +====================== + +Arbitrary argument lists are less frequent: + +.. class:: tiny +.. + + >>> def cheeseshop(kind, *arguments, **keywords): + ... print "-- Do you have any", kind, "?" + ... print "-- I'm sorry, we're all out of", kind + ... for arg in arguments: + ... print arg + ... print "-" * 40 + ... keys = keywords.keys() + ... keys.sort() + ... for kw in keys: print kw, ":", keywords[kw] + +Function arguments (6) +====================== + +.. class:: tiny +.. + + >>> cheeseshop("Limburger", "It's very runny, sir.", + ... "It's really very, VERY runny, sir.", + ... shopkeeper='Michael Palin', + ... client="John Cleese", + ... sketch="Cheese Shop Sketch") + -- Do you have any Limburger ? + -- I'm sorry, we're all out of Limburger + It's very runny, sir. + It's really very, VERY runny, sir. + ---------------------------------------- + client : John Cleese + shopkeeper : Michael Palin + sketch : Cheese Shop Sketch + +Function arguments (7) +====================== + +If your arguments are already in a list (or tuple), you can use the +``*`` operator to unpack the arguments:: + + >>> range(3, 6) # normal call + [3, 4, 5] + >>> args = [3, 6] + >>> range(*args) # arguments unpacked + [3, 4, 5] + +Function arguments (8) +====================== + +The same works with keyword arguments and dictionaries: + +.. class:: tiny +.. + + >>> def parrot(voltage, state='a stiff', action='voom'): + ... print "-- This parrot wouldn't", action, + ... print "if you put", voltage, "volts through it.", + ... print "E's", state, "!" + >>> d = {"voltage": "four million", + ... "state": "bleedin' demised", + ... "action": "VOOM"} + >>> parrot(**d) + -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised ! + +Exercise: ``range2`` +==================== + +Write a function ``range2`` that acts just like the Python built-in. + +You'll probably use a variable number of positional, non-keyword +arguments. + +To remind yourself how ``range`` works, see ``help(range)``. + +Exercise: ``range2`` (2) +======================== + +:: + + >>> def range2(*args): + ... start, step = 0, 1 + ... if len(args) > 1: + ... start, stop = args[:2] + ... else: + ... stop = args[0] + ... if len(args) == 3: + ... step = args[2] + ... return range(start, stop, step) + +Exercise: ``range2`` (3) +======================== + +:: + + >>> range2(5) + [0, 1, 2, 3, 4] + >>> range2(1, 5) + [1, 2, 3, 4] + >>> range2(1, 6, 2) + [1, 3, 5] + +Finding documentation +===================== + +Python has great built-in documentation facilities. Try +``help(max)`` or ``help(filter)``. + +Exercise: Using ``max`` +======================= + +Use ``max`` to find the largest element of the list ``l``. + +Exercise: Using ``filter`` +========================== + +Use ``filter`` to remove all zeros from a list of numbers like this +one:: + + >>> l = [5, 4, 3, 0, 2, 1, 0] + +Finding documentation (2) +========================= + +How to know what methods a list has? Remember there was ``l.append``. + +What other methods exist? Try ``dir(l)``. For each method, you can +do ``help(method)``, like ``help(l.insert)``. You can also do +``help(l)``. + +Exercise: Some sorting +====================== + +Write a function ``sort_in`` that takes a list and an element to sort +into the list. The function should modify the list *in place* and +expect the list to be sorted already. + +You'll probably use ``range``. Try out ``l.insert``. + +Exercise: Some sorting (2) +========================== + +Let's also look at how ``enumerate`` works:: + + >>> for index, value in enumerate( + ... ['un', 'deux', 'trois']): + ... print index, value + ... + 0 un + 1 deux + 2 trois + +Exercise: Some sorting (3) +========================== + +:: + + >>> def sort_in(s, el): + ... for index, item in enumerate(s): + ... if el < item: + ... break + ... else: + ... index = len(s) + ... s.insert(index, el) + ... return s + +Exercise: Some sorting (4) +========================== + +Let's try it out:: + + >>> sort_in([3, 5], 4) + [3, 4, 5] + >>> sort_in([], 3) + [3] + >>> sort_in([5], 3) + [3, 5] + >>> sort_in([5], 6) + [5, 6] + +Exercise: Some sorting (5) +========================== + +For more sorting, check out the ``sorted`` built-in. + +Try ``sorted`` to reverse sort the items in a list. + +Exercise: Some sorting (6) +========================== + +:: + + >>> s = ['a', 'z', 'b', 'f'] + >>> sorted(s, reverse=True) + ['z', 'f', 'b', 'a'] + >>> s # s is still the old list + ['a', 'z', 'b', 'f'] + >>> s.sort(reverse=True) # in-place + >>> s + ['z', 'f', 'b', 'a'] + +More functions +============== + +Functions in Python are first class objects:: + + >>> def divide_by_2(x): + ... return x / 2 + ... + >>> divide_by_2 + + >>> divide_by_2(10) + 5 + +More functions (2) +================== + +We can pass one function as an argument to another:: + + >>> def is_lower(s): + ... return s.islower() + >>> filter(is_lower, + ... ['Spam', 'eggs', 'limBurger']) + ['eggs'] + +More functions (3) +================== + +Create functions on the fly: + +.. class:: tiny +.. + + >>> def divide_by_factory(n): + ... def divide_by_n(x): + ... return x / n + ... return divide_by_n + >>> divide_by_2 = divide_by_factory(2) + >>> divide_by_5 = divide_by_factory(5) + >>> divide_by_2(10) + 5 + >>> divide_by_5(50) + 10 + +List comprehensions +=================== + +Easier to read than ``filter``:: + + >>> l = ['Spam', 'eggs', 'limBurger'] + >>> [w for w in l if w.islower()] + ['eggs'] + +Exercise: More sorting +====================== + +``sorted`` takes an optional ``key`` argument. If provided, this must +be a function that takes an element and returns a value suitable for +sorting that element: a *predicate*. + +Exercise: More sorting (2) +========================== + +Write a function ``even_sort`` that takes a list with numbers and +returns a new list with even numbers before odd ones. + +So ``[4, 5, 6, 7, 8]`` will result in ``[4, 6, 8, 5, 7]``. + +Exercise: More sorting (3) +========================== + +:: + + >>> def modulo2(number): + ... return number % 2 + >>> def even_sort(s): + ... return sorted(s, key=modulo2) + + >>> even_sort([4, 5, 6, 7, 8]) # stable + [4, 6, 8, 5, 7] + +Lambda +====== + +With the keyword ``lambda``, small anonymous functions can be created. +This function:: + + >>> lambda a, b: a + b + at ...> + +Is equivalent to this one:: + + >>> def add(a, b): + ... return a + b + +Lambda (2) +========== + +We can also assign the former a name:: + + >>> add = lambda a, b: a + b + >>> add(5, -3) + 2 + +Exercise: More sorting (3) +========================== + +Instead of defining a separate ``modulo2`` function, we can use a +``lambda`` function with ``even_sort``:: + + >>> def even_sort2(s): + ... return sorted(s, key=lambda n: n%2) + + >>> even_sort2([4, 5, 6, 7, 8]) + [4, 6, 8, 5, 7] + +Exercise: More sorting (4) +========================== + +Use ``sorted`` to reverse the order of a list. But this time, don't +use the ``reverse`` argument, but do it with a ``key`` parameter. + +Exercise: More sorting (5) +========================== + +These two are thus equivalent:: + + >>> sorted([5, 9, 3, 5], key=lambda n:-n) + [9, 5, 5, 3] + + >>> sorted([5, 9, 3, 5], reverse=True) + [9, 5, 5, 3] + +More on lists +============= + +- ``list.append(x)`` +- ``list.extend(L)`` +- ``list.insert(i, x)`` +- ``list.remove(x)`` +- ``list.pop([i])`` + +More on lists (2) +================= + +- ``list.count(x)`` +- ``list.sort()`` +- ``list.reverse()`` + +Using lists as stacks +===================== + +LIFO:: + + >>> stack = [3, 4, 5] + >>> stack.append(6) + >>> stack.append(7) + >>> stack + [3, 4, 5, 6, 7] + +Using lists as stacks (2) +========================= + +:: + + >>> stack + [3, 4, 5, 6, 7] + >>> stack.pop() + 7 + >>> stack + [3, 4, 5, 6] + >>> stack.pop() + 6 + +Using lists as queues +===================== + +FIFO:: + + >>> queue = ["Eric", "John", "Michael"] + >>> queue.append("Terry") # Terry arrives + >>> queue.append("Graham") # Graham arrives + >>> queue.pop(0) + 'Eric' + >>> queue + ['John', 'Michael', 'Terry', 'Graham'] + +Files +===== + +``open`` returns a file object, and is most commonly used with two +arguments: ``open(filename, mode)``. + +:: + + >>> f = open('/tmp/workfile', 'w') + >>> print f + + +Files (2) +========= + +The second argument is a string containing a few characters describing +the way in which the file will be used: + +- ``'r'`` is for only reading +- ``'w'`` is for only writing +- ``'a'`` is for appending +- ``r+`` is for read and write + +Files (3) +========= + +On Windows, ``'b'`` appended to the mode opens the file in binary +mode, so there are also modes like ``'rb'``, ``'wb'``, and ``'r+b'``. +Windows makes a distinction between text and binary files; the +end-of-line characters in text files are automatically altered +slightly when data is read or written. + +Files (4) +========= + +Let's write to our previously opened file: + + >>> f.write('This is the first.\n') + >>> f.close() # flush + +Files (5) +========= + +We can now read it:: + + >>> f = open('/tmp/workfile') # 'r' default + >>> f.read() # read the entire file + 'This is the first.\n' + >>> f.close() + +Files (6) +========= + +Let's write more to our file:: + + >>> f = open('/tmp/workfile', 'a') + >>> f.write("""This is the second, + ... and this is the third line. + ... """) + >>> f.close() # flush + +Files (7) +========= + +We can read lines by looping over the file object: + + >>> f = open('/tmp/workfile', 'r') + >>> for line in f: + ... print line, # includes \n + This is the first. + This is the second, + and this is the third line. + >>> f.close() + +Exercise: ``replacef`` +====================== + +Write a function ``replacef`` that takes the name of a file, and two +strings. The function should replace in the given file all +occurrences of the first string by the second one. + +Exercise: ``replacef`` (2) +========================== + +:: + + >>> def replacef(fn, fro, to): + ... f = open(fn) + ... contents = f.read() + ... f.close() + ... f = open(fn, 'w') + ... f.write(contents.replace(fro, to)) + ... f.close() + +Exercise: ``replacef`` (3) +========================== + +Let's try it out:: + + >>> replacef('/tmp/workfile', ' is', ' be') + >>> f = open('/tmp/workfile') + >>> print f.read() + This be the first. + This be the second, + and this be the third line. + >>> f.close() + +Encoding strings +================ + +The ``encode`` and ``decode`` methods of strings are useful:: + + >>> s = 'Hello, Python African Tour!'.encode('base64') + >>> s + 'SGVsbG8sIFB5dGhvbiBBZnJpY2FuIFRvdXIh\n' + >>> s.decode('base64') + 'Hello, Python African Tour!' + +Our first Python module +======================= + +Let's create our first script: + +- Create a new file called ``rot13.py`` +- We'll write our code into the file now. Be careful with indentation, + and don't write ``>>>`` or ``...``. + +Our first Python module (2) +=========================== + +Write:: + + def encode_rot13(fn_in, fn_out): + f = open(fn_in) + contents = f.read() + f.close() + f = open(fn_out, 'w') + f.write(contents.encode('rot13')) + f.close() + +Our first Python module (3) +=========================== + +At the end, write:: + + if __name__ == '__main__': + import sys + encode_rot13(sys.argv[1], sys.argv[2]) + +Our first Python module (4) +=========================== + +Call it:: + + python rot13.py myfile.txt myfile-encoded.txt + +Exercise: ``encode.py`` +======================= + +Write a script:: + + encode.py + +Encode the and write out to . + +Exercise: ``encode.py`` (2) +=========================== + +**Extra**: If is ommitted, then should be +overwritten. + +Look at ``sys.argv``. + +Exercise: ``encode.py`` (3) +=========================== + +**Extra**:: + + encode + +E.g.:: + + encode iso-8859-1 utf-8 orig.txt conv.txt + +You'll need ``''.encode`` and ``''.decode`` methods! + +Tuples +====== + +Tuples are like lists, except that they're immutable, like strings:: + + >>> t = (12345, 54321, 'hello!') + >>> t[0] + 12345 + >>> t[0] = 67890 + Traceback (most recent call last): + ... + TypeError: 'tuple' object does not support item assignment + +Tuples (2) +========== + +A nice feature of Python is *sequence unpacking*:: + + >>> x, y, z = t + >>> x + 12345 + >>> y + 54321 + >>> z + 'hello!' + +Tuples (3) +========== + +We saw sequence unpacking before, with ``enumerate``:: + + >>> for index, item in enumerate( + ... ("Un", "Deux", "Trois")): + ... print index, item + 0 Un + 1 Deux + 2 Trois + >>> tuple(enumerate(("Un", "Deux", "Trois"))) + ((0, 'Un'), (1, 'Deux'), (2, 'Trois')) + +Sets +==== + +A set is an unordered collection with no duplicate elements:: + + >>> basket = ['apple', 'orange', 'apple', + ... 'pear', 'orange', 'banana'] + >>> fruit = set(basket) # no duplicates + >>> fruit + set(['orange', 'pear', 'apple', 'banana']) + +Sets (2) +======== + +:: + + >>> 'orange' in fruit # fast test + True + >>> 'crabgrass' in fruit + False + >>> fruit.add('strawberry') + >>> len(fruit) + 5 + +Sets (3) +======== + +Set operations on word letters:: + + >>> a = set('abracadabra') + >>> b = set('alacazam') + >>> a # unique letters in a + set(['a', 'r', 'b', 'c', 'd']) + >>> a - b # letters in a but not in b + set(['r', 'b', 'd']) + +Sets (4) +======== + +Set operations on word letters contd.:: + + >>> union = a | b + >>> intersection = a & b + >>> xor = a ^ b + +Exercise: ``common.py`` +======================= + +Write a script ``common.py``:: + + python common.py [file2] [file3] [...] + +This script should print out a sorted list of common words in all +files. + +Use the ``''.split`` method to get a list of words in a file. + +Exercise: ``common.py`` (2) +=========================== + +:: + + >>> def common(contents): + ... words = set(contents[0].split()) + ... for s in contents[1:]: + ... words &= set(s.split()) + ... return sorted(words) + +Exercise: ``common.py`` (3) +=========================== + +:: + + >>> common(["This is the first line", + ... "This is the second line", + ... "This is the third line", + ... "This line is funny"]) + ['This', 'is', 'line'] + +Exercise: ``common.py`` (4) +=========================== + +:: + + if __name__ == '__main__': + all_contents = [] + for fn in sys.argv[1:]: + f = open(fn) + all_contents.append(f.read()) + f.close() + common(all_contents) + +Exercise: ``common.py`` (5) +=========================== + +Make ``common.py`` case-insensitive. Look at helpful methods of a +string. Try ``help(str)``. + +Dictionaries +============ + +A dictionary is an unordered set of *key: value* pairs, with the +requirement that the keys are unique. + +A pair of braces creates an empty dictionary: ``{}``. Placing a +comma-separated list of key:value pairs within the braces adds initial +key:value pairs to the dictionary, like ``{1: 'un'}``. + +Dictionaries (2) +================ + +Any immutable type can be used as a key, like strings and tuples. +Lists may not be used as keys, since they can be modified in place. + +Dictionaries (3) +================ + +The main operations are: + +- storing a value with some key +- extracting a value given the key + +:: + + >>> tel = {'jack': 4098, 'sape': 4139} + >>> tel['guido'] = 4127 + >>> tel['jack'] + 4098 + +Dictionaries (4) +================ + +:: + + >>> tel + {'sape': 4139, 'jack': 4098, 'guido': 4127} + >>> del tel['sape'] + >>> tel['irv'] = 4127 + >>> tel + {'jack': 4098, 'irv': 4127, 'guido': 4127} + >>> tel.keys() + ['jack', 'irv', 'guido'] + >>> 'guido' in tel + True + +Dictionaries (5) +================ + +Looping over dictionaries:: + + >>> for key in tel: + ... print key, + jack irv guido + + >>> for key, value in tel.items(): + ... print '%s: %s' % (key, value) + jack: 4098 + irv: 4127 + guido: 4127 + +Exercise: ``count.py`` +====================== + +Make a script ``count.py`` that returns a list of words in a given +file along with the number of their occurrences, like this:: + + 33 is + 25 there + 21 are + ... + +Exercise: ``count.py`` (2) +========================== + +:: + + >>> def count(text): + ... table = {} + ... for word in text.split(): + ... table.setdefault(word, 0) + ... table[word] += 1 + ... return table + +Exercise: ``count.py`` (3) +========================== + +:: + + >>> def count_format(text): + ... pairs = count(text).items() + ... pairs.sort(key=lambda i:-i[1]) + ... for word, occ in pairs: + ... print occ, word + +Exercise: ``count.py`` (4) +========================== + +Let's try it out:: + + >>> f = open('/usr/share/doc/time/README') + >>> count_format(f.read()) + 5 and + 5 the + 2 program + 2 GNU + 2 as + ... + >>> f.close() + +Exercise: ``count.py`` (5) +========================== + +Our count program has a little problem: It'll interpret punctuation +characters as part of a word, like with "there," or "file.". + +Look at method ``''.strip()`` to see how to fix this. Also look at +the ``string`` module for useful constants. Use ``import string; +help(string)``. + +Exercise: ``count.py`` (6) +========================== + +:: + + >>> import string + >>> '-file;.'.strip(string.punctuation) + 'file' + +Zen of Python +============= + +:: + + >>> import this + The Zen of Python, by Tim Peters... + +Classes +======= + +:: + + >>> yussuf = {'firstname': 'Yussuf', + ... 'lastname': 'Islam'} + >>> def fullname(record): + ... value = record['firstname'] + ... middlename = record.get('middlename') + ... if middlename: + ... value += ' ' + middlename + ... value += ' ' + record['lastname'] + ... return value + +Classes (2) +=========== + +:: + + >>> fullname(yussuf) + 'Yussuf Islam' + >>> michael = {'firstname': 'Michael', + ... 'lastname': 'Palin', + ... 'middlename': 'Abraham'} + >>> fullname(michael) + 'Michael Abraham Palin' + +Classes (3) +=========== + +.. class:: tiny +.. + + >>> class Person: + ... def __init__(self, firstname, lastname, + ... middlename=''): + ... self.firstname = firstname + ... self.lastname = lastname + ... self.middlename = middlename + ... def fullname(self): + ... value = self.firstname + ... if self.middlename: + ... value += ' ' + self.middlename + ... value += ' ' + self.lastname + ... return value + +Classes (4) +=========== + +:: + + >>> me = Person('Daniel', 'Nouri') + >>> john = Person('John', 'Cleese') + >>> michael = Person( + ... 'Michael', 'Palin', 'Abraham') + +Classes (5) +=========== + +:: + + >>> me.firstname + 'Daniel' + >>> me.middlename + '' + >>> me.fullname() + 'Daniel Nouri' + >>> michael.fullname() + 'Michael Abraham Palin' + +Classes (6) +=========== + +:: + + >>> persons = [john, me, michael] + >>> persons.sort() + + <<< persons[0].lastname + 'Nouri' + <<< persons[1].lastname + 'Cleese' + <<< persons[2].lastname + 'Palin' + +Classes (6) +=========== + +:: + + >>> class SortablePerson(Person): + ... def __lt__(self, other): + ... return self.lastname < other.lastname + +Classes (7) +=========== + +:: + + >>> SP = SortablePerson + >>> persons = [ + ... SP('John', 'Cleese'), + ... SP('Daniel', 'Nouri'), + ... SP('Michael', 'Palin', 'Abraham'), + ... ] + +Classes (8) +=========== + +:: + + >>> persons.sort() + >>> persons[0].fullname() + 'John Cleese' + >>> persons[1].fullname() + 'Daniel Nouri' + >>> persons[2].fullname() + 'Michael Abraham Palin' + diff --git a/exception-sample-2.md b/exception-sample-2.md new file mode 100644 index 0000000..a324e5e --- /dev/null +++ b/exception-sample-2.md @@ -0,0 +1,198 @@ +You can raise an exception with the try/except statements, which full syntax is: + +>>> try: +>>> open("tmp/non-existing-file.tttt") +>>> except IOError: +>>> print "The file '/tmp/non-existing-file.ttt' does not exist" +>>> finally: +>>> print 'Whatever the results is we can enter here, to properly close the file for instanece.' +>>> else: +>>> print "Well, the file exists despite itscrazy name: the else clause is executed." + +Note that in general most code includes only the try/except: + +>>> try: +>>> open("tmp/non-existing-file.tttt") +>>> except IOError: +>>> print "The file 'tmp/non-existing-file.ttt' does not exist" + +or directly with the raise keyword: + +x = 0. +if x == 0: + raise ZeroDivisionError +else: + return 1./x + +3.2. Explanation + +The try/except example is quite straightforward to understand given the print statements. The only tricky part is the IOError. This is an exception, that is a predefined error in Python language. There are a bunch of them like TypeError, ZeroDivisionError and so on (see below for more details). + +The idea is that if you know that a specific error might occur, then you should use the try/except to catch this particular error. + +The more general usage of the except block is to provide an exception and its argument: + +try: + 1/0 +except Exception, e: + print(e) + +In fact e is the exception argument that you can introspect: + +e.args +e.message + +3.3. Multiple exceptions + +You can put multiple exeception in the except statement as follows: + +>>> try: +>>> x = input('Enter the first number: ') +>>> y = input('Enter the second number: ') +>>> print x/y +>>> except (ZeroDivisionError, TypeError), e: +>>> print 'Your numbers were bogus...' +>>> print e + +you may decide not to set any exception: + +>>> try: +>>> try_statements +>>> except: +>>> except_statements + +This try-except statement catches all the exceptions that occur. However, it is not considered a good programming practice, because it does not make the programmer identify the root cause of the problem that may occur. +3.4. Ignoring an exception + +You can ignore an exception by catching it: + +x = -10 +try: + import math + print math.sqrt(x) +except ValueError, e: + import cmath + print cmath.sqrt(x) + +Of course this example is simple, you can have test if the number is negative before hand but it shows how to catch an error and fix the problem within your code. You could also simply do nothing if the number if negative: + +x = -10 +try: + import math + print math.sqrt(x) +except: + pass + +3.5. What are the existing exceptions ? + +>>> import exceptions +>>> dir(exceptions) +['ArithmeticError', 'AssertionError', 'AttributeError', ...] + +3.6. User-defined exceptions + +Although there are quite a lot of exceptions, you can also define your own exception. The following example (From Python website exceptions) illustrates the syntax and usage: + +>>> class MyError(Exception): +... def __init__(self, value): +... self.value = value +... def __str__(self): +... return repr(self.value) +... +>>> try: +... raise(MyError(2*2)) +... except MyError, e: +... print 'My exception occured, value:', e.value +My exception occured, value: 4 + +3.7. Cleanup using the finally or with statement + +The finally block is run whatever is the outcome of the try block. This is particurlaly useful in the context of file manipulation.: + +try: + f = open("temp.txt", "w") + # something wrong here e.g. cannot open the file +except IOError, e: + print e +finally: + f.close() + +Another way to ensure the previous cleanup action is to use the with statement. Let consider this code that prints a file contents with numbers for each line (withouth the with statement): + +num = 1 +f = open(fname) +for line in f: + print("%d %s" % (num, line)) + num += 1 + +if you don’t call the close statement specifically, we don’t know precisey when the file will be closed. Instead, the same code can be written with the with statement as follows: + +num = 1 +with open(fname, "r") as f: + for line in f: + print("%d %s" % (num, line)) + num += 1 + +With the with statement; the file is closed immediately after the for loop. +3.8. Exception hierarchy + +From Python website (exception-hierarchy): + +BaseException + +-- SystemExit + +-- KeyboardInterrupt + +-- GeneratorExit + +-- Exception + +-- StopIteration + +-- StandardError + | +-- BufferError + | +-- ArithmeticError + | | +-- FloatingPointError + | | +-- OverflowError + | | +-- ZeroDivisionError + | +-- AssertionError + | +-- AttributeError + | +-- EnvironmentError + | | +-- IOError + | | +-- OSError + | | +-- WindowsError (Windows) + | | +-- VMSError (VMS) + | +-- EOFError + | +-- ImportError + | +-- LookupError + | | +-- IndexError + | | +-- KeyError + | +-- MemoryError + | +-- NameError + | | +-- UnboundLocalError + | +-- ReferenceError + | +-- RuntimeError + | | +-- NotImplementedError + | +-- SyntaxError + | | +-- IndentationError + | | +-- TabError + | +-- SystemError + | +-- TypeError + | +-- ValueError + | +-- UnicodeError + | +-- UnicodeDecodeError + | +-- UnicodeEncodeError + | +-- UnicodeTranslateError + +-- Warning + +-- DeprecationWarning + +-- PendingDeprecationWarning + +-- RuntimeWarning + +-- SyntaxWarning + +-- UserWarning + +-- FutureWarning + +-- ImportWarning + +-- UnicodeWarning + +-- BytesWarning + +3.9. A note about assertion + +You can perform quick sanity check using assertion and the assert() function: + +assert x > 0, "x must be greater than zero" + +Nevertheless, asserts usage are not recommended because they are disabled if Python is started with the optimisation option -O. It should be used for debugging only. Note that you can use built-in value __debug__ to include debugging code. diff --git a/exercise1.md b/exercise1.md new file mode 100644 index 0000000..bccb6a7 --- /dev/null +++ b/exercise1.md @@ -0,0 +1,212 @@ + + +Question: Let's start with easy things first. What will the following code produce? +``` +a = 2 +a = 4 +a = 6 +print(a + a + a) +``` + +Question: What's wrong with the following script? +``` +a = 1 +_a = 2 +_a2 = 3 +2a = 4 +``` + + +Question: Executing the code will throw an error. Can you explain why? +``` +a = 1 +b = 2 +print(a == b) +print(b == c) +``` + + +Question: Fix the last line so that it outputs the sum of 1 and 2. +``` +a = "1" +b = 2 +print(a + b) + +Expected output: + +3 +``` + +Question: Complete the script so that it prints out the second item of the list. +``` +letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + +print[1] +Expected output: + +b +``` +Question: Please complete the script so that it prints out a list slice containing items d , e , and f . +``` +letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + +Expected output: + +['d', 'e', 'f'] +``` + +Question: List slicing is important in various data manipulation activities. Let's do a few more exercises on that. +``` +Please complete the script so that it prints out the first three items of list letters. + +letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + +Expected output: + +['a', 'b', 'c'] +``` +Question: Complete the script so that it prints out letter i using negative indexing. +``` +letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + +Expected output: + +i +``` + +Question: Complete the script so that it prints out a list slice containing the last three items of list letters . +``` +letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + +Expected output: + +['h', 'i', 'j'] +``` +Question: Complete the script so that it prints out a list slice containing letters a, c, e, g, and i. +``` +letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + +Expected output: + +['a', 'c', 'e', 'g', 'i'] +``` + + +Create a script that generates and prints a list of numbers from 1 to 20. Please do not create the list manually. + + +Question: Complete the script so that it produces the expected output. Please use my_range as input data. +``` +my_range = range(1, 21) + + Expected output: + +[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200] +``` + +Question: Complete the script so it generates the expected output using my_range as input data. Please note that the items of the expected list output are all strings. +``` +my_range = range(1, 21) + + Expected output: + +['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'] +``` + + +Question: Complete the script so that it removes duplicate items from list a . +``` +a = ["1", 1, "1", 2] + +Expected output: + + ['1', 2, 1] +``` +Question: Create a dictionary that contains the keys a and b and their respective values 1 and 2 . + + + +Question: Please complete the script so that it prints out the value of key b . + +d = {"a": 1, "b": 2} + +Expected output: + +2 +Question: Calculate the sum of the values of keys a and b . +``` +d = {"a": 1, "b": 2, "c": 3} + +Expected output: + +3 +``` + +correct or wrong ? +d = {"Name": "John", "Surname": "Smith"} +print(d["Smith"]) + +Question: Add a new pair of key (e.g. c ) and value (e.g. 3 ) to the dictionary and print out the new dictionary. +``` +d = {"a": 1, "b": 2} + +Expected output: + + {'a': 1, 'c': 3, 'b': 2} + +``` + + Question: Calculate the sum of all dictionary values. +``` +d = {"a": 1, "b": 2, "c": 3} + +Expected output: + + 6 +``` + + + Question: Filter the dictionary by removing all items with a value of greater than 1. +``` +d = {"a": 1, "b": 2, "c": 3} + +Expected output: + +{'a': 1} +``` + +Create a dictionary of keys a, b, c where each key has as value a list from 1 to 10, 11 to 20, and 21 to 30 respectively. Then print out the dictionary in a nice format. + +Question: Access the third value of key b from the dictionary. +``` +from pprint import pprint + +d = dict(a = list(range(1, 11)), b = list(range(11, 21)), c = list(range(21, 31))) + +Expected output: + +13 +``` + + +Question: Please complete the script so that it prints out the expected output. +``` +d = dict(a = list(range(1, 11)), b = list(range(11, 21)), c = list(range(21, 31))) + +Expected output: + +b has value [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +c has value [21, 22, 23, 24, 25, 26, 27, 28, 29, 30] +a has value [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +``` + + +Make a script that prints out letters of English alphabet from a to z, one letter per line in the terminal. + + + + + + + + diff --git a/func.md b/func.md new file mode 100644 index 0000000..d14b94d --- /dev/null +++ b/func.md @@ -0,0 +1,382 @@ +# Function +A function is a named sequence of statements +The `def` statement is used to define functions +It groups a block of code together so that we can call it by name. +It enables us to pass values into the the function when we call it. +It can returns a value (even if None). +When a function is called, it has its own namespace. Variables in the function are local to the function (and disappear when the function exits + +## Defining a function + +We use def keyword to define a function. General syntax is like +``` +def functionname(params): + statement1 + statement2 +``` +Example-1 + +``` +def print_lyrics(): + print("I'm a lumberjack, and I'm okay.") + print('I sleep all night and I work all day.') +``` + +### How do you call functions in Python? +Simply write the function's name followed by (), The syntax for calling the new function +``` +>>> print_lyrics() +I'm a lumberjack, and I'm okay. +I sleep all night and I work all day. +``` +Let us write a function which will take two integers as input and then return the sum. + +### Example-2 + +``` +>>> def sum(a, b): +... return a + b +``` +In the second line with the return keyword, we are sending back the value of a + b to the caller. You must call it like +``` +>>> res = sum(3, 5) +>>> res +8 +``` +### Example-3 + +``` +def function_name(arg1, arg2): + local_var1 = arg1 + 1 + local_var2 = arg2 * 2 + return local_var1 + local_var2 +``` +here is an example of calling this function: +``` +result = function_name(1, 2) +``` + +## Default argument value + +In a function variables may have default argument values, that means if we don’t give any value for that particular variable it will be assigned automatically. +``` +>>> def test(a , b=-99): +... if a > b: +... return True +... else: +... return False + +``` +output: + +``` +>>> test(12, 23) +False +>>> test(12) +True +``` + +Important + +Remember that you can not have an argument without default argument if you already have one argument with default values before it. Like f(a, b=90, c) is illegal as b is having a default value but after that c is not having any default value. + +## Keyword arguments + +``` +>>> def func(a, b=5, c=10): +... print('a is', a, 'and b is', b, 'and c is', c) +... +>>> func(12, 24) +a is 12 and b is 24 and c is 10 +>>> func(12, c = 24) +a is 12 and b is 5 and c is 24 +>>> func(b=12, c = 24, a = -1) +a is -1 and b is 12 and c is 24 + +``` +### Example-2 +``` +def sumsub(a, b, c=0, d=0): + return a - b + c - d +``` +output: +``` +>>> sumsub(12,4) +8 +>>> sumsub(12,4,27,23) +12 +>>> sumsub(12,4,d=27,c=23) +4 +``` + +## Docstrings + + * In Python we use docstrings to explain how to use the code, it will be useful in interactive mode and to create auto-documentation. + * Below we see an example of the docstring for a function called longest_side. + +``` +import math +def longest_side(a, b): + """ + Function to find the length of the longest side of a right triangle. + + :arg a: Side a of the triangle + :arg b: Side b of the triangle + + :return: Length of the longest side c as float + """ + return math.sqrt(a*a + b*b) +``` + +(Passing functions as arguments + +## Local Variables + + * When you declare variables inside a function definition, they are not related in any way to other variables with the same names used outside the function - i.e. variable names are local to the function. + * This is called the scope of the variable. All variables have the scope of the block they are declared in starting from the point of definition of the name. + +``` + x = 50 + + +def func(x): + print('x is', x) + x = 2 + print('Changed local x to', x) + + +func(x) +print('x is still', x) +``` + +Output: + +``` +$ python function_local.py +x is 50 +Changed local x to 2 +x is still 50 +``` + +## The global statement + +``` +x = 50 + + +def func(): + global x + + print('x is', x) + x = 2 + print('Changed global x to', x) + + +func() +print('Value of x is', x) + +``` +output: +``` +$ python function_global.py +x is 50 +Changed global x to 2 +Value of x is 2 + +``` + + +## args and keyword args + + Additional positional arguments passed to a function that are not specified in the function definition (the def: statement``), are collected in an argument preceded by a single asterisk `*args` . +``` +def print_args(*args): + print(arg) +``` +output: +``` +print_args("one", "two", "three") +print_args("one", "two", "three", "four") +``` + + Keyword arguments passed to a function that are not specified in the function definition can be collected in a dictionary and passed to an argument preceded by a double asterisk.`**kwargs` +``` +def print_kwargs(**kwargs): + print("%s: %s" % (k, v)) + +``` +output: +``` +print_kwargs(name="Jane", surname="Doe") +print_kwargs(age=10) +``` + +Eg:2 + +``` +def show_args(x, y=-1, *args, **kwargs): + print '-' * 40 + print 'x:', x + print 'y:', y + print 'args:', args + print 'kwargs:', kwargs + +def test(): + show_args(1) + show_args(x=2, y=3) + show_args(y=5, x=4) + show_args(4, 5, 6, 7, 8) + show_args(11, y=44, a=55, b=66) + +test() +``` + +output: + +``` +$ python workbook006.py +---------------------------------------- +x: 1 +y: -1 +args: () +kwargs: {} +---------------------------------------- +x: 2 +y: 3 +args: () +kwargs: {} +---------------------------------------- +x: 4 +y: 5 +args: () +kwargs: {} +---------------------------------------- +x: 4 +y: 5 +args: (6, 7, 8) +kwargs: {} +---------------------------------------- +x: 11 +y: 44 +args: () +kwargs: {'a': 55, 'b': 66} +``` +## lambda + * is anonymous (does not need a name) and + * contains only an expression and no statements. + +``` +a = lambda: 3 + +# is the same as + +def a(): + return 3 +``` + +eg-2: + +``` +>>>fn = lambda x, y, z: (x ** 2) + (y * 2) + z +>>>fn(4, 5, 6) +32 + +``` + +## Iterators & Generators + + +iterator + And iterator is something that satisfies the iterator protocol. Clue: If it's an iterator, you can use it in a for: statement. +The Iteration Protocol + + +generator + A generator is a class or function that implements an iterator, i.e. that implements the iterator protocol. +the iterator protocol + +* An object satisfies the iterator protocol if it does the following: + +* It implements a `__iter__` method, which returns an iterator object. +* It implements a next function, which returns the next item from the collection, sequence, stream, etc of items to be iterated over +* It raises the StopIteration exception when the items are exhausted and the `next()` method is called. + +The built-in function iter takes an iterable object and returns an iterator. +``` +>>> x = iter([1, 2, 3]) +>>> x + +>>> x.next() +1 +>>> x.next() +2 +>>> x.next() +3 +>>> x.next() +Traceback (most recent call last): + File "", line 1, in +StopIteration +``` + +## yield +* The yield statement enables us to write functions that are generators. Such functions may be similar to coroutines, since they may "yield" multiple times and are resumed. + +Generators simplifies creation of iterators. A generator is a function that produces a sequence of results instead of a single value. +``` +def yrange(n): + i = 0 + while i < n: + yield i + i += 1 +``` +Each time the yield statement is executed the function generates a new value. +``` +>>> y = yrange(3) +>>> y + +>>> y.next() +0 +>>> y.next() +1 +>>> y.next() +2 +>>> y.next() +Traceback (most recent call last): + File "", line 1, in +StopIteration +``` +Eg-2: + +``` +>>> def foo(): +... print "begin" +... for i in range(3): +... print "before yield", i +... yield i +... print "after yield", i +... print "end" +... +>>> f = foo() +>>> f.next() +begin +before yield 0 +0 +>>> f.next() +after yield 0 +before yield 1 +1 +>>> f.next() +after yield 1 +before yield 2 +2 +>>> f.next() +after yield 2 +end +Traceback (most recent call last): + File "", line 1, in +StopIteration +>>> +``` + + + diff --git a/index.md b/index.md index 86bd8c5..e12c9b4 100644 --- a/index.md +++ b/index.md @@ -1,33 +1,131 @@ # Python 1. Introductions - * Resources + * What is python * general description of Python * Interactive Python 2. Lexical matters * Lines * Comments * Names and tokens - * Doc strings + * Python Data Types * Whitespaces and indentation -3. Variables and Datatypes +3. Variables * what is Variables * Naming Variables * Keywords * Assign Multiple Variables - * Expressions and statements -4. Strings + * String operations + * Type Conversions +4. Operators and expressions + + * Operators + * Example of integer arithmetic + * Relational Operators + * Logical Operators + * Shorthand Operator + * Expressions + +5. Strings * what is a String ? * How to declare string ? + * Strings are immutable * Concatination * Reproduction + * The in operator * Formatting strings * Escape Characters * String Slicing - * Escape Characters - * Some useful built in Functions -5. Loops - * while -6. Condition - * if - * if-else -7. Function + +6. Conditional execution + * Boolean expres + * Logical operation + * If statement + * Conditional execu + * Chained condition + * Nested condition +7. Iteration + * while: statement + * Infinite loop + * continue and break statements + * the for loop +8. Collections + * List + * A list is a sequence + * Lists are mutable + * Traversing a list + * List operations + * List slices + * List methods + * Deleting elements + * Lists and functions + * Lists and strings + * Tuples + * Tuples are immutable + * Comparing tuples + * Tuple assignment + * Dictionaries + * Dictionaries syntax + * Dictionaries operations + * Dicionaries are mutable + * Dictionaries methods + * Deleting items +9. Exception Handling + * NameError + * TypeError + * How to handle exceptions? + * Raising exceptions + * Using finally for cleanup +10. Function + * The def statement + * Returning values + * Parameters + * Arguments + * Local variables + * Global variables and the global statement + * Doc strings for functions + * Decorators for functions + * lambda + * Iterators and generators +11. Files + * File opening + * Closing a file + * Reading a file + * Using the with statement + * Writing in a file +12. Modules + * Introduction + * Importing modules + * Submodules + * __all__ in __init__.py + * Default modules + * Module os + * Requests Module + * Command line arguments +13. Class + * A simple class + * Defining methods + * The constructor + * Member variables + * Calling methods + * Adding inheritance + * Class variables + * Class methods and static methods + * Properties + +15. Regular Expressions + * Defining regular expressions + * Compiling regular expressions + * Using regular expressions + * Using match objects to extract a value + * Extracting multiple items + * Replacing multiple items +16. Using Databases and SQL + * What is a database? + * Database concepts + * Creating a database table + * closing database + + + + + diff --git a/indexing.png b/indexing.png new file mode 100644 index 0000000..30dd3b1 Binary files /dev/null and b/indexing.png differ diff --git a/json.md b/json.md new file mode 100644 index 0000000..71983eb --- /dev/null +++ b/json.md @@ -0,0 +1,65 @@ +"JSON (pronounced 'Jason'), short for JavaScript Object Notation, is a lightweight computer data interchange format. It is a text-based, human-readable format for representing simple data structures and associative arrays (called objects)." +JSON can store Lists, bools, numbers, tuples and dictionaries. But to be saved into a file, all these structures must be reduced to strings. It is the string version that can be read or written to a file. Python has a JSON module that will help converting the datastructures to JSON strings. Use the import function to import the JSON module. + + +Functions +json.dump(obj, fileObj): Serializes obj as a JSON formatted stream to fileObj. +json.dumps(obj) : Serializes obj as JSON formatted string. +json.load(JSONfile) : De-serializes JSONfile to a Python object. +json.loads(JSONfile) : De-serializes JSONfile(type: string) to a Python object. + +Classes +JSONEncoder: An encoder class to convert Python objects to JSON format. +JSONDecoder: A decoder class to convert JSON format file into Python obj. + +The conversions are based on this conversion table. + + +json .load(f): Load json data from file + +json.loads(f): Load Josn data form stroing + +json.dump(j,f) :write json object to file + +json.dumps(j) : Json Object to String + + +import json + +The JSON module is mainly used to convert the python dictionary above into a JSON string that can be written into a file. + +json_string = json.dumps(datastore) + +The JSON module can also take a JSON string and convert it back to a dictionary structure: + +datastore = json.loads(json_string) + +While the JSON module will convert strings to Python datatypes, normally the JSON functions are used to read and write directly from JSON files. + + +Parsing JSON + +Take the following string containing JSON data: + +json_string = '{"first_name": "Guido", "last_name":"Rossum"}' + +It can be parsed like this: + +import json +parsed_json = json.loads(json_string) + +and can now be used as a normal dictionary: + +print(parsed_json['first_name']) +"Guido" + +You can also convert the following to JSON: + +d = { + 'first_name': 'Guido', + 'second_name': 'Rossum', + 'titles': ['BDFL', 'Developer'], +} + +print(json.dumps(d)) +'{"first_name": "Guido", "last_name": "Rossum", "titles": ["BDFL", "De diff --git a/logging.txt b/logging.txt new file mode 100644 index 0000000..406db5f --- /dev/null +++ b/logging.txt @@ -0,0 +1,11 @@ +import logging +log = logging.getLogger(__name__) + + + log.debug('debug message') + log.info('info message') + log.warning('warning message') + log.error('error message') + log.critical('critical message') + log.log('log message') + log.exception('exception message') diff --git a/mit-materials/MIT6_0001F16_Lec1.pdf b/mit-materials/MIT6_0001F16_Lec1.pdf new file mode 100644 index 0000000..a5a794a Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec1.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec11.pdf b/mit-materials/MIT6_0001F16_Lec11.pdf new file mode 100644 index 0000000..a128442 Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec11.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec12.pdf b/mit-materials/MIT6_0001F16_Lec12.pdf new file mode 100644 index 0000000..0671ffc Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec12.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec2.pdf b/mit-materials/MIT6_0001F16_Lec2.pdf new file mode 100644 index 0000000..19c4f80 Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec2.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec3.pdf b/mit-materials/MIT6_0001F16_Lec3.pdf new file mode 100644 index 0000000..e8a1d0d Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec3.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec4.pdf b/mit-materials/MIT6_0001F16_Lec4.pdf new file mode 100644 index 0000000..ecb0931 Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec4.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec5(1).pdf b/mit-materials/MIT6_0001F16_Lec5(1).pdf new file mode 100644 index 0000000..632b819 Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec5(1).pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec5.pdf b/mit-materials/MIT6_0001F16_Lec5.pdf new file mode 100644 index 0000000..632b819 Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec5.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec6.pdf b/mit-materials/MIT6_0001F16_Lec6.pdf new file mode 100644 index 0000000..99ff9cc Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec6.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec7.pdf b/mit-materials/MIT6_0001F16_Lec7.pdf new file mode 100644 index 0000000..be4a27d Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec7.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec8.pdf b/mit-materials/MIT6_0001F16_Lec8.pdf new file mode 100644 index 0000000..ad17db0 Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec8.pdf differ diff --git a/mit-materials/MIT6_0001F16_Lec9.pdf b/mit-materials/MIT6_0001F16_Lec9.pdf new file mode 100644 index 0000000..01e519b Binary files /dev/null and b/mit-materials/MIT6_0001F16_Lec9.pdf differ diff --git a/modulue.md b/modulue.md new file mode 100644 index 0000000..091b76a --- /dev/null +++ b/modulue.md @@ -0,0 +1,86 @@ +# Modules +Modules are reusable libraries of code in Python. Python comes with many standard library modules. + +A module is imported using the import statement. + +``` +>>> import time +>>> print time.asctime() +'Fri Mar 30 12:59:21 2012' +``` + +In this example, we’ve imported the time module and called the asctime function from that module, which returns current time as a string. + +There is also another way to use the import statement. +``` +>>> from time import asctime +>>> asctime() +'Fri Mar 30 13:01:37 2012' +``` +Here were imported just the asctime function from the time module. + + +Writing our own modules is very simple. + +For example, create a file called num.py with the following content. +``` +def square(x): + return x * x + +def cube(x): + return x * x * x +``` + +``` + +>>> import num +>>> num.square(3) +9 +>>> num.cube(3) +27 +``` + +``` +""" +Bars Module +============ +This is an example module with provide different ways to print bars. +""" + +def starbar(num): + """Prints a bar with * + + :arg num: Length of the bar + """ + print('*' * num) + +def hashbar(num): + """Prints a bar with # + + :arg num: Length of the bar + """ + print('#' * num) + +def simplebar(num): + """Prints a bar with - + + :arg num: Length of the bar + """ + print('-' * num) + + + +>>> import bars +>>> +>>> bars.hashbar(10) +########## +>>> bars.simplebar(10) +---------- +>>> bars.starbar(10) +********** +``` + + + + + diff --git a/oop/01-encapsulation-1.py b/oop/01-encapsulation-1.py new file mode 100644 index 0000000..7954dac --- /dev/null +++ b/oop/01-encapsulation-1.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# encapsulation-1.py + +# Encapsulation means to preserve data in classes using methods +# Here, we're setting the 'val' attribute through 'set_val()'. +# See the next example, `encapsulation-2.py` for more info + +# In this example, we have two methods, `set_val` and `get_val`. +# The first one sets the `val` value while the second one +# prints/returns the value. + + +class MyClass(object): + + def set_val(self, val): + self.value = val + + def get_val(self): + print(self.value) + return self.value + +a = MyClass() +b = MyClass() + +a.set_val(10) +b.set_val(100) + +a.get_val() +b.get_val() + +# NOTE: If you run this code, it won't print anything to the screen. +# This is because, even if we're calling `a.get_val()` and `b.get_val()`, +# the `get_val()` function doesn't contain a `print()` function. +# If we want to get the output printed to screen, we should do any of +# the following: + +# a) Either replace `return self.value` with `print(self.value)` +# or add a print statement **above** `return` as `print(self.value)`. + +# b) Remove `return(self.value)` and replace it with `print(self.value)` \ No newline at end of file diff --git a/oop/02-encapsulation-2.py b/oop/02-encapsulation-2.py new file mode 100644 index 0000000..4b6e824 --- /dev/null +++ b/oop/02-encapsulation-2.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# encapsulation-2.py + +# This example builds on top of `encapsulation-1.py`. +# Here we see how we can set values in methods without +# going through the method itself, ie.. how we can break +# encapsulation. + +# NOTE: BREAKING ENCAPSULATION IS BAD. + + +class MyClass(object): + def set_val(self, val): + self.value = val + + def get_val(self): + print(self.value) + +a = MyClass() +b = MyClass() + +a.set_val(10) +b.set_val(1000) +a.value = 100 # <== Overriding `set_value` directly +# <== ie.. Breaking encapsulation + +a.get_val() +b.get_val() diff --git a/oop/03-encapsulation-3.py b/oop/03-encapsulation-3.py new file mode 100644 index 0000000..5411ee0 --- /dev/null +++ b/oop/03-encapsulation-3.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# 03-encapsulation-3.py + +# Here we look at another example, where we have three methods +# set_val(), get_val(), and increment_val(). + +# set_val() helps to set a value, get_val() prints the value, +# and increment_val() increments the set value by 1. + +# We can still break encapsulation here by calling 'self.value' +# directly in an instance, which is **BAD**. + +# set_val() forces us to input an integer, ie.. what the code wants +# to work properly. Here, it's possible to break the encapsulation by +# calling 'self.val` directly, which will cause unexpected results later. +# In this example, the code is written to enforce an intger as input, if we +# don't break encapsulation and go through the gateway 'set_val()' + +# + + +class MyInteger(object): + def set_val(self, val): + try: + val = int(val) + except ValueError: + return + self.val = val + + def get_val(self): + print(self.val) + + def increment_val(self): + self.val = self.val + 1 + print(self.val) + +a = MyInteger() +a.set_val(10) +a.get_val() +a.increment_val() +print("\n") + +# Trying to break encapsulation in a new instance with an int +c = MyInteger() +c.val = 15 +c.get_val() +c.increment_val() +print("\n") + +# Trying to break encapsulation in a new instance with a str +b = MyInteger() +b.val = "MyString" # <== Breaking encapsulation, works fine +b.get_val() # <== Prints the val set by breaking encap +b.increment_val() # This will fail, since str + int wont work +print("\n") diff --git a/oop/04-init_constructor-1.py b/oop/04-init_constructor-1.py new file mode 100644 index 0000000..12e1fb4 --- /dev/null +++ b/oop/04-init_constructor-1.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +# 04-init_constructor.py + +# __init__() is a constructor method which helps to +# set initial values while instatiating a class. + +# __init__() will get called with the attributes set in __init__(), +# when a class is instantiated. + +# The '__' before and after the method name denotes that +# the method is private. It's called private or magic methods +# since it's called internally and automatically. + + +class MyNum(object): + def __init__(self): + print("Calling the __init__() constructor!\n") + self.val = 0 + + def increment(self): + self.val = self.val + 1 + print(self.val) + +dd = MyNum() +dd.increment() # will print 1 +dd.increment() # will print 2 diff --git a/oop/05-init_constructor-2.py b/oop/05-init_constructor-2.py new file mode 100644 index 0000000..8207765 --- /dev/null +++ b/oop/05-init_constructor-2.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +# 05-init_constructor-2.py + +# We add a test in the __init__() constructor to check +# if 'value' is an int or not. + + +class MyNum(object): + def __init__(self, value): + try: + value = int(value) + except ValueError: + value = 0 + self.value = value + + def increment(self): + self.value = self.value + 1 + print(self.value) + + +a = MyNum(10) +a.increment() # This should print 11 +a.increment() # This should print 12 diff --git a/oop/06-class-attributes-1.py b/oop/06-class-attributes-1.py new file mode 100644 index 0000000..a9737de --- /dev/null +++ b/oop/06-class-attributes-1.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python2.7 + +# 06-class-attributes-1.py + +# Here we define an attribute under the class `YourClass` +# as well as an attribute within the function. + +# The attribute defined in the class is called `class attributes` +# and the attribute defined in the function is called `instance attributes`. + + +class YourClass(object): + classy = 10 + + def set_val(self): + self.insty = 100 + +dd = YourClass() +dd.classy # This will fetch the class attribute 10. +dd.insty # This will fetch the instance attribute 100. + +# Once `dd` is instantiated, we can access both the class and instance +# attributes, ie.. dd.classy and dd.insty. diff --git a/oop/07-class-attributes-2.py b/oop/07-class-attributes-2.py new file mode 100644 index 0000000..7a6e4d5 --- /dev/null +++ b/oop/07-class-attributes-2.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# 07-class-attributes-2.py + +# The code below shows two important points: + +# a) A class attribute can be overridden in an instance, even +# though it is bad due to breaking Encapsulation. + +# b) There is a lookup path for attributes in Python. The first being +# the method defined within the class, and then the class above it. + +# We are overriding the 'classy' class attribute in the instance 'dd'. +# When it's overridden, the python interpreter reads the overridden value. +# But once the new value is deleted with 'del', the overridden value is no longer +# present in the instance, and hence the lookup goes a level above and gets it from +# the class. + + +class YourClass(object): + classy = "class value" + +dd = YourClass() +print(dd.classy) # < This should return the string "class value" + +dd.classy = "Instance value" +print(dd.classy) # This should return the string "Instance value" + +# This will delete the value set for 'dd.classy' in the instance. +del dd.classy +# Since the overriding attribute was deleted, this will print 'class value'. +print(dd.classy) diff --git a/oop/08-class-instance-attributes-1.py b/oop/08-class-instance-attributes-1.py new file mode 100644 index 0000000..c0e4cfa --- /dev/null +++ b/oop/08-class-instance-attributes-1.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# 08-class-instance-attributes-1.py + +# This code shows that an Instance can access it's own +# attributes as well as Class attributes. + +# We have a class attribute named 'count', and we add 1 to +# it each time we create an instance. This can help count the +# number of instances at the time of instantiation. + + +class InstanceCounter(object): + count = 0 + + def __init__(self, val): + self.val = val + InstanceCounter.count += 1 + + def set_val(self, newval): + self.val = newval + + def get_val(self): + print(self.val) + + def get_count(self): + print(InstanceCounter.count) + +a = InstanceCounter(5) +b = InstanceCounter(10) +c = InstanceCounter(15) + +for obj in (a, b, c): + print("value of obj: %s" % obj.get_val()) + print("Count : %s" % obj.get_count()) diff --git a/oop/09-inheritance-1.py b/oop/09-inheritance-1.py new file mode 100644 index 0000000..d60a0ce --- /dev/null +++ b/oop/09-inheritance-1.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# 09-inheritance-1.py + +# The code below shows how a class can inherit from another class. +# We have two classes, `Date` and `Time`. Here `Time` inherits from +# `Date`. + +# Any class inheriting from another class (also called a Parent class) +# inherits the methods and attributes from the Parent class. + +# Hence, any instances created from the class `Time` can access +# the methods defined in the parent class `Date`. + + +class Date(object): + def get_date(self): + print("2016-05-14") + + +class Time(Date): + def get_time(self): + print("07:00:00") + +# Creating an instance from `Date` +dt = Date() +dt.get_date() # Accesing the `get_date()` method of `Date` +print("--------") + +# Creating an instance from `Time`. +tm = Time() +tm.get_time() # Accessing the `get_time()` method from `Time`. +# Accessing the `get_date() which is defined in the parent class `Date`. +tm.get_date() diff --git a/oop/10-inheritance-2.py b/oop/10-inheritance-2.py new file mode 100644 index 0000000..708bf24 --- /dev/null +++ b/oop/10-inheritance-2.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +# 10-inheritance-2.py + +# The code below shows another example of inheritance +# Dog and Cat are two classes which inherits from Animal. +# This an instance created from Dog or Cat can access the methods +# in the Animal class, ie.. eat(). + +# The instance of 'Dog' can access the methods of the Dog class +# and it's parent class 'Animal'. + +# The instance of 'Cat' can access the methods of the Cat class +# and it's parent class 'Animal'. + +# But the instance created from 'Cat' cannot access the attributes +# within the 'Dog' class, and vice versa. + +class Animal(object): + def __init__(self, name): + self.name = name + + def eat(self, food): + print("%s is eating %s" % (self.name, food)) + +class Dog(Animal): + def fetch(self, thing): + print("%s goes after the %s" % (self.name, thing)) + +class Cat(Animal): + def swatstring(self): + print("%s shred the string!" % self.name) + +d = Dog("Roger") +c = Cat("Fluffy") + +d.fetch("paper") +d.eat("dog food") +print("--------") +c.eat("cat food") +c.swatstring() + +# The below methods would fail, since the instances doesn't have +# have access to the other class. + +c.fetch("frizbee") +d.swatstring() \ No newline at end of file diff --git a/oop/11-polymorphism-1.py b/oop/11-polymorphism-1.py new file mode 100644 index 0000000..a695822 --- /dev/null +++ b/oop/11-polymorphism-1.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +# 11-polymorphism-1.py + +# Polymorphism means having the same interface/attributes in different +# classes. + +# Polymorphism is the characteristic of being able to assign +# a different meaning or usage in different contexts. +# A not-so-clear/clean example is, different classes can have +# the same function name. + +# Here, the class Dog and Cat has the same method named 'show_affection' +# Even if they are same, both does different actions in the instance. +# +# Since the order of the lookup is +# 'instance' -> 'class' -> 'parent class', even if the +# 'class' and 'parent class' has functions with the same name, +# the instance will only pick up the first hit, +# ie.. from the 'class' and won't go to the parent class. + + +class Animal(object): + + def __init__(self, name): + self.name = name + + def eat(self, food): + print('{0} eats {1}'.format(self.name, food)) + + +class Dog(Animal): + + def fetch(self, thing): + print('{0} goes after the {1}!'.format(self.name, thing)) + + def show_affection(self): + print('{0} wags tail'.format(self.name)) + + +class Cat(Animal): + + def swatstring(self): + print('{0} shreds more string'.format(self.name)) + + def show_affection(self): + print('{0} purrs'.format(self.name)) + +for a in (Dog('Rover'), Cat('Fluffy'), Cat('Lucky'), Dog('Scout')): + a.show_affection() diff --git a/oop/12-polymorphism-2.py b/oop/12-polymorphism-2.py new file mode 100644 index 0000000..80eddbb --- /dev/null +++ b/oop/12-polymorphism-2.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +# 12-polymorphism-2.py + +# Another example for Polymorphism are the several inbuilt +# functions in Python. Take for example, the builtin function +# called 'len'. + +# 'len' is available for almost all types, such as strings, +# ints, floats, dictionaries, lists, tuples etc.. +# When len is called on a type, it actually calls the inbuilts +# private function 'len' on that type or __len__ + +# Every object type that supports 'len' will have a private +# 'len' function inbuilt. + +# Hence, for example, a list type already has a 'len()' +# function inbuilt in the Python code, and when you run the +# len() function on the data type, it checks if the len +# private function is available for that type or not. +# If it is available, it runs that. + +text = ["Hello", "Hola", "helo"] +print(len(text)) + +print(len("Hello")) +print(len({'a': 1, 'b': 2, 'c': 3})) diff --git a/oop/13-inheriting-init-constructor-1.py b/oop/13-inheriting-init-constructor-1.py new file mode 100644 index 0000000..1650fd5 --- /dev/null +++ b/oop/13-inheriting-init-constructor-1.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +# 13-inheriting-init-constructor-1.py + +# This is a normal inheritance example from which we build +# the next example. Make sure to read and understand the +# next example '14-inheriting-init-constructor-2.py'. + + +class Animal(object): + def __init__(self, name): + self.name = name + + +class Dog(Animal): + def fetch(self, thing): + print('%s goes after the %s' % (self.name, thing)) + +d = Dog("Roger") +print "The dog's name is", d.name +d.fetch("frizbee") diff --git a/oop/14-multiple-inheritance-1.py b/oop/14-multiple-inheritance-1.py new file mode 100644 index 0000000..5d32afd --- /dev/null +++ b/oop/14-multiple-inheritance-1.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# 14-multiple-inheritance-1.py + +# Python supports multiple inheritance and uses a depth-first order +# when searching for methods. +# This search pattern is call MRO (Method Resolution Order) + +# This is the first example, which shows the lookup of a common +# function named 'dothis()', which we'll continue in other examples. + +# As per the MRO output, it starts in class D, then B, A, and lastly C. + +# Both A and C contains 'dothis()'. Let's trace how the lookup happens. + +# As per the MRO output, it starts in class D, then B, A, and lastly C. + +# class `A` defines 'dothis()' and the search ends there. It doesn't go to C. + +# The MRO will show the full resolution path even if the full path is +# not traversed. + +# The method lookup flow in this case is : D -> B -> A -> C + + +class A(object): + + def dothis(self): + print("doing this in A") + + +class B(A): + pass + + +class C(object): + def dothis(self): + print("doing this in C") + + +class D(B, C): + pass + +d_instance = D() +d_instance.dothis() # <== This should print from class A. + +print("\nPrint the Method Resolution Order") +print(D.mro()) diff --git a/oop/15-multiple-inheritance-2.py b/oop/15-multiple-inheritance-2.py new file mode 100644 index 0000000..01d561f --- /dev/null +++ b/oop/15-multiple-inheritance-2.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +# 15-multiple-inheritance-2.py + +# Python supports multiple inheritance + +# It uses a depth-first order when searching for methods. +# This search pattern is call MRO (Method Resolution Order) + +# This is a second example, which shows the lookup of 'dothis()'. +# Both A and C contains 'dothis()'. Let's trace how the lookup happens. + +# As per the MRO output using depth-first search, +# it starts in class D, then B, A, and lastly C. + +# Here we're looking for 'dothis()' which is defined in class `C`. +# The lookup goes from D -> B -> A -> C. + +# Since class `A` doesn't have `dothis()`, the lookup goes back to class `C` +# and finds it there. + + +class A(object): + + def dothat(self): + print("Doing this in A") + + +class B(A): + pass + + +class C(object): + + def dothis(self): + print("\nDoing this in C") + + +class D(B, C): + """Multiple Inheritance, + D inheriting from both B and C""" + pass + +d_instance = D() + +d_instance.dothis() + +print("\nPrint the Method Resolution Order") +print(D.mro()) diff --git a/oop/16-multiple-inheritance-3.py b/oop/16-multiple-inheritance-3.py new file mode 100644 index 0000000..036b875 --- /dev/null +++ b/oop/16-multiple-inheritance-3.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +# 16-multiple-inheritance-3.py + +# Python supports multiple inheritance +# and uses a depth-first order when searching for methods. +# This search pattern is call MRO (Method Resolution Order) + +# Example for "Diamond Shape" inheritance +# Lookup can get complicated when multiple classes inherit +# from multiple parent classes. + +# In order to avoid ambiguity while doing a lookup for a method +# in various classes, from Python 2.3, the MRO lookup order has an +# additional feature. + +# It still does a depth-first lookup, but if the occurrence of a class +# happens multiple times in the MRO path, it removes the initial occurrence +# and keeps the latter. + +# In the example below, class `D` inherits from `B` and `C`. +# And both `B` and `C` inherits from `A`. +# Both `A` and `C` has the method `dothis()`. + +# We instantiate `D` and requests the 'dothis()' method. +# By default, the lookup should go D -> B -> A -> C -> A. +# But from Python 2.3, in order to reduce the lookup time, +# the MRO skips the classes which occur multiple times in the path. + +# Hence the lookup will be D -> B -> C -> A. + + +class A(object): + + def dothis(self): + print("doing this in A") + + +class B(A): + pass + + +class C(A): + def dothis(self): + print("doing this in C") + + +class D(B, C): + pass + +d_instance = D() +d_instance.dothis() + +print("\nPrint the Method Resolution Order") +print(D.mro()) diff --git a/oop/17-instance_methods-1.py b/oop/17-instance_methods-1.py new file mode 100644 index 0000000..179f6b1 --- /dev/null +++ b/oop/17-instance_methods-1.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +# 17-instance_methods-1.py + +# Instance methods are also known as Bound methods since the methods +# within a class are bound to the instance created from the class, via +# 'self'. + + +class A(object): + def method(*argv): + return argv + +a = A() +print(a.method) + +# The print() function will print the following : +# python 17-instance_methods-1.py +# > + +# The output shows that 'method' is a bound method diff --git a/oop/18-instance_methods-2.py b/oop/18-instance_methods-2.py new file mode 100644 index 0000000..572e279 --- /dev/null +++ b/oop/18-instance_methods-2.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +# 18-instance_methods.py + +# Instance methods are the normal way of accessing methods, seen in all +# classes till now. ie.. by instantiating instances from a class, and +# access the methods within the class. The usage of `self` is very +# important in instance methods due to `self` being a hook/handle to the +# instance itself, or the instance itself. + +# We look into a previous example, once more, to understand `Instance methods`. + +# We have an __init__() constructor, and three methods within the +# `InstanceCounter` class. + +# Three instances a, b, and c are created from the class `InstanceCounter`. + +# Since the methods defined in the class are accessed through the +# instances 'a', 'b', and 'c', these methods are called 'Instance +# methods'. + +# Since the instance is bound to the methods defined in the class by the +# keyword `self`, we also call `Instance methods` as 'Bound methods'. + +# In the code below, the instance is `obj` (the iterator) and we access +# each method as `obj.set_val()`, `obj.get_val()`, and `obj.get_count`. + + +class InstanceCounter(object): + count = 0 + + def __init__(self, val): + self.val = val + InstanceCounter.count += 1 + + def set_val(self, newval): + self.val = newval + + def get_val(self): + return self.val + + def get_count(self): + return InstanceCounter.count + +a = InstanceCounter(5) +b = InstanceCounter(10) +c = InstanceCounter(15) + +for obj in (a, b, c): + print("Value of object: %s" % (obj.get_val)) + print("Count : %s " % (obj.get_count)) diff --git a/oop/19-decorators-1.py b/oop/19-decorators-1.py new file mode 100644 index 0000000..9a9244c --- /dev/null +++ b/oop/19-decorators-1.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +# 19-decorators-1.py +# Decorators, as simple as it gets :) + +# Reference: Decorators 101 - A Gentle Introduction to Functional Programming. +# By Jillian Munson - PyGotham 2014. +# https://www.youtube.com/watch?v=yW0cK3IxlHc + +# Decorators are functions that compliment other functions, +# or in other words, modify a function or method. + +# In the example below, we have a function named `decorated`. +# This function just prints "This happened". +# We have a decorator created named `inner_decorator()`. +# This decorator function has an function within, which +# does some operations (print stuff for simplicity) and then +# returns the return-value of the internal function. + +# How does it work? +# a) The function `decorated()` gets called. +# b) Since the decorator `@my_decorator` is defined above +# `decorated()`, `my_decorator()` gets called. +# c) my_decorator() takes a function name as args, and hence `decorated()` +# gets passed as the arg. +# d) `my_decorator()` does it's job, and when it reaches `myfunction()` +# calls the actual function, ie.. decorated() +# e) Once the function `decorated()` is done, it gets back to `my_decorator()`. +# f) Hence, using a decorator can drastically change the behavior of the +# function you're actually executing. + + +def my_decorator(my_function): # <-- (4) + def inner_decorator(): # <-- (5) + print("This happened before!") # <-- (6) + my_function() # <-- (7) + print("This happens after ") # <-- (10) + print("This happened at the end!") # <-- (11) + return inner_decorator + # return None + + +@my_decorator # <-- (3) +def my_decorated(): # <-- (2) <-- (8) + print("This happened!") # <-- (9) + +if __name__ == '__main__': + my_decorated() # <-- (1) + +# This prints: +# # python 19-decorators-1.py +# This happened before! +# This happened! +# This happens after +# This happened at the end! diff --git a/oop/20-decorators-2.py b/oop/20-decorators-2.py new file mode 100644 index 0000000..9d3e8e1 --- /dev/null +++ b/oop/20-decorators-2.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# Reference: Decorators 101 - A Gentle Introduction to Functional Programming. +# By Jillian Munson - PyGotham 2014. +# https://www.youtube.com/watch?v=yW0cK3IxlHc + +# 20-decorators-2.py +# An updated version of 19-decorators-1.py + +# This code snippet takes the previous example, and add a bit more information +# to the output. + +import datetime + + +def my_decorator(inner): + def inner_decorator(): + print(datetime.datetime.utcnow()) + inner() + print(datetime.datetime.utcnow()) + return inner_decorator + + +@my_decorator +def decorated(): + print("This happened!") + +if __name__ == "__main__": + decorated() + +# This will print: (NOTE: The time will change of course :P) +# # python 20-decorators-2.py +# 2016-05-29 11:46:07.444330 +# This happened! +# 2016-05-29 11:46:07.444367 diff --git a/oop/21-decorators-3.py b/oop/21-decorators-3.py new file mode 100644 index 0000000..d7b4899 --- /dev/null +++ b/oop/21-decorators-3.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +# Reference: Decorators 101 - A Gentle Introduction to Functional Programming. +# By Jillian Munson - PyGotham 2014. +# https://www.youtube.com/watch?v=yW0cK3IxlHc + +# This is an updated version of 20-decorators-2.py. +# Here, the `decorated()` function takes an argument +# and prints it back on terminal. + +# When the decorator `@my_decorator` is called, it +# takes the function `decorated()` as its argument, and +# the argument of `decorated()` as the argument of `inner_decorator()`. +# Hence the arg `number` is passed to `num_copy`. + +import datetime + + +def my_decorator(inner): + def inner_decorator(num_copy): + print(datetime.datetime.utcnow()) + inner(int(num_copy) + 1) + print(datetime.datetime.utcnow()) + return inner_decorator + + +@my_decorator +def decorated(number): + print("This happened : " + str(number)) + +if __name__ == "__main__": + decorated(5) + +# This prints: +# python 21-decorators-3.py +# 2016-05-29 12:11:57.212125 +# This happened : 6 +# 2016-05-29 12:11:57.212168 diff --git a/oop/22-decorators-4.py b/oop/22-decorators-4.py new file mode 100644 index 0000000..9399fd3 --- /dev/null +++ b/oop/22-decorators-4.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Reference: Decorators 101 - A Gentle Introduction to Functional Programming. +# By Jillian Munson - PyGotham 2014. +# https://www.youtube.com/watch?v=yW0cK3IxlHc + +# 22-decorators-4.py + +# This example builds on the previous decorator examples. +# The previous example, 21-decorators-3.py showed how to +# deal with one argument passed to the function. + +# This example shows how we can deal with multiple args. + +# Reminder : `args` is a list of arguments passed, while +# kwargs is a dictionary passed as arguments. + + +def decorator(inner): + def inner_decorator(*args, **kwargs): + print(args, kwargs) + return inner_decorator + + +@decorator +def decorated(string_args): + print("This happened : " + string_args) + +if __name__ == "__main__": + decorated("Hello, how are you?") + +# This prints : +# # python 22-decorators-4.py +# ('Hello, how are you?',) diff --git a/oop/23-decorators-5.py b/oop/23-decorators-5.py new file mode 100644 index 0000000..eed3a74 --- /dev/null +++ b/oop/23-decorators-5.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +# 23-decorators-5.py + +# Reference : https://www.youtube.com/watch?v=bxhuLgybIro + +from __future__ import print_function + + +# 2. Decorator function +def handle_exceptions(func_name): + def inner(*args, **kwargs): + try: + return func_name(*args, **kwargs) + except Exception: + print("An exception was thrown : ", Exception) + return inner + + +# 1. Main function +@handle_exceptions +def divide(x, y): + return x / y + +print(divide(8, 0)) diff --git a/oop/24-decorators-6.py b/oop/24-decorators-6.py new file mode 100644 index 0000000..9519059 --- /dev/null +++ b/oop/24-decorators-6.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + + +def decorator(inner): + def inner_decorator(*args, **kwargs): + print("This function takes " + str(len(args)) + " arguments") + inner(*args) + return inner_decorator + + +@decorator +def decorated(string_args): + print("This happened: " + str(string_args)) + + +@decorator +def alsoDecorated(num1, num2): + print("Sum of " + str(num1) + "and" + str(num2) + ": " + str(num1 + num2)) + + +if __name__ == "__main__": + decorated("Hello") + alsoDecorated(1, 2) diff --git a/oop/25-decorators-7.py b/oop/25-decorators-7.py new file mode 100644 index 0000000..b41569d --- /dev/null +++ b/oop/25-decorators-7.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# 25-decorators-7.py + +# Reference https://www.youtube.com/watch?v=Slf1b3yUocc + +# We have two functions, one which adds two numbers, +# and another which subtracts two numbers. + +# We apply the decorator @double which takes in the +# functions that is called with the decorator, and doubles +# the output of the respective function. + + +def double(my_func): + def inner_func(a, b): + return 2 * my_func(a, b) + return inner_func + + +@double +def adder(a, b): + return a + b + + +@double +def subtractor(a, b): + return a - b + + +print(adder(10, 20)) +print(subtractor(6, 1)) diff --git a/oop/26-class-decorators.py b/oop/26-class-decorators.py new file mode 100644 index 0000000..ba7cfd8 --- /dev/null +++ b/oop/26-class-decorators.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# 26-class-decorators.py + +# Reference : https://www.youtube.com/watch?v=Slf1b3yUocc +# Talk by Mike Burns + +# Till the previous examples, we saw function decorators. +# But decorators can be applied to Classes as well. +# This example deals with class decorators. + +# NOTE: If you are creating a decorator for a class, you'll it +# to return a Class. + +# NOTE: Similarly, if you are creating a decorator for a function, +# you'll need it to return a function. + + +def honirific(cls): + class HonirificCls(cls): + def full_name(self): + return "Dr. " + super(HonirificCls, self).full_name() + return HonirificCls + + +@honirific +class Name(object): + def __init__(self, first_name, last_name): + self.first_name = first_name + self.last_name = last_name + + def full_name(self): + return " ".join([self.first_name, self.last_name]) + +result = Name("Vimal", "A.R").full_name() +print("Full name: {0}".format(result)) + + +# This needs further check. Erroring out. diff --git a/oop/27-classmethod-1.py b/oop/27-classmethod-1.py new file mode 100644 index 0000000..37b4040 --- /dev/null +++ b/oop/27-classmethod-1.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +# 19-class_methods-1.py + +# A classmethod is an inbuilt decorator which is called on functions via +# @classmethod. + +# The @classmethod decorator marks the function/method as bound to the +# class and not to an instance. + +# Remember that we used 'self' in a function within a class, which denoted +# the instance. In class methods, we use `cls` which denotes the class +# rather than the instance. + +# The following example is a very simple explanation of class-methods. + +# class_1() is a class method while class_2() is an instance method. + +# Class methods can be accessed by the class as well as the instance. + +# Instance methods can only be accessed by the Instance. That's why in this example, MyClass.class_2() will fail with an error. + +# NOTE : For the class MyClass: +# MyClass is the class itself +# MyClass() is an instance + + +class MyClass(object): + @classmethod + def class_1(cls): + print("Class method 1") + + def class_2(self): + print("Self/Instance method 1") + + +print("Calling the class `MyClass` directly without an instance:") +MyClass.class_1() +# MyClass.class_2() + +# NOTE: You will want to comment `MyClass.class_2()` once you hit the `TypeError` +# to continue with the examples below. + +print("\nCalling the instance `MyClass()`:") +MyClass().class_1() +MyClass().class_2() diff --git a/oop/28-classmethod-2.py b/oop/28-classmethod-2.py new file mode 100644 index 0000000..b557f8d --- /dev/null +++ b/oop/28-classmethod-2.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# 28-classmethod-2.py + +# Reference: https://jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/ + +# Classmethods are decorators which are inbuilt in Python. +# We decorate a function as a classemethod using the decorator +# @classmethod. + +# Class methods are used for functions which need not be +# called via an instance. Certain use cases may be: + +# a) Creating instances take resources, hence the methods/functions +# which need necessarily + + +class MyClass(object): + count = 0 + + def __init__(self, val): + self.val = val + MyClass.count += 1 + + def set_val(self, newval): + self.val = newval + + def get_val(self): + return self.val + + @classmethod + def get_count(cls): + return cls.count + +object_1 = MyClass(10) +print("\nValue of object : %s" % object_1.get_val()) +print(MyClass.get_count()) + +object_2 = MyClass(20) +print("\nValue of object : %s" % object_2.get_val()) +print(MyClass.get_count()) + +object_3 = MyClass(40) +print("\nValue of object : %s" % object_3.get_val()) +print(MyClass.get_count()) diff --git a/oop/29-staticmethod-1.py b/oop/29-staticmethod-1.py new file mode 100644 index 0000000..d069f99 --- /dev/null +++ b/oop/29-staticmethod-1.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# 29-staticmethod-1.py + +""" +# Refer https://arvimal.wordpress.com/2016/06/12/instance-class-static-methods-object-oriented-programming/ +# a) Static methods are functions methods which doesn’t need a binding +# to a class or an instance. +# b) Static methods, as well as Class methods, don’t require an instance +# to be called. +# c) Static methods doesn’t need self or cls as the first argument +# since it’s not bound to an instance or class. +# d) Static methods are normal functions, but within a class. +# e) Static methods are defined with the keyword @staticmethod +# above the function/method. +# f) Static methods are usually used to work on Class Attributes. +""" + + +class MyClass(object): + count = 0 + + def __init__(self, val): + self.val = self.filterint(val) + MyClass.count += 1 + + @staticmethod + def filterint(value): + if not isinstance(value, int): + print("Entered value is not an INT, value set to 0") + return 0 + else: + return value + + +a = MyClass(5) +b = MyClass(10) +c = MyClass(15) + +print(a.val) +print(b.val) +print(c.val) +print(a.filterint(100)) diff --git a/oop/30-staticmethod-2.py b/oop/30-staticmethod-2.py new file mode 100644 index 0000000..f36685c --- /dev/null +++ b/oop/30-staticmethod-2.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +from __future__ import print_function + +# 30-staticmethod-2.py + +# Refer +# https://arvimal.wordpress.com/2016/06/12/instance-class-static-methods-object-oriented-programming/ + +# Static methods are functions/methods which doesn’t need a binding to a class or an instance. +# Static methods, as well as Class methods, don’t require an instance to be called. +# Static methods doesn’t need self or cls as the first argument since it’s not bound to an instance or class. +# Static methods are normal functions, but within a class. +# Static methods are defined with the keyword @staticmethod above the function/method. +# Static methods are usually used to work on Class Attributes. + + +class MyClass(object): + # A class attribute + count = 0 + + def __init__(self, name): + print("An instance is created!") + self.name = name + MyClass.count += 1 + + # Our class method + @staticmethod + def status(): + print("The total number of instances are ", MyClass.count) + +print(MyClass.count) + +my_func_1 = MyClass("MyClass 1") +my_func_2 = MyClass("MyClass 2") +my_func_3 = MyClass("MyClass 3") + +MyClass.status() +print(MyClass.count) diff --git a/oop/31-magicmethods-1.py b/oop/31-magicmethods-1.py new file mode 100644 index 0000000..f76a3e8 --- /dev/null +++ b/oop/31-magicmethods-1.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# 30-magicmethods-1.py + +# In the backend, python is mostly objects and method +# calls on objects. + +# Here we see an example, where the `print()` function +# is just a call to the magic method `__repr__()`. + + +class PrintList(object): + + def __init__(self, my_list): + self.mylist = my_list + + def __repr__(self): + return str(self.mylist) + + +printlist = PrintList(["a", "b", "c"]) +print(printlist.__repr__()) diff --git a/oop/32-magicmethods-2.py b/oop/32-magicmethods-2.py new file mode 100644 index 0000000..a984063 --- /dev/null +++ b/oop/32-magicmethods-2.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +# 31-magicmethods-2.py + +# In the backend, python is mostly objects and method +# calls on objects. + +# To read more on magic methods, refer : +# http://www.rafekettler.com/magicmethods.html + +my_list_1 = ["a", "b", "c"] + +my_list_2 = ["d", "e", "f"] + +print("\nCalling the `+` builtin with both lists") +print(my_list_1 + my_list_2) + +print("\nCalling `__add__()` with both lists") +print(my_list_1.__add__(my_list_2)) diff --git a/oop/33-attribute-encapsulation-1.py b/oop/33-attribute-encapsulation-1.py new file mode 100644 index 0000000..e69de29 diff --git a/oop/34-abstractclasses-1.py b/oop/34-abstractclasses-1.py new file mode 100644 index 0000000..24ccbc7 --- /dev/null +++ b/oop/34-abstractclasses-1.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# 34-abstractclasses-1.py + +# This code snippet talks about Abstract Base Classes (abc). + +# The `abc` module provides features to create +# Abstract Base Classes. + +# To create an Abstract Base Class, set the `__metaclass__` magic method +# to `abc.ABCMeta`. This will mark the respective class as an Abstract +# Base Class. + +# Now, in order to specify the methods which are to be enforced on the +# child classes, ie.. to create Abstract Methods, we use the decorator +# @abc.abstractmethod on the methods we need. + +# The child class which inherits from an Abstract Base Class can implement +# methods of their own, but *should always* implement the methods defined in +# the parent ABC Class. + +import abc + + +class My_ABC_Class(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_val(self, val): + return + + @abc.abstractmethod + def get_val(self): + return + +# Abstract Base Class defined above ^^^ + +# Custom class inheriting from the above Abstract Base Class, below + + +class MyClass(My_ABC_Class): + + def set_val(self, input): + self.val = input + + def get_val(self): + print("\nCalling the get_val() method") + print("I'm part of the Abstract Methods defined in My_ABC_Class()") + return self.val + + def hello(self): + print("\nCalling the hello() method") + print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()") + +my_class = MyClass() + +my_class.set_val(10) +print(my_class.get_val()) +my_class.hello() diff --git a/oop/35-abstractclasses-2.py b/oop/35-abstractclasses-2.py new file mode 100644 index 0000000..31d1ca7 --- /dev/null +++ b/oop/35-abstractclasses-2.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# 34-abstractclasses-1.py + +# This code snippet talks about Abstract Base Classes (abc). + +# The `abc` module provides features to create +# Abstract Base Classes. + +# To create an Abstract Base Class, set the `__metaclass__` magic method +# to `abc.ABCMeta`. This will mark the respective class as an Abstract +# Base Class. + +# Now, in order to specify the methods which are to be enforced on the +# child classes, ie.. to create Abstract Methods, we use the decorator +# @abc.abstractmethod on the methods we need. + +# The child class which inherits from an Abstract Base Class can implement +# methods of their own, but *should always* implement the methods defined in +# the parent ABC Class. + +# NOTE: This code will error out. This is an example on what +# happens when a child class doesn't implement the abstract methods +# defined in the Parent Class. + +import abc + + +class My_ABC_Class(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_val(self, val): + return + + @abc.abstractmethod + def get_val(self): + return + +# Abstract Base Class defined above ^^^ + +# Custom class inheriting from the above Abstract Base Class, below + + +class MyClass(My_ABC_Class): + + def set_val(self, input): + self.val = input + + def hello(self): + print("\nCalling the hello() method") + print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()") + +my_class = MyClass() + +my_class.set_val(10) +print(my_class.get_val()) +my_class.hello() diff --git a/oop/36-abstractclasses-3.py b/oop/36-abstractclasses-3.py new file mode 100644 index 0000000..70532bc --- /dev/null +++ b/oop/36-abstractclasses-3.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# 34-abstractclasses-1.py + +# This code snippet talks about Abstract Base Classes (abc). + +# The `abc` module provides features to create +# Abstract Base Classes. + +# To create an Abstract Base Class, set the `__metaclass__` magic method +# to `abc.ABCMeta`. This will mark the respective class as an Abstract +# Base Class. + +# Now, in order to specify the methods which are to be enforced on the +# child classes, ie.. to create Abstract Methods, we use the decorator +# @abc.abstractmethod on the methods we need. + +# The child class which inherits from an Abstract Base Class can implement +# methods of their own, but *should always* implement the methods defined in +# the parent ABC Class. + +# NOTE: This code will error out. This is an example on what +# happens when a child class doesn't implement the abstract methods +# defined in the Parent Class. + +import abc + + +class My_ABC_Class(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_val(self, val): + return + + @abc.abstractmethod + def get_val(self): + return + +# Abstract Base Class defined above ^^^ + +# Custom class inheriting from the above Abstract Base Class, below + + +class MyClass(My_ABC_Class): + + def set_val(self, input): + self.val = input + + def hello(self): + print("\nCalling the hello() method") + print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()") + +my_class = My_ABC_Class() + +my_class.set_val(10) +print(my_class.get_val()) +my_class.hello() diff --git a/oop/37-method-overloading-1.py b/oop/37-method-overloading-1.py new file mode 100644 index 0000000..1a3b0bc --- /dev/null +++ b/oop/37-method-overloading-1.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# 37-method-overloading-1.py + +# Reference: O'Reilly Learning Path: +# http://shop.oreilly.com/product/0636920040057.do +# Chapter 24 : Method Overloading - Extending and Providing + +# This code is an example on how we can extend a method inherited by +# a child class from the Parent class. + +# 1) We have defined `MyClass()` as an abstract class, +# and it has three methods, my_set_val(), my_get_val(), and print_doc(). +# 2) MyChildClass() inherits from MyClass() +# 3) MyChildClass() extends the parent's my_set_val() method +# by it's own my_set_val() method. It checks for the input, +# checks if it's an integer, and then calls the my_set_val() +# method in the parent. + +# 4) The print_doc() method in the Parent is an abstract method +# and hence should be implemented in the child class MyChildClass() + + +import abc + + +class MyClass(object): + + __metaclass__ = abc.ABCMeta + + def my_set_val(self, value): + self.value = value + + def my_get_val(self): + return self.value + + @abc.abstractmethod + def print_doc(self): + return + + +class MyChildClass(MyClass): + + def my_set_val(self, value): + if not isinstance(value, int): + value = 0 + super(MyChildClass, self).my_set_val(self) + + def print_doc(self): + print("Documentation for MyChild Class") + +my_instance = MyChildClass() +my_instance.my_set_val(100) +print(my_instance.my_get_val()) +print(my_instance.print_doc()) + diff --git a/oop/38-method-overloading-2.py b/oop/38-method-overloading-2.py new file mode 100644 index 0000000..94a0a09 --- /dev/null +++ b/oop/38-method-overloading-2.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# 37-method-overloading-1.py + +# Reference: O'Reilly Learning Path: +# http://shop.oreilly.com/product/0636920040057.do +# Chapter 24 : Method Overloading - Extending and Providing + +import abc + + +class GetSetParent(object): + + __metaclass__ = abc.ABCMeta + + def __init__(self, value): + self.val = 0 + + def set_val(self, value): + self.val = value + + def get_val(self): + return self.val + + @abc.abstractmethod + def showdoc(self): + return + + +class GetSetList(GetSetParent): + def __init__(self, value=0): + self.vallist = [value] + + def get_val(self): + return self.vallist[-1] + + def get_vals(self): + return self.vallist + + def set_val(self, value): + self.vallist.append(value) + + def showdoc(self): + print("GetSetList object, len {0}, store history of values set".format( + len(self.vallist))) diff --git a/oop/39-method-overloading-3.py b/oop/39-method-overloading-3.py new file mode 100644 index 0000000..3e364ee --- /dev/null +++ b/oop/39-method-overloading-3.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# 39-method-overloading-3.py + +# We've seen that inherited methods can be overloaded. +# This is possible using in-built functions as well. + +# Let's see how we can overload methods from the `list` module. + + +class MyList(list): + + def __getitem__(self, index): + if index == 0: + raise IndexError + if index > 0: + index -= 1 + return list.__getitem__(self, index) + + def __setitem__(self, index, value): + if index == 0: + raise IndexError + if index > 0: + index -= 1 + list.__setitem__(self, index, value) + +x = MyList(['a', 'b', 'c']) +print(x) +print("-" * 10) + +x.append('d') +print(x) +print("-" * 10) + +x.__setitem__(4, 'e') +print(x) +print("-" * 10) + +print(x[1]) +print(x.__getitem__(1)) +print("-" * 10) + +print(x[4]) +print(x.__getitem__(4)) diff --git a/oop/40-super-1.py b/oop/40-super-1.py new file mode 100644 index 0000000..7e1f472 --- /dev/null +++ b/oop/40-super-1.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +# 40-super-1.py + +# This is an example on how super() works +# in Inheritance. + +# For more step-by-step details, refer : +# https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/ + + +class MyClass(object): + + def func(self): + print("I'm being called from the Parent class") + + +class ChildClass(MyClass): + + def func(self): + print("I'm actually being called from the Child class") + print("But...") + # Calling the `func()` method from the Parent class. + super(ChildClass, self).func() + +my_instance_2 = ChildClass() +my_instance_2.func() + diff --git a/oop/41-super-2.py b/oop/41-super-2.py new file mode 100644 index 0000000..c53b339 --- /dev/null +++ b/oop/41-super-2.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# 41-super-2.py + +# For more information on how this works, refer: + +# https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/ + +# https://arvimal.wordpress.com/2016/06/29/inheritance-and-method-overloading-object-oriented-programming/ + +import abc + + +class MyClass(object): + + __metaclass__ = abc.ABCMeta + + def my_set_val(self, value): + self.value = value + + def my_get_val(self): + return self.value + + @abc.abstractmethod + def print_doc(self): + return + + +class MyChildClass(MyClass): + + def my_set_val(self, value): + if not isinstance(value, int): + value = 0 + super(MyChildClass, self).my_set_val(self) + + def print_doc(self): + print("Documentation for MyChild Class") + +my_instance = MyChildClass() +my_instance.my_set_val(100) +print(my_instance.my_get_val()) +my_instance.print_doc() diff --git a/oop/42-super-3.py b/oop/42-super-3.py new file mode 100644 index 0000000..c194f96 --- /dev/null +++ b/oop/42-super-3.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# 42-super-3.py + +# super() and __init__() + +# Refer +# https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/ + +# http://www.blog.pythonlibrary.org/2014/01/21/python-201-what-is-super/ + + +class A(object): + def foo(self): + print("A") + + +class B(A): + def foo(self): + print("B") + + +class C(A): + def foo(self): + print("C") + super(C, self).foo() + + +class D(B, C): + def foo(self): + print("D") + super(D, self).foo() + +d = D() +d.foo() diff --git a/oop/README.md b/oop/README.md new file mode 100644 index 0000000..64e1c40 --- /dev/null +++ b/oop/README.md @@ -0,0 +1,136 @@ +# Object Oriented Programming in Python + +01. [Classes](https://github.com/arvimal/Python-and-OOP#01-classes) +02. [Instances, Instance methods, Instance attributes](https://github.com/arvimal/Python-and-OOP#02-instances-instance-methods-instance-attributes) +03. [Class attributes](https://github.com/arvimal/Python-and-OOP#03-class-attributes) +04. [The __init__ constructor](https://github.com/arvimal/Python-and-OOP#04-the-init-constructor) +05. [Inheritance (Inheriting {attributes,methods,constructors etc..})](https://github.com/arvimal/Python-and-OOP#05-inheritance-inheriting-attributesmethodsconstructors-etc) +06. [Encapsulation](https://github.com/arvimal/Python-and-OOP#06-encapsulation) +07. [Polymorphism](https://github.com/arvimal/Python-and-OOP#07-polymorphism) +08. [Instance methods](https://github.com/arvimal/Python-and-OOP#08-instance-methods) +09. [Multiple Inheritance and method/attribute lookup](https://github.com/arvimal/Python-and-OOP#10-multiple-inheritance-and-how-lookup-works) +10. [Method Resolution Order (MRO)](https://github.com/arvimal/Python-and-OOP#11-method-resolution-order-mro) +11. [Decorators](https://github.com/arvimal/Python-and-OOP#12-decorators) +12. [Static methods](https://github.com/arvimal/Python-and-OOP#13-static-methods) +13. [Class methods](https://github.com/arvimal/Python-and-OOP#14-class-methods) + + +# NOTES +------------ +#### 01. Classes + +Classes are the building blocks in Object Oriented Programming. + +Classes can be seen as blueprints from which you create your Instances. + +------------ +#### 02. Instances, Instance methods, Instance attributes + +------------ +#### 03. Class attributes + +Attributes or methods specific to a class are called Class attributes + +Example: + +``` +class MyClass(object): + value = 10 + + def __init__(self): + pass +``` + +Here `value` is a class attribute. These are used when certain values need to be set outside a function. + +------------ +#### 04. The __init__ constructor + +The __init__() constructor is a magic method which gets called when a class is instantiated. + +Any attributes set under the __init__() constructor will be instantiated at the time of instance creation. + +------------ +#### 05. Inheritance (Inheriting {attributes,methods,constructors etc..}) + +------------- +#### 06. Encapsulation + +------------ +#### 07. Polymorphism + +------------ +#### 08. Instance methods + +------------ +#### 09. Multiple Inheritance and method/attribute lookup + +* Any class can inherit from other classes. +* Any python class can inherit from multiple classes at the same time. +* The class that inherits another class is called the Base/Child class. +* The class being inherited by the Child class is called the Parent class. +* The child class inherits any methods and attributes defined in the parent classes. +* Python uses a depth-first method resolution order (MRO) to fetch methods. +* When two classes inherits from the same class, from Python2.3 onwards, the MRO omits the first occurrence of the class. +* This new MRO lookup method applies from Python2.3, and is for the new-style classes. + *NOTE:* New style classes inherits from the 'object' parent class. + +------------ +#### 10. Method Resolution Order (MRO) + +* Python has a method lookup order, called `MRO` (Method Resolution Order) +* The MRO path can be printed to stdout using `print .mro()` +* Python, by default, uses a depth-first lookup path for MRO. + +* ie.. Imagine you have four classes, A, B, C, D. + + 1. You instance is created from `D`. + 2. `D` inherits from `B` and `C` + 3. `B` inherits from `A`. + 4. Both `C` and `A` has a method with the same name. + 5. Since python follows a depth-first MRO, the method is called from `A` + +REFERENCE: Check the code examples in: + * 14-multiple-inheritance-1.py + * 15-multiple-inheritance-2.py + +In some cases, the inheritance can get quite cumbersome when multiple classes inherit from the same classes, in multiple levels. + +**NOTE** : From Python2.3, the MRO has changed slightly in order to speed up the method lookups. + +The MRO lookup now skips/omits the initial occurrences of classes which occurs multiple time in the lookup path. + +* Example: + 1. Four classes, A, B, C, D. + 2. D inherits from both B and C + 3. B inherits from A + 4. C inherits from A + 5. Both C and A contains a similar named method. + 6. Your instance in created from class D. + 7. You try a lookup for the method which is named both in A and C. + 8. The usual lookup should be D -> B -> A -> C -> A + a. Hence since the method exists in A and C, it should return from A. + b. But since the class A is occurring twice and due to the new MRO method, the lookup will be + D -> B -> C -> A. + 9. The lookup should return the method from class C, rather than A. + +REFERENCE: Check the code example in: + * 16-multiple-inheritance-3.py + +-------------- +#### 11. Decorators + +-------------- +#### 12. Static methods + +-------------- +#### 13. Class methods + +-------------- + +#### 14. Magic methods + +Magic methods + + + diff --git a/python-database.md b/python-database.md new file mode 100644 index 0000000..9221547 --- /dev/null +++ b/python-database.md @@ -0,0 +1,237 @@ +# python sqllite3 + + +SQLite is a simple relational database system, which saves its data in regular data files or even in the internal memory of the computer, i.e. the RAM. + +* First, establish a connection to the the SQLite database by creating a Connection object. +* Next, create a Cursor object using the cursor method of the Connection object. +* Then, execute the SELECT statement. +* After that, call the fetchall() method of the cursor object to fetch the data. +* Finally, loop the cursor and process each row individually. + + +## Creating a new database in Python + +``` +import sqlite3 + +with sqlite3.connect('example.db') as db: + pass + +``` +## checking version +``` +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import sqlite3 as lite +import sys + +con = None + +con = lite.connect('test.db') +cur = con.cursor() +cur.execute('SELECT SQLITE_VERSION()') +data = cur.fetchone() +print "SQLite version: %s" % data +con.close() + +``` +## Creating Database Table + + +``` +#!/usr/bin/python + +import sqlite3 + +conn = sqlite3.connect('test.db') +print "Opened database successfully"; + +conn.execute('''CREATE TABLE COMPANY + (ID INT PRIMARY KEY NOT NULL, + NAME TEXT NOT NULL, + AGE INT NOT NULL, + ADDRESS CHAR(50), + SALARY REAL);''') +print "Table created successfully"; + +conn.close() +``` + + +## INSERT Operation + +``` +#!/usr/bin/python + +import sqlite3 + +conn = sqlite3.connect('test.db') +print "Opened database successfully"; + +conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ + VALUES (1, 'Paul', 32, 'California', 20000.00 )"); + +conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ + VALUES (2, 'Allen', 25, 'Texas', 15000.00 )"); + +conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ + VALUES (3, 'Teddy', 23, 'Norway', 20000.00 )"); + +conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ + VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 )"); + +conn.commit() +print "Records created successfully"; +conn.close() +``` + +When the above program is executed, it will create the given records in the COMPANY table and it will display the following two lines − +``` +Opened database successfully +Records created successfully +``` +## SELECT Operation + +Following Python program shows how to fetch and display records from the COMPANY table created in the above example. +``` +#!/usr/bin/python + +import sqlite3 + +conn = sqlite3.connect('test.db') +print "Opened database successfully"; + +cursor = conn.execute("SELECT id, name, address, salary from COMPANY") +for row in cursor: + print "ID = ", row[0] + print "NAME = ", row[1] + print "ADDRESS = ", row[2] + print "SALARY = ", row[3], "\n" + +print "Operation done successfully"; +conn.close() +``` +When the above program is executed, it will produce the following result. +``` +Opened database successfully +ID = 1 +NAME = Paul +ADDRESS = California +SALARY = 20000.0 + +ID = 2 +NAME = Allen +ADDRESS = Texas +SALARY = 15000.0 + +ID = 3 +NAME = Teddy +ADDRESS = Norway +SALARY = 20000.0 + +ID = 4 +NAME = Mark +ADDRESS = Rich-Mond +SALARY = 65000.0 + +Operation done successfully +``` +## UPDATE Operation + +Following Python code shows how to use UPDATE statement to update any record and then fetch and display the updated records from the COMPANY table. +``` +#!/usr/bin/python + +import sqlite3 + +conn = sqlite3.connect('test.db') +print "Opened database successfully"; + +conn.execute("UPDATE COMPANY set SALARY = 25000.00 where ID = 1") +conn.commit +print "Total number of rows updated :", conn.total_changes + +cursor = conn.execute("SELECT id, name, address, salary from COMPANY") +for row in cursor: + print "ID = ", row[0] + print "NAME = ", row[1] + print "ADDRESS = ", row[2] + print "SALARY = ", row[3], "\n" + +print "Operation done successfully"; +conn.close() +``` +When the above program is executed, it will produce the following result. +``` +Opened database successfully +Total number of rows updated : 1 +ID = 1 +NAME = Paul +ADDRESS = California +SALARY = 25000.0 + +ID = 2 +NAME = Allen +ADDRESS = Texas +SALARY = 15000.0 + +ID = 3 +NAME = Teddy +ADDRESS = Norway +SALARY = 20000.0 + +ID = 4 +NAME = Mark +ADDRESS = Rich-Mond +SALARY = 65000.0 + +Operation done successfully +``` +## DELETE Operation + +Following Python code shows how to use DELETE statement to delete any record and then fetch and display the remaining records from the COMPANY table. +``` +#!/usr/bin/python + +import sqlite3 + +conn = sqlite3.connect('test.db') +print "Opened database successfully"; + +conn.execute("DELETE from COMPANY where ID = 2;") +conn.commit() +print "Total number of rows deleted :", conn.total_changes + +cursor = conn.execute("SELECT id, name, address, salary from COMPANY") +for row in cursor: + print "ID = ", row[0] + print "NAME = ", row[1] + print "ADDRESS = ", row[2] + print "SALARY = ", row[3], "\n" + +print "Operation done successfully"; +conn.close() +``` +When the above program is executed, it will produce the following result. +``` +Opened database successfully +Total number of rows deleted : 1 +ID = 1 +NAME = Paul +ADDRESS = California +SALARY = 20000.0 + +ID = 3 +NAME = Teddy +ADDRESS = Norway +SALARY = 20000.0 + +ID = 4 +NAME = Mark +ADDRESS = Rich-Mond +SALARY = 65000.0 + +Operation done successfully +``` diff --git a/regular-expression.md b/regular-expression.md new file mode 100644 index 0000000..8492938 --- /dev/null +++ b/regular-expression.md @@ -0,0 +1,268 @@ +# Regular Expressions + +A regular expression pattern is a sequence of characters that will match sequences of characters in a target. + +The patterns or regular expressions can be defined as follows: +* Literal characters must match exactly. For example, "a" matches "a". +* Concatenated patterns match concatenated targets. For example, "ab" ("a" followed by "b") matches "ab". +* Alternate patterns (separated by a vertical bar) match either of the alternative patterns. For example, "(aaa)|(bbb)" will match either "aaa" or "bbb". +* Repeating and optional items: + * "abc*" matches "ab" followed by zero or more occurances of "c", for example, "ab", "abc", "abcc", etc. + * "abc+" matches "ab" followed by one or more occurances of "c", for example, "abc", "abcc", etc, but not "ab". + * "abc?" matches "ab" followed by zero or one occurances of "c", for example, "ab" or "abc". + +### Compiling regular expressions +When a regular expression is to be used more than once, you should consider compiling it. For example: +``` +import sys, re + +pat = re.compile('aa[bc]*dd') + +while 1: + line = raw_input('Enter a line ("q" to quit):') + if line == 'q': + break + if pat.search(line): + print 'matched:', line + else: + print 'no match:', line +``` +Comments: + + We import module re in order to use regular expresions. + re.compile() compiles a regular expression so that we can reuse the compiled regular expression without compiling it repeatedly. + +### Using regular expressions + +Use match() to match at the beginning of a string (or not at all). + +Use search() to search a string and match the first string from the left. + +Here are some examples: + +>>> import re +>>> pat = re.compile('aa[0-9]*bb') +>>> x = pat.match('aa1234bbccddee') +>>> x +<_sre.SRE_Match object at 0x401e9608> +>>> x = pat.match('xxxxaa1234bbccddee') +>>> x +>>> type(x) + +>>> x = pat.search('xxxxaa1234bbccddee') +>>> x +<_sre.SRE_Match object at 0x401e9608> + +Notes: + + When a match or search is successful, it returns a match object. When it fails, it returns None. + + You can also call the corresponding functions match and search in the re module, e.g.: + + >>> x = re.search(pat, 'xxxxaa1234bbccddee') + >>> x + <_sre.SRE_Match object at 0x401e9560> + + For a list of functions in the re module, see Module Contents -- http://docs.python.org/library/re.html#module-contents. + +### Using match objects to extract a value + +Match objects enable you to extract matched sub-strings after performing a match. A match object is returned by successful match. The part of the target available in the match object is the portion matched by groups in the pattern, that is the portion of the pattern inside parentheses. For example: + +``` +mo = re.search(r'height: (\d*) width: (\d*)', 'height: 123 width: 456') +mo.groups() +('123', '456') +``` + +## some examples +``` +import re + +def print_match(match): + if match is None: + print 'No match' + return + + print "'{g}' was matched between the indices {s}".format(g=match.group(), s=match.span()) + return match + +``` +word = "infinity" + + +Find the character 'i' at the beginning of the word +``` + print re.findall(r"^i", word) + ['i'] + +``` +Find all occurrences of the character 'i' +``` +print re.findall(r"i", word) +['i', 'i', 'i'] +``` +Find all non-overlapping two-character length substrings, where the second character is 'i' +``` +print re.findall(r".i", word) +['fi', 'ni'] +``` + +Find all non-overlapping substrings ending with character 'i', +where the preceding character exists or does not +``` +print re.findall(r".?i", word) + ['i', 'fi', 'ni'] +``` + +Find all non-overlapping substrings 'in' +``` +print re.findall(r"i[n]", word) +['in', 'in'] +``` +Find all non-overlapping substrings starting with character 'i', +and ending with any character except 'n' +``` +print re.findall(r"i[^n]", word) +['it'] +``` + +Perform a greedy searching for the substring starting and ending with the character 'i' +(longest possible match) +``` +print_match(re.search(r"i.*i", word)) +``` +'infini' was matched between the indices (0, 6) + +Perform a non-greedy searching for the substring starting and ending with the character 'i' +(shortest possible match) +``` +print_match(re.search(r"i.*?i", word)) +'infi' was matched between the indices (0, 4) + +``` + +single_line = "Looks like this sentence is not as simple as it looks" + + + +Perform a case-sensitive searching for the first occurrence of the word 'looks' +``` +print_match(re.search(r"looks", single_line)) + +'looks' was matched between the indices (48, 53) +``` +Perform a case-insensitive searching for the first occurrence of the word 'looks' +``` +print_match(re.search(r"looks", single_line, re.IGNORECASE)) +'looks' was matched between the indices (0, 5) +``` + +Perform a case-sensitive matching for the word 'looks' +``` +print_match(re.match(r"looks", single_line)) +No match +``` +Perform a case-insensitive matching for the word 'looks' +``` +print_match(re.match(r"looks", single_line, re.IGNORECASE)) +'Looks' was matched between the indices (0, 5) +``` + + +# Orinoco Flow by Enya +multi_line = """Let me sail, let me sail +Let the Orinoco Flow +Let me reach, let me beach +On the shores of Tripoli""" + + + +# Searching for the word 'sail' at the end of the string +print_match(re.search(r"sail$", multi_line)) +# No match + + +# Searching for the word 'sail' at the end of any line +print_match(re.search(r"sail$", multi_line, re.MULTILINE)) +# 'sail' was matched between the indices (20, 24) + + +# Searching for the phrase 'beach On' with any character except newline between these two words +print_match(re.search(r"beach.On", multi_line)) +# No match + + +#Searching for the phrase 'beach On' with any character between these two words +print_match(re.search(r"beach.On", multi_line, re.DOTALL)) +# 'beach +# On' was matched between the indices (67, 75) + + +# Searching for all the lines between the line containing the word 'reach', +# and the line ending with the word 'Tripoli' +print_match(re.search(r"^[^\n]*reach.*Tripoli$", multi_line, re.MULTILINE|re.DOTALL)) +# 'Let me reach, let me beach +# On the shores of Tripoli' was matched between the indices (46, 97) + + + + +import re + + +string = "Once you have accomplished small things, you may attempt great ones safely." + +# Return all words beginning with letter 'a', as a list of strings +print re.findall(r"\ba[\w]+", string) +# ['accomplished', 'attempt'] + + +# Return all words beginning with letter 'a', as an iterator yielding match objects +it = re.finditer(r"\ba[\w]+", string) +for match in it: + print "'{g}' was found between the indices {s}".format(g=match.group(), s=match.span()) +# 'accomplished' was found between the indices (14, 26) +# 'attempt' was found between the indices (49, 56) + + + +# Split string by any character which is not a UNICODE word character +print re.split("\W+", string) +# ['Once', 'you', 'have', 'accomplished', 'small', 'things', 'you', 'may', 'attempt', 'great', 'ones', 'safely', ''] + +# Split string by any character which is not a UNICODE word character at most 2 split +print re.split("\W+", string, 2) +# ['Once', 'you', 'have accomplished small things, you may attempt great ones safely.'] + +# If the splitting pattern does not occur in the string, string is returned as the first element of the list +print re.split("(:)", string) +# ['Once you have accomplished small things, you may attempt great ones safely.'] + + + +# Replace all occurrences of space, comma, or dot with colon +print re.sub("[ ,.]", ":", string) +# Once:you:have:accomplished:small:things::you:may:attempt:great:ones:safely: + +# Replace maximum 2 occurences of pattern +print re.sub("[ ,.]", ":", string, 2) +# Once:you:have accomplished small things, you may attempt great ones safely. + +# Replace as 'sub', and return a tuple of (new string, number of replacements) +print re.subn("[ ,.]", ":", string) +# ('Once:you:have:accomplished:small:things::you:may:attempt:great:ones:safely:', 13) + + + +# Find all five characthers long words +print re.findall(r"\b\w{5}\b", string) +# ['small', 'great'] + +# Find all five, six, or seven characthers long words +print re.findall(r"\b\w{5,7}\b", string) +# ['small', 'things', 'attempt', 'great', 'safely'] + +# Find all words which are at least 8 characters long +print re.findall(r"\b\w{8,}\b", string) +# ['accomplished'] diff --git a/shutil.txt b/shutil.txt new file mode 100644 index 0000000..f47edb3 --- /dev/null +++ b/shutil.txt @@ -0,0 +1,45 @@ +import shutil +import os +source = os.listdir("/tmp/") +destination = "/tmp/newfolder/" +for files in source: + if files.endswith(".txt"): + shutil.copy(files,destination) + + + +import shutil +shutil.copyfile('/path/to/file', '/path/to/other/phile') + + +import shutil +import os +source = os.listdir("/tmp/") +destination = "/tmp/newfolder/" +for files in source: + if files.endswith(".txt"): + shutil.move(files,destination) + +import shutil +import os +SOURCE = "samples" +BACKUP = "samples-bak" +# create a backup directory +shutil.copytree(SOURCE, BACKUP) +print os.listdir(BACKUP) + + +import shutil +shutil.rmtree('one/two/three') + + +>>> import shutil +>>> shutil.move("server.log", "server.log.backup") +>>> shutil.move("image.png", "/home/user/") + + +>>> shutil.copy("image.png", "/home/user/") + + +>>> import shutil +>>> shutil.os