I decided to write a Literate Programme Tutorial of Python in Python, i.e. a document that contains executable code as well as very extensive comments about the basic functionalties of the code. This "programme" is quite big, 431 lines, but most of it is text explaining what is happening. Is it ok to post the tutorial/programme here on Code Review? For completness I include the script down here.
""" Welcome to this Python tutorial. This document is both a tutorial and a piece of code: you can read it and you can also execute it! if you are curious just press F5 to see what it does. At the end of this tutorial you will understand how all the functions that you see in this file are created. And you will know some basic concept of programming. --- Traditionally the first programme is an hello world programme, let us start with that: """ def hello_world(): print("Hello world!") """ 'def' is the most important keyword in Python: it defines a function. A function in short is a piece of code that: 1) manipulates an input or 2) gives an output or performs some IO. The 'print' function just outputs something. -------- We may want to keep track of a value under a name, this is called creating a variable. Creating a variable is very straightforward: Just type a name the '=' sign and a value. """ greeting = "Hello" place = "World" answer = 42 """ Now that we have covered the basics of output and variables, we can get down to the real job that computers can do well: calculating things. Lets make a simple example: """ result = 2 + 2 """ If I ask "How much is the result?" you can easily reply 4. But let's make another example (a**b means a to the power of b) """ result = 9**13 """ Now it is not easy to mentally calculate that the result is 2541865828329, is it? Instead a computer can calculate it in less than a tenth of a second. --- It is very rare to create a programme that will execute all the statements it contains no matter what, in other words we may want to branch the programme, do a thing if a condition is True, do another if the condition is False. I am going to illustrate this with a simple age asking example: """ try: input = raw_input except NameError: pass """ Please ignore the lines of code above, they are there to make this programme compatible with the old Python 2, they are outside the scope of this tutorial """ def decide_if_teenager(age): age = int(input("How old are you? ")) if 13 <= age <= 19: print("You are a teenager") else: print("You are not a teenager") """ The input(text_to_display) function asks the user for a value and 'returns' that value. For now just rememember that you can use my_user_decided_value = input("Enter your value: ") The age input is then converted to an integer to compare it with numbers, because all input defaults to text and then an if: else: statement is used to print an appropriate message. --- We now run into an obvious question: "If computers are so fast how can I get my computer to execute a big number of operations? Do I have to type them all one by one?" Luckly you can tell the computer to execute multiple statements with 'loops'. A loop is nothing more than repeating something. """ def count_to_ten(): for number in range(1,10): print(number) """ This function will output all the numbers from one to ten. --- We can now create our first programme the really uses the computational power of modern computers: a factorial programme. (The factorial of a number is the product of all the numbers from 1 to that number). """ def factorial(n): result = 1 for i in range(1,n+1): result *= i return result """ Let us consider the most peculiar parts of this function: * range(1,n+1): ranges in Python are non-inclusive, that is the last element is considered outside of the range. * 'a *= b' is the same as 'a = a * b' but is shorter, so it is used to be clearer and to avoid too much tipying. * 'return' this keyword is very important, it allows us to create functions that 'give back' a value. --- There is also another kind of looping, the while loop, that loops while the condition remains True. It should be used rarely but for completeness sake I am going to implement a programme that uses it. The collatz conjecture says that the Collatz sequence of all the numbers will arrive to one. The collatz sequence of a number is definde as folows """ def next_collatz(n): if is_even(n): return n//2 else: return 3*n + 1 def collatz_sequence(n): steps = 1 while n != 1: n = collatz_sequence(n) steps += 1 return steps """ The above function will compute the collatz sequence of a number and will return the number of collatz_steps taken to arrive to 1 --- Let's make another function that computes numerical values, this time a little harder, the GCD (Greatest Common Divisor). Here we will understand the huge power of functions: complex functions can be built from simple ones. Our plan is to decompose the problem in the following sub-problems: 1) Find the greatest or max of a list of numbers. 2) Find the common numbers between two lists. 3) Find the divisors of a number. 1) Find the greatest or max of a list of numbers. We are very lucky because we don't have to solve this problem ourselves: there is a built-in function ready to use called max that will do just this. 2) Find the common numbers between two lists. The best way to approach this task is list comprehension. List comprehension is a simple way to change lists. A list is a series of comma separated values. """ example_lst = [3,5,7,2,8] """ There are two ways to change lists, one is 'filtering' them, taking only the elements that satisfy a condition """ def is_even(n): return n % 2 == 0 even_only = [i for i in example_lst if is_even(i)] """ The other is 'mapping' them, applying a function that changes the value: """ to_the_3rd_power = [i**3 for i in example_lst] """ Finally we should remember that these can be applied together: """ even_only_to_the_3rd_power = [i**3 for i in example_lst if is_even(i)] """ Now defining a function that takes the common elements of two lists is trivial: """ def common(list1,list2): return [i for i in list1 if i in lst2] """ 3) Finding the divisors of a number. This task is very interesting: you can either choose a very simple function that is slow or a very fast one that is harder. In this tutorial I will use the simple one. Often you can translate a mathematical concept in code directly: ' The divisors (or factors) of a number are all the natural numbers (integers) less than it if the remainder of the division number / divider equals 0.' """ def divisors(n): return [i for i in range(1,n+1) if n % i == 0] """ We have been able to translate the concept to code verbatim! (Technical side note: '%' is the reaminder-of-division (modulo) operator. """ """ And now that we have all the pieces we can assemble the final GCD proramme. """ def GCD(a,b): return max(common(divisors(a),divisors(b))) """ In the above function the parenthesis work as they do in the algebraic expressions i.e. you start evaluating what is in the innermost parenthesis first and then you proceed to the outer parenthesis. --- We now know some Math techniques, so it is time to learn about files IO and text processing techniques. """ def get_text_of_self(): with open(__file__) as f: return f.read() """ In the code above I introduced 2 new concepts: 1) 'with open(filename) as f:' is the best way to handle files IO: just call it, than do what you want with f and then forget about it: it handles closing automatically. 2) '__file__' as you can see this variable has __double__undercores__, it means that it is 'magic': the Python interpreter (the programme that runs the Python programmes) gives a value to all this __double__underscored__variables__ at runtime. '__file__' is the name of the script. --- Now we may be curious to know what are the most common words in this file. Yet another time, we can use a built-in called counter """ from collections import Counter """ Imports should always be placed at the start of files but for you to better read this tutorial, I have put this import in the midddle of the file. Now you may start wondering: "How can I understand what is built-in and what is not?" Actually it is not easy: the Python Standard Library (i.e. the place that contains all the built-ins) is huge. A reasonable rule of thumb is that 95% of what you can think of is built-in, you just have to search for it. A good page to see all the built-ins listed nicely is https://docs.python.org/3/library" --- So let's return to goal of getting the most common words of this file. We are going to divide the task in smaller functions: 1) Getting the content of this file 2) Making a list of words out of it 3) Using Counter to count them 1) Is already done, just see the get_text_of_self function above. 2) To make a list of words out of it we must use the important '.split()' built-in, it allows youto split a text at a char and gives you a list. As words are separated by spaces we can split on the spaces """ def get_wordlist(text): return text.split(" ") """ 3) Counter is built-in. --- We can now assemble the pieces: """ def count_words_in_this_file(): words = get_wordlist(get_text_of_self()) return Counter(words) """ This will return all the words in the file with the number of times they appear, scroll to the top to see the most common ones. --- So far we have covered the basics of calculating with numbers, IO and processing text, now I will show you how to create a GUI, a graphical user interface, where the user can press buttons and enter values in 'widgets'. "Is a GUI module built-in?" The answer unsuprisingly is yes, it is called Tkinter. This GUI will be a number guessing game: The user moves a slider to select a number, he then presses a button and if the number is equal to the computer's number, he will win, otherwise the computer will tell him lower or bigger. """ """ First of all we import the needed libraries, as I want this programme to be compatible with the old Python 2.x I have to make the imports code a bit more complicated, anyway it is just trying to import the modules for Python 3, if it fails it imports the modules for Python 2 """ try: import tkinter as tk import tkinter.messagebox as pop_up except ImportError: import Tkinter as tk import tkMessageBox as pop_up import random """ As always we decompose the problem into functions, as creating a number guessing game is easy we only need 3 of them """ def generate_message(user_number,cpu_number): if user_number < cpu_number: return "Too low" elif user_number == cpu_number: return "Right!" else: return "Too big" def number_guessing_game_GUI(): """ Tkinter GUI programmes are usually Object Oriented, but I consider that an advanced topic, also it is not compulasory. As this is a beginner course I decided not to use Objects but simple functions and variables. """ """ The following line is a convention always followed when writing a tkinter programme. """ root = tk.Tk() root.wm_title("Number guessing game") """ Sadly inside tkinter we can't use the Python built-ins types, we are forced to use IntVar and StringVar. These kind of types must be assigned with the set keyword and read with the get keyword. cpu_number is the only IntVar that we will use in this simple programme. """ cpu_number = tk.IntVar() cpu_number.set(random.randint(1,10)) def reset(): cpu_number.set(random.randint(1,10)) def show_message(): pop_up.showinfo("The number you chose is...", generate_message(number_selector.get(),cpu_number.get())) """ Down here you can see the code creating the widgets i.e. the things that you see in the GUI. It is quite long but pretty easy to undersatnd. """ number_selector = tk.Scale(root,from_=1, to=10,orient = tk.HORIZONTAL) number_selector.pack() confirm_button = tk.Button(root,text = "Check this number",command = show_message) confirm_button.pack() play_again_button = tk.Button(root, text = "Play again",command = reset) play_again_button.pack() """ In order to actually display the GUI you need to call the mainloop function """ tk.mainloop() """ Now we are going to display a welcome message to the user (to you in this case :)) and show him the list of the functions in this module """ from inspect import getmembers, isfunction functions_list = [hello_world, decide_if_teenager, count_to_ten, factorial, next_collatz, collatz_sequence, is_even, common, divisors, GCD, get_text_of_self, get_wordlist, count_words_in_this_file, generate_message,number_guessing_game_GUI, ] def main(): print("""Welcome to the interactive shell, you can experiment with the following functions defined in this tutorial.""") print([i.__name__ for i in functions_list]) """ When writing a medium/big Python script it is good practice to use the following 'guard' """ if __name__ == "__main__": main() """ This way the Python script will only execute the main funciton if it called directly, if you put the UI code in the main function this means that you can use the script not only standalone but also as a library importing it """
P.S. I would like to post the programme in this wiki is the copyright of the reviews ok?