Return to Course Home Page


The Zen of Python

Code
# What is the Zen of Python??
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Beautiful is better than ugly

Code
# "And Or"
A1=12
B=7
C=10
if (A1>B) | (A1>C): print(A1)
else: print(B)
    
a_1 = 12
b = 7
c = 10
if a_1 > b or a_1 > c:
    print(a_1)
else:
    print(b)

Explicit is better than implicit

Code
# "Import-ance"
url = 'https://jsonplaceholder.typicode.com/posts/1'
from requests import *


a = get(url)
a.json()
{'userId': 1,
 'id': 1,
 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'}

Simple is better than complex

Code
# "Addition through subtractions"
# complex:
def func(x):    
    return x*x

x = 2
func(x)

# simple:

x * x

Readability Matters

Curly Bracket Languages: Define code blocks using { and }. (Some also define the end of a statement using ;) (aspects of semantics are defined by syntax)

Examples: C, Javascript, R

Offsides Rule Languages: Define code blocks using indentation.

R: (Curly Brackets + Indentation)

if(a > b) {
    print("a is greater than b")
} else {
    print("a is not greater than b")
}

Python (Indentation)

if a > b:
    print("a is greater than b")
else:
    print("a is not greater than b")

Readability matters

Clear (Javascript Example):

// This function takes a name and logs
// a string which greets that name
function sayHi(name) {
    console.log("Hi " + name + ", nice to meet you.")
}

sayHi("Sam");

Not Clear (“Minified” Javascript):

function f(o){console.log("Hi "+o+", nice to meet you.")}f("Sam");

Readability matters

General Rules:

  1. Make your code easy to read.
  2. Include lots of comments
  3. Keep lines of code short
  4. Avoid single character variable names.
  5. Call your functions with named parameters where applicable.
  6. Use descriptive variable names.

Simple is Better than Complex

Don’t overcomplicate your code to look smart.

2nd place, Best ‘The Perl Journal’, 5th Annual Obfuscated Perl Contest: Mark Jason Dominus.

@P=split//,".URRUUxR";@d=split//,"\nlanruoJ lreP ehT";
sub p{@p{"r$p","u$p"}=(P,P);pipe"r$p","u$p";$p++;($q*=
2)+=$f=!fork;map{$P=$P[$f|6&ord$p{}];$p{}=/$P/i?$P:
close}%p}p;p;p;p;p;map$p{}=~/[P.]/&&close,%p;wait
until;map/r/&&<>,%p;print$d[$q]

Sparse is Better than Dense

Don’t try to stick too much code on one line

Dense

if i>0: return sqrt(i)
elif i==0: return 0
else: return 1j * sqrt(-i)

Sparse

if i > 0:
    return sqrt(i)
elif i == 0:
    return 0
else:
    return 1j * sqrt(-i)

There should be one and preferably only one obvious way to do it

There should be one-- and preferably only one --obvious way to do it

Different languages implement prefix (++i and --i) and postfix (i++ and i--) operators differently, which often leads to confusion.

Example C code:

int i = 4;
int j = 21;

int k = ++i * 7 + 2 - j--;

What are the values of i, j, and k?

There should be one-- and preferably only one --obvious way to do it

There are no prefix or postfix operators in python. You must increment and decrement variables directly.

Example C code:

int i = 4;
int j = 21;

int k = ++i * 7 + 2 - j--;

What are the values of i, j, and k?

Code
i = 4
j = 21

i = i + 1

k = i * 7 + 2 - j

j = j - 1

print(i,j,k)

Errors should never pass silently

Example: While calling a low-level function that saves data to disk, the function runs out of disk space before all of its data is written.

Three “Philosophies” of Error Handling

  1. Don’t check for problems.
  2. Systematically determine the Error Status of every statement
  3. Raise Exceptions when things go wrong

Strategy 1: No Error Handling

While saving data to disk, the function runs out of disk space before all of its data is written.

def write_data(data):
    os.write(data)

this_is_fine.jpeg

Strategy 1: No Error Handling

While saving data to disk, the function runs out of disk space before all of its data is written.

Pros:

  • Easy to implement!
  • Usually fine; errors are very rare

Cons:

  • Unpredictable failure modes
  • Errors can impact other data, programs, etc…
  • Very dangerous when moving to new contexts

Strategy 2: All Functions/Operations return an Error Status (C, C++, Swift)

While saving data to disk, the function runs out of disk space before all of its data is written.

def write_data(data):
    return_val = os.write(data)
    if return_val == "SUCCESS":
        return "SUCCESS"
    else:
        return "OH-NO!"
    

error_handling.jpg

Strategy 2: All Functions/Operations return an Error Status (C, C++, Swift)

While saving data to disk, the function runs out of disk space before all of its data is written.

Pros:

  • Comprehensive coverage of error conditions
  • State of the program is always known

Cons:

  • High burden on programmers to constantly check error state
  • Any coverage gaps in error checking can lead to unstable conditions and make programs crash-prone

Strategy 3: Errors are handled using an Exception framework

While saving data to disk, the function runs out of disk space before all of its data is written.

def write_data(data):
    try:
        os.write(data)
    except DiskFullError:
        print("Can't write this file; it's too big!")

exception.jpeg

Strategy 3: Errors are handled using an Exception framework (Python, Ruby, )

Pros:

  • Elimintaes repetitive nature of error checking
  • Allows errors to propagate up from lower-level programs, making centralized error handling easier. (tryexcept clauses)

Cons:

  • Exceptions are easier to ignore/trap than errors.
  • Can lead to sloppy code that “hides” excpetions instead of “handling” them.
  • These cons can be avoided with good programming practice (errors of implementation, not errors of design)

Try or Try Not``

Python Errors

There are two types of errors in Python: SyntaxErrors and Exceptions.

SyntaxErrors

A SyntaxError happens when the Python language interpreter (the parser) detects an incorrectly formatted statement.

This code is trying to divide two numbers, but there are mismatched parentheses. What happens when we run it?

>>> print( 5 / 4 ))
Code
print( 5 / 4 ))
SyntaxError: unmatched ')' (2701704956.py, line 1)

When python says SyntaxError, you should read this as I don't know what you want me to do!?

Often the error includes some indication of where the problem is, although this indication can sometimes be misleading if the detection occurs far away from the syntax problem that created the error. Often the interpreter will attempt to explain what the problem is!

Exceptions

An Exception happens the code you have written violates the Python language specification.

This code is trying to divide zero by 0. Its syntax is correct. But what happens when we run it?

>>> print( 0 / 0 )
Code
try:
    print( 0 / 0 ) 
except ZeroDivisionError:
    print(f"It didn't work because you tried to divide by zero")
It didn't work because you tried to divide by zero

When python says anything other than SyntaxError, you should read this as You are asking to do something I can't do

In this case, the ZeroDivisionError is raised because the Python language specification does not allow for division by zero.

Types of Exceptions

Python has a lot of builtin Errors that correspond to the definition of the Python language.

A few common Exceptions you will see include TypeError, IndexError, and KeyError.

TypeError

A TypeError is raised when you try to perform a valid method on an inappropriate data type.

Code
# TypeError Examples:
'a' + 3

IndexError

An IndexError is raised when you try to access an undefined element of a sequence. Sequences are structured data types whose elements are stored in a specific order. A list is an example of a sequence.

Code
# IndexError Example:
my_list = ['a', 'b', 'c', 'd']
my_list[4]
IndexError: list index out of range

KeyError

A KeyError is raised when you try to perform a valid method on an inappropriate data type.

Code
# KeyError Examples:

my_dict = {'column_1': 'definition 1', 'another_word': 'a second definition'}
my_dict['column1']
KeyError: 'column1'

Deciphering Tracebacks

When an exception is raised in python the interpreter generates a “Traceback” that shows where and why the error occurred. Generally, the REPL has most detailed Traceback information, although Jupyter Notebooks and iPython interactive shells also provide necessary information to debug any exception.

Code
# defining a function
def multiply(num1, num2):
    result = num1 * num2
    print(results)
 
# calling the function
multiply(10, 2)
NameError: name 'results' is not defined
Code
## The End