Python Tutorial

Python is a language which prides itself on simplicity of reading and understanding. If you'd like to read more about python go to https://en.wikipedia.org/wiki/Python_(programming_language) !

If you'd like to read further than this, or a more basic guide I would recommend: https://en.wikibooks.org/wiki/Non-Programmer%27s_Tutorial_for_Python_3

Here's an example python program to print something on the screen:

print('Hello, World!')
# This is a comment, it isn't run as code, but often they are helpful
print('Hello, World!')
Hello, World!

1. Arithmetic

Like every programming language, Python is a good calculator. Run the block of code below to make sure the answer is right!

1 + 1
2

The order of operations you learned in school applies, BEDMAS (brackets, exponents, division, multiplication, addition, subtraction):

8 + 6*2*3 - (15 - 13)
42

Numbers are valid Python code as are the common operators, +, /, * and -. You can write different types of numbers including integers, real numbers (floating point) and negative integers.

42 + 3.149 + -1
44.149

Since 42 is literally 42, we call these numbers literals. You are literally writing number in your Python code.

2. Variables

In python, variables are similar to C, Java and other languages - they store a certain value. Specifically, in python variables do not have a type. So, a string can also become an integer, and then even into a function! Literals are given types of string, integer, float, lists, objects, etc. But the variable's type can change.

x = 200.00
print("x =", x, "and is of type", type(x))
x = "Hello World!"
print("x =", x, "and is of type", type(x))
x = 200.0 and is of type <class 'float'>
x = Hello World! and is of type <class 'str'>
x = 10000.00
print("x=", x, "Type", type(x))
x = "Yoooooooo//"
print("x=",x , "Type",type(x))
x= 10000.0 Type <class 'float'>
x= Yoooooooo// Type <class 'str'>

3. Exceptions in Python

Python only understands certain code. When you write something Python doesn't understand it throws an exception and tries to explain what went wrong, but it can only speak in a broken Pythonesque english. Let's see some examples by running these code blocks

variable_that_is_undefined
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-7-a887aa8d91a7> in <module>()
----> 1 variable_that_is_undefined

NameError: name 'variable_that_is_undefined' is not defined
print('Hello'
  File "<ipython-input-8-49c0f052bb79>", line 1
    print('Hello'
                 ^
SyntaxError: unexpected EOF while parsing
2000 / 0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-9-d14d67f6c4a6> in <module>()
----> 1 2000 / 0

ZeroDivisionError: division by zero

Python tries to tell you where it stopped understanding, but in the above examples, each program is only 1 line long.

It also tries to show you where on the line the problem happened with caret ("^").

Finally it tells you the type of thing that went wrong, (NameError, SyntaxError, ZeroDivisionError) and a bit more information like "name 'gibberish' is not defined" or "unexpected EOF while parsing".

Unfortunately you might not find "unexpected EOF while parsing" too helpful. EOF stands for End of File, but what file? What is parsing? Python does it's best, but it does take a bit of time to develop a knack for what these messages mean. If you run into an error you don't understand please ask.

4. Strings

We have already seen a string literal in Python, "Hello, World!"

"Hello, World!"
'Hello, World!'

Text literals are surrounded by quotes. Without the quotes Hello by itself would be viewed as a variable name. You can use either double quotes (") or single quotes (') for text literals. Note that in python a character is just a string with 1 character and isn't a different data type.

Let's use strings:

print("Hello " * 5)
print("Hello" + "World")
Hello Hello Hello Hello Hello 
HelloWorld
print("Hello " * 8)
print("Hello" + "world")
Hello Hello Hello Hello Hello Hello Hello Hello 
Helloworld

Strings in Python are a bit more complicated because they have their own functions (operations to perform on them) which we can call to modify them:

print("Uppercase:", "Hello world".upper())
print("Lowercase:", "Hello world".lower())
print()

print("Strip all whitespace at end of string:", "Hello           ".strip(), "world")
print("Find location of 'world' in string:", "Hello world".find('world'))
print()

# C like string formatting where the "%s" is replaced by the values given
val = "Hello %s"
replaced_val = val % "world"
print("String formatting with %s like C:", replaced_val)

# We can have template strings where parts are specified on the fly
val = "Hello {name}"
replaced_val = val.format(name="world")
print("String formatting with .format():", replaced_val)
Uppercase: HELLO WORLD
Lowercase: hello world

Strip all whitespace at end of string: Hello world
Find location of 'world' in string: 6

String formatting with %s like C: Hello world
String formatting with .format(): Hello world
print("Uppercase","Hello World".upper())
print("Lowercase","Hello Wolrd".lower())
print("Complete the sentence:","Hello      ".strip(),"World")
print("Find the location in string", "Hello World".find('World'))

val = "Hello %s"
replaced_val = val % "world"
print("string formatting with %s like C:", replaced_val )
val = "Hello {name}"
replaced_val = val.format(name="world")
print("String ormatting with .format():", replaced_val)
Uppercase HELLO WORLD
Lowercase hello wolrd
Complete the sentence: Hello World
Find the location in string 6
string formatting with %s like C: Hello world
String ormatting with .format(): Hello world

To convert another data type like float, integer, etc into a string, you can use the str() function:

str(1.0) + " cm"
'1.0 cm'
str(44.089393) + "cm"
'44.089393cm'

Indexed by Zero

For better or worse, everything in Python is index by 0 like C/C++ or Java. We will see this over and over again but for now if you call format like this:

"{0} like {1}".format("I", 'Python')
'I like Python'
"{0} like {1}".format("I", 'Cricket')
'I like Cricket'

We would call "I" the 0th string passed into the function .format() and 'Python' the 1st.

Multi line strings

Frequently you will have a string which spans multiple lines. There are 3 ways to handle this in python:

  • Use a backslash
  • Triple quotes
# Using a backslash is similar to C, where the compiler knows the line is being continued in the next line
print("this is \
a multiline string")

# In multiline strings, even newlines (\n) is added as a character. So, a newline gets printed.
print("""this is
a multiline string""")
this is a multiline string
this is
a multiline string
print("Let \
cut it")
print("""Move
      forward""")
Let cut it
Move
      forward

Exercise: Use the help() function

To find more about a specific enity in python, simply do help(<entity>). Try it out!

  • Find the help for the find() function we used earlier by typing help("abcd".find). How many arguments does it have ?
  • Find the help for the find() function and check what the function split() does ?
help(int)
Help on class int in module builtins:

class int(object)
 |  int(x=0) -> integer
 |  int(x, base=10) -> integer
 |  
 |  Convert a number or string to an integer, or return 0 if no arguments
 |  are given.  If x is a number, return x.__int__().  For floating point
 |  numbers, this truncates towards zero.
 |  
 |  If x is not a number or if base is given, then x must be a string,
 |  bytes, or bytearray instance representing an integer literal in the
 |  given base.  The literal can be preceded by '+' or '-' and be surrounded
 |  by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
 |  Base 0 means to interpret the base from the string as an integer literal.
 |  >>> int('0b100', base=0)
 |  4
 |  
 |  Methods defined here:
 |  
 |  __abs__(self, /)
 |      abs(self)
 |  
 |  __add__(self, value, /)
 |      Return self+value.
 |  
 |  __and__(self, value, /)
 |      Return self&value.
 |  
 |  __bool__(self, /)
 |      self != 0
 |  
 |  __ceil__(...)
 |      Ceiling of an Integral returns itself.
 |  
 |  __divmod__(self, value, /)
 |      Return divmod(self, value).
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __float__(self, /)
 |      float(self)
 |  
 |  __floor__(...)
 |      Flooring an Integral returns itself.
 |  
 |  __floordiv__(self, value, /)
 |      Return self//value.
 |  
 |  __format__(...)
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __getnewargs__(...)
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __hash__(self, /)
 |      Return hash(self).
 |  
 |  __index__(self, /)
 |      Return self converted to an integer, if self is suitable for use as an index into a list.
 |  
 |  __int__(self, /)
 |      int(self)
 |  
 |  __invert__(self, /)
 |      ~self
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __lshift__(self, value, /)
 |      Return self<<value.
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __mod__(self, value, /)
 |      Return self%value.
 |  
 |  __mul__(self, value, /)
 |      Return self*value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __neg__(self, /)
 |      -self
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __or__(self, value, /)
 |      Return self|value.
 |  
 |  __pos__(self, /)
 |      +self
 |  
 |  __pow__(self, value, mod=None, /)
 |      Return pow(self, value, mod).
 |  
 |  __radd__(self, value, /)
 |      Return value+self.
 |  
 |  __rand__(self, value, /)
 |      Return value&self.
 |  
 |  __rdivmod__(self, value, /)
 |      Return divmod(value, self).
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __rfloordiv__(self, value, /)
 |      Return value//self.
 |  
 |  __rlshift__(self, value, /)
 |      Return value<<self.
 |  
 |  __rmod__(self, value, /)
 |      Return value%self.
 |  
 |  __rmul__(self, value, /)
 |      Return value*self.
 |  
 |  __ror__(self, value, /)
 |      Return value|self.
 |  
 |  __round__(...)
 |      Rounding an Integral returns itself.
 |      Rounding with an ndigits argument also returns an integer.
 |  
 |  __rpow__(self, value, mod=None, /)
 |      Return pow(value, self, mod).
 |  
 |  __rrshift__(self, value, /)
 |      Return value>>self.
 |  
 |  __rshift__(self, value, /)
 |      Return self>>value.
 |  
 |  __rsub__(self, value, /)
 |      Return value-self.
 |  
 |  __rtruediv__(self, value, /)
 |      Return value/self.
 |  
 |  __rxor__(self, value, /)
 |      Return value^self.
 |  
 |  __sizeof__(...)
 |      Returns size in memory, in bytes
 |  
 |  __str__(self, /)
 |      Return str(self).
 |  
 |  __sub__(self, value, /)
 |      Return self-value.
 |  
 |  __truediv__(self, value, /)
 |      Return self/value.
 |  
 |  __trunc__(...)
 |      Truncating an Integral returns itself.
 |  
 |  __xor__(self, value, /)
 |      Return self^value.
 |  
 |  bit_length(...)
 |      int.bit_length() -> int
 |      
 |      Number of bits necessary to represent self in binary.
 |      >>> bin(37)
 |      '0b100101'
 |      >>> (37).bit_length()
 |      6
 |  
 |  conjugate(...)
 |      Returns self, the complex conjugate of any int.
 |  
 |  from_bytes(...) from builtins.type
 |      int.from_bytes(bytes, byteorder, *, signed=False) -> int
 |      
 |      Return the integer represented by the given array of bytes.
 |      
 |      The bytes argument must either support the buffer protocol or be an
 |      iterable object producing bytes.  Bytes and bytearray are examples of
 |      built-in objects that support the buffer protocol.
 |      
 |      The byteorder argument determines the byte order used to represent the
 |      integer.  If byteorder is 'big', the most significant byte is at the
 |      beginning of the byte array.  If byteorder is 'little', the most
 |      significant byte is at the end of the byte array.  To request the native
 |      byte order of the host system, use `sys.byteorder' as the byte order value.
 |      
 |      The signed keyword-only argument indicates whether two's complement is
 |      used to represent the integer.
 |  
 |  to_bytes(...)
 |      int.to_bytes(length, byteorder, *, signed=False) -> bytes
 |      
 |      Return an array of bytes representing an integer.
 |      
 |      The integer is represented using length bytes.  An OverflowError is
 |      raised if the integer is not representable with the given number of
 |      bytes.
 |      
 |      The byteorder argument determines the byte order used to represent the
 |      integer.  If byteorder is 'big', the most significant byte is at the
 |      beginning of the byte array.  If byteorder is 'little', the most
 |      significant byte is at the end of the byte array.  To request the native
 |      byte order of the host system, use `sys.byteorder' as the byte order value.
 |      
 |      The signed keyword-only argument determines whether two's complement is
 |      used to represent the integer.  If signed is False and a negative integer
 |      is given, an OverflowError is raised.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  denominator
 |      the denominator of a rational number in lowest terms
 |  
 |  imag
 |      the imaginary part of a complex number
 |  
 |  numerator
 |      the numerator of a rational number in lowest terms
 |  
 |  real
 |      the real part of a complex number

help(str)
Help on class str in module builtins:

class str(object)
 |  str(object='') -> str
 |  str(bytes_or_buffer[, encoding[, errors]]) -> str
 |  
 |  Create a new string object from the given object. If encoding or
 |  errors is specified, then the object must expose a data buffer
 |  that will be decoded using the given encoding and error handler.
 |  Otherwise, returns the result of object.__str__() (if defined)
 |  or repr(object).
 |  encoding defaults to sys.getdefaultencoding().
 |  errors defaults to 'strict'.
 |  
 |  Methods defined here:
 |  
 |  __add__(self, value, /)
 |      Return self+value.
 |  
 |  __contains__(self, key, /)
 |      Return key in self.
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __format__(...)
 |      S.__format__(format_spec) -> str
 |      
 |      Return a formatted version of S as described by format_spec.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __getitem__(self, key, /)
 |      Return self[key].
 |  
 |  __getnewargs__(...)
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __hash__(self, /)
 |      Return hash(self).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __mod__(self, value, /)
 |      Return self%value.
 |  
 |  __mul__(self, value, /)
 |      Return self*value.n
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __rmod__(self, value, /)
 |      Return value%self.
 |  
 |  __rmul__(self, value, /)
 |      Return self*value.
 |  
 |  __sizeof__(...)
 |      S.__sizeof__() -> size of S in memory, in bytes
 |  
 |  __str__(self, /)
 |      Return str(self).
 |  
 |  capitalize(...)
 |      S.capitalize() -> str
 |      
 |      Return a capitalized version of S, i.e. make the first character
 |      have upper case and the rest lower case.
 |  
 |  casefold(...)
 |      S.casefold() -> str
 |      
 |      Return a version of S suitable for caseless comparisons.
 |  
 |  center(...)
 |      S.center(width[, fillchar]) -> str
 |      
 |      Return S centered in a string of length width. Padding is
 |      done using the specified fill character (default is a space)
 |  
 |  count(...)
 |      S.count(sub[, start[, end]]) -> int
 |      
 |      Return the number of non-overlapping occurrences of substring sub in
 |      string S[start:end].  Optional arguments start and end are
 |      interpreted as in slice notation.
 |  
 |  encode(...)
 |      S.encode(encoding='utf-8', errors='strict') -> bytes
 |      
 |      Encode S using the codec registered for encoding. Default encoding
 |      is 'utf-8'. errors may be given to set a different error
 |      handling scheme. Default is 'strict' meaning that encoding errors raise
 |      a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
 |      'xmlcharrefreplace' as well as any other name registered with
 |      codecs.register_error that can handle UnicodeEncodeErrors.
 |  
 |  endswith(...)
 |      S.endswith(suffix[, start[, end]]) -> bool
 |      
 |      Return True if S ends with the specified suffix, False otherwise.
 |      With optional start, test S beginning at that position.
 |      With optional end, stop comparing S at that position.
 |      suffix can also be a tuple of strings to try.
 |  
 |  expandtabs(...)
 |      S.expandtabs(tabsize=8) -> str
 |      
 |      Return a copy of S where all tab characters are expanded using spaces.
 |      If tabsize is not given, a tab size of 8 characters is assumed.
 |  
 |  find(...)
 |      S.find(sub[, start[, end]]) -> int
 |      
 |      Return the lowest index in S where substring sub is found,
 |      such that sub is contained within S[start:end].  Optional
 |      arguments start and end are interpreted as in slice notation.
 |      
 |      Return -1 on failure.
 |  
 |  format(...)
 |      S.format(*args, **kwargs) -> str
 |      
 |      Return a formatted version of S, using substitutions from args and kwargs.
 |      The substitutions are identified by braces ('{' and '}').
 |  
 |  format_map(...)
 |      S.format_map(mapping) -> str
 |      
 |      Return a formatted version of S, using substitutions from mapping.
 |      The substitutions are identified by braces ('{' and '}').
 |  
 |  index(...)
 |      S.index(sub[, start[, end]]) -> int
 |      
 |      Like S.find() but raise ValueError when the substring is not found.
 |  
 |  isalnum(...)
 |      S.isalnum() -> bool
 |      
 |      Return True if all characters in S are alphanumeric
 |      and there is at least one character in S, False otherwise.
 |  
 |  isalpha(...)
 |      S.isalpha() -> bool
 |      
 |      Return True if all characters in S are alphabetic
 |      and there is at least one character in S, False otherwise.
 |  
 |  isdecimal(...)
 |      S.isdecimal() -> bool
 |      
 |      Return True if there are only decimal characters in S,
 |      False otherwise.
 |  
 |  isdigit(...)
 |      S.isdigit() -> bool
 |      
 |      Return True if all characters in S are digits
 |      and there is at least one character in S, False otherwise.
 |  
 |  isidentifier(...)
 |      S.isidentifier() -> bool
 |      
 |      Return True if S is a valid identifier according
 |      to the language definition.
 |      
 |      Use keyword.iskeyword() to test for reserved identifiers
 |      such as "def" and "class".
 |  
 |  islower(...)
 |      S.islower() -> bool
 |      
 |      Return True if all cased characters in S are lowercase and there is
 |      at least one cased character in S, False otherwise.
 |  
 |  isnumeric(...)
 |      S.isnumeric() -> bool
 |      
 |      Return True if there are only numeric characters in S,
 |      False otherwise.
 |  
 |  isprintable(...)
 |      S.isprintable() -> bool
 |      
 |      Return True if all characters in S are considered
 |      printable in repr() or S is empty, False otherwise.
 |  
 |  isspace(...)
 |      S.isspace() -> bool
 |      
 |      Return True if all characters in S are whitespace
 |      and there is at least one character in S, False otherwise.
 |  
 |  istitle(...)
 |      S.istitle() -> bool
 |      
 |      Return True if S is a titlecased string and there is at least one
 |      character in S, i.e. upper- and titlecase characters may only
 |      follow uncased characters and lowercase characters only cased ones.
 |      Return False otherwise.
 |  
 |  isupper(...)
 |      S.isupper() -> bool
 |      
 |      Return True if all cased characters in S are uppercase and there is
 |      at least one cased character in S, False otherwise.
 |  
 |  join(...)
 |      S.join(iterable) -> str
 |      
 |      Return a string which is the concatenation of the strings in the
 |      iterable.  The separator between elements is S.
 |  
 |  ljust(...)
 |      S.ljust(width[, fillchar]) -> str
 |      
 |      Return S left-justified in a Unicode string of length width. Padding is
 |      done using the specified fill character (default is a space).
 |  
 |  lower(...)
 |      S.lower() -> str
 |      
 |      Return a copy of the string S converted to lowercase.
 |  
 |  lstrip(...)
 |      S.lstrip([chars]) -> str
 |      
 |      Return a copy of the string S with leading whitespace removed.
 |      If chars is given and not None, remove characters in chars instead.
 |  
 |  partition(...)
 |      S.partition(sep) -> (head, sep, tail)
 |      
 |      Search for the separator sep in S, and return the part before it,
 |      the separator itself, and the part after it.  If the separator is not
 |      found, return S and two empty strings.
 |  
 |  replace(...)
 |      S.replace(old, new[, count]) -> str
 |      
 |      Return a copy of S with all occurrences of substring
 |      old replaced by new.  If the optional argument count is
 |      given, only the first count occurrences are replaced.
 |  
 |  rfind(...)
 |      S.rfind(sub[, start[, end]]) -> int
 |      
 |      Return the highest index in S where substring sub is found,
 |      such that sub is contained within S[start:end].  Optional
 |      arguments start and end are interpreted as in slice notation.
 |      
 |      Return -1 on failure.
 |  
 |  rindex(...)
 |      S.rindex(sub[, start[, end]]) -> int
 |      
 |      Like S.rfind() but raise ValueError when the substring is not found.
 |  
 |  rjust(...)
 |      S.rjust(width[, fillchar]) -> str
 |      
 |      Return S right-justified in a string of length width. Padding is
 |      done using the specified fill character (default is a space).
 |  
 |  rpartition(...)
 |      S.rpartition(sep) -> (head, sep, tail)
 |      
 |      Search for the separator sep in S, starting at the end of S, and return
 |      the part before it, the separator itself, and the part after it.  If the
 |      separator is not found, return two empty strings and S.
 |  
 |  rsplit(...)
 |      S.rsplit(sep=None, maxsplit=-1) -> list of strings
 |      
 |      Return a list of the words in S, using sep as the
 |      delimiter string, starting at the end of the string and
 |      working to the front.  If maxsplit is given, at most maxsplit
 |      splits are done. If sep is not specified, any whitespace string
 |      is a separator.
 |  
 |  rstrip(...)
 |      S.rstrip([chars]) -> str
 |      
 |      Return a copy of the string S with trailing whitespace removed.
 |      If chars is given and not None, remove characters in chars instead.
 |  
 |  split(...)
 |      S.split(sep=None, maxsplit=-1) -> list of strings
 |      
 |      Return a list of the words in S, using sep as the
 |      delimiter string.  If maxsplit is given, at most maxsplit
 |      splits are done. If sep is not specified or is None, any
 |      whitespace string is a separator and empty strings are
 |      removed from the result.
 |  
 |  splitlines(...)
 |      S.splitlines([keepends]) -> list of strings
 |      
 |      Return a list of the lines in S, breaking at line boundaries.
 |      Line breaks are not included in the resulting list unless keepends
 |      is given and true.
 |  
 |  startswith(...)
 |      S.startswith(prefix[, start[, end]]) -> bool
 |      
 |      Return True if S starts with the specified prefix, False otherwise.
 |      With optional start, test S beginning at that position.
 |      With optional end, stop comparing S at that position.
 |      prefix can also be a tuple of strings to try.
 |  
 |  strip(...)
 |      S.strip([chars]) -> str
 |      
 |      Return a copy of the string S with leading and trailing
 |      whitespace removed.
 |      If chars is given and not None, remove characters in chars instead.
 |  
 |  swapcase(...)
 |      S.swapcase() -> str
 |      
 |      Return a copy of S with uppercase characters converted to lowercase
 |      and vice versa.
 |  
 |  title(...)
 |      S.title() -> str
 |      
 |      Return a titlecased version of S, i.e. words start with title case
 |      characters, all remaining cased characters have lower case.
 |  
 |  translate(...)
 |      S.translate(table) -> str
 |      
 |      Return a copy of the string S, where all characters have been mapped
 |      through the given translation table, which must be a mapping of
 |      Unicode ordinals to Unicode ordinals, strings, or None.
 |      Unmapped characters are left untouched. Characters mapped to None
 |      are deleted.
 |  
 |  upper(...)
 |      S.upper() -> str
 |      
 |      Return a copy of S converted to uppercase.
 |  
 |  zfill(...)
 |      S.zfill(width) -> str
 |      
 |      Pad a numeric string S with zeros on the left, to fill a field
 |      of the specified width. The string S is never truncated.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  maketrans(x, y=None, z=None, /)
 |      Return a translation table usable for str.translate().
 |      
 |      If there is only one argument, it must be a dictionary mapping Unicode
 |      ordinals (integers) or characters to Unicode ordinals, strings or None.
 |      Character keys will be then converted to ordinals.
 |      If there are two arguments, they must be strings of equal length, and
 |      in the resulting dictionary, each character in x will be mapped to the
 |      character at the same position in y. If there is a third argument, it
 |      must be a string, whose characters will be mapped to None in the result.

Exercise: String functions

  • Use the <str>.replace() function replace the text in "Hello world" to "Hello python"
  • Use the <str>.zfill() function to take an integer and pad it with zeroes to make it 5 characters long: Define a = 512 and convert it to "00512"
  • Use the <str>.startswith() function to check if a string starts with a vowel. Run it on "python" and "india".
help("abc".replace)
Help on built-in function replace:

replace(...) method of builtins.str instance
    S.replace(old, new[, count]) -> str
    
    Return a copy of S with all occurrences of substring
    old replaced by new.  If the optional argument count is
    given, only the first count occurrences are replaced.

s = "Hello World"
s.replace("World","Python ")
'Hello Python '
help("cc".zfill)
Help on built-in function zfill:

zfill(...) method of builtins.str instance
    S.zfill(width) -> str
    
    Pad a numeric string S with zeros on the left, to fill a field
    of the specified width. The string S is never truncated.

s = "55"
(s.zfill(5))
'00055'
help("sss".startswith)
Help on built-in function startswith:

startswith(...) method of builtins.str instance
    S.startswith(prefix[, start[, end]]) -> bool
    
    Return True if S starts with the specified prefix, False otherwise.
    With optional start, test S beginning at that position.
    With optional end, stop comparing S at that position.
    prefix can also be a tuple of strings to try.

"python".startswith("a") or "python".startswith("e") or "python".startswith("i") or "python".startswith("o") or "python".startswith("u")
False
"india".startswith("a") or "india".startswith("e") or "india".startswith("i") or "india".startswith("o") or "india".startswith("u")
True

5. If Else

Like all languages, Python allows us to conditionally run code.

To have an if condition we need the idea of something being true and something being false. We have True or False as "boolean" values. True would represent OK where as false would represent No or Cancel.

False is False
True
True is False
False
True is False
False

We can write expressions with operations too.

1 > 2
False
"Cool".startswith("C")
True
"Cool".endswith("C")
False
"oo" in "Cool"
True
42 == 1 # note the double equals sign for equality
False

In order to write an "if" statement we need code that spans multiple lines

if condition:
    print("Condition is True")
else:
    print("Condition is False")

Some things to notice. The if condition ends in a colon (":"). In Python blocks of code are indicated with a colon (":") and are grouped by white space. Notice the else also ends with a colon (":"), "else:". Let's try changing the condition and see what happens.

condition = (1 > 2)
if condition:
    print("Condition is True")
else:
    print("Condition is False")
Condition is False

About that white space, consider the following code:

if condition:
    print("Condition is True")
else:
    print("Condition is False")
print("Condition is True or False, either way this is outputted")

Since the last print statement isn't indented it gets run after the if block or the else block.

You can play with this. Try indenting the last print statement below and see what happens.

condition = True
if condition:
    print("Condition is True")
else:
    print("Condition is False")
print("Condition is True or False, either way this is outputted")
Condition is True
Condition is True or False, either way this is outputted

You can also use "and", "or", "not" to combine conditions (No ugly && and || here):

True and True is True
True and False is False
False and True is False
False and False is False
not True is False

Exercise - Boolean values

Below change the values of the three variables to make the entire "if condition" true.

# Edit the values of these 3 variables
boolean_literal = True
number = 12
string_literal = "I like to count cows before bed."

# Don't change the code below this
if number > 10 and boolean_literal and "cows" in string_literal:
    print("Success!")
else:
    print("Try again!")
Success!

6. Lists

So far we have numbers, strings, and conditional if statements. Now for our first container - a list.

A list in Python is just like an array or a linked list. They have a defined order and you can add to it or remove from it. Let's take a look at some simple lists.

# The empty list
empty_list = []
print(empty_list)
[]
breakfast_list = ["bread", "butter", "jam"]
print(breakfast_list)
['bread', 'butter', 'jam']
[1,2,3]
[1, 2, 3]

List literals are all about square brackets ("[ ]") and commas (","). You can create a list of literals by wrapping them in square brackets and separating them with commas.

As python doesn't care too much about data types, you can even mix different types of things into the same list; numbers, strings, booleans (unlike arrays)

[True, 0, "String"]
[True, 0, 'String']

We can put variables into a list and set a variable to a list.

wikipedia = "wikipedia.org"
wiki_sites_list = ["wikidata.org", wikipedia]
print(wiki_sites_list)
['wikidata.org', 'wikipedia.org']

Like strings, lists have their own operations. "append" is an interesting one. "append" lets you add an item to the end of a list.

wiki_sites_list = ["wikidata.org", "wikipedia.org"]
wiki_sites_list.append("wikisource.org")
print(wiki_sites_list)
['wikidata.org', 'wikipedia.org', 'wikisource.org']
wiki_sites_list[0]
'wikidata.org'

There is that 0 indexing again. The first element of the list is given index value 0.

print("These are some of the wikimedia sites: {0}, {1}, {2}".format(wiki_sites_list[0], wiki_sites_list[1], wiki_sites_list[2]))
These are some of the wikimedia sites: wikidata.org, wikipedia.org, wikisource.org

Length

We can also find the lenght of the list using the len function:

len(wiki_sites_list)
3

Splicing

Parts of a list can also be gotten using the splice operator. To use the splice operator, square brackets with color (:) is used:

nums = [0,1,2,3,4,5]
print("2:4 ->", nums[2:4])  # Takes everything 2nd to 3rd term
print("1:3 ->", nums[1:3])  # Takes everything from 1st to 2nd term
print("2: ->", nums[2:])    # Takes everything from 2nd to last term
print(":4 ->", nums[:4])    # Takes everything from start to the 3rd term
print(":-2 ->", nums[:-2])  # Takes everything from start to the 3rd last term
2:4 -> [2, 3]
1:3 -> [1, 2]
2: -> [2, 3, 4, 5]
:4 -> [0, 1, 2, 3]
:-2 -> [0, 1, 2, 3]
nums = [0,1,2,3,4,5,6,7,8,9,]
print("2:4 ->", nums[2:4])
print("2:4 ->", nums[2:])
print("2:4 ->", nums[:4])
print("2:4 ->", nums[:-3])
2:4 -> [2, 3]
2:4 -> [2, 3, 4, 5, 6, 7, 8, 9]
2:4 -> [0, 1, 2, 3]
2:4 -> [0, 1, 2, 3, 4, 5, 6]

Exercise - Use the functions .join() and .split()

  • Use the <str>.join() function to join a list of items using a comma: Convert ["bread", "butter", "jam"] -> "bread, butter, jam"
  • Use the <str>.split() function to split a string into a list: Convert back the earlier "bread, butter, jam" -> ["bread", "butter", "jam"]
help(str.join)
Help on method_descriptor:

join(...)
    S.join(iterable) -> str
    
    Return a string which is the concatenation of the strings in the
    iterable.  The separator between elements is S.

s = ","
seq = ["bread", "butter", "jam"]
(s.join(seq))
'bread,butter,jam'
help(str.split)
Help on method_descriptor:

split(...)
    S.split(sep=None, maxsplit=-1) -> list of strings
    
    Return a list of the words in S, using sep as the
    delimiter string.  If maxsplit is given, at most maxsplit
    splits are done. If sep is not specified or is None, any
    whitespace string is a separator and empty strings are
    removed from the result.

s = "bread,butter,jam"
s.split(",")
['bread', 'butter', 'jam']

7. Loops

Indexes are useful, but lists really shine when you start looping.

Loops let you do something for each item in a list. They look like this:

for item in my_list:
    print(item)  # Do any action per item in the list

"for" and "in" are required. "my_list" can be any variable or literal which is like a list. "item" is the name you want to give each item of the list in the indented block as you iterate through. We call each step where item has a new value an iteration.

for num in [1, 2, 3]:
    print(num)
1
2
3

In python, we don't use constructs like for ( int i = 0; i < maximum; i++ ) which are just confusing, rather we create a "range" of integers from 0 to maximum and loop over that:

maximum = 10
print("This is the range object:", range(0, maximum))
print("This is what it creates when it's used in a loop or other functions:", list(range(0, maximum)))
This is the range object: range(0, 10)
This is what it creates when it's used in a loop or other functions: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

And then we can use that with a loop to print a list of squares.

for number in range(0, 10):
    print("{0} squared is {1}".format(number, number*number))
0 squared is 0
1 squared is 1
2 squared is 4
3 squared is 9
4 squared is 16
5 squared is 25
6 squared is 36
7 squared is 49
8 squared is 64
9 squared is 81

8. Dictionaries

We have come a long way! Just one more section. Dictionaries are another container like lists, but instead of being index by a number like 0 or 1 it is indexed by a key which can be almost anything. The name comes from being able to use it to represent a dictionary.

List literals use square brackets ("[]") but dictionaries use braces ("{}").

{"wikipedia.org": "The free encyclopedia", 
 "wikisource.org": "The free library"}

In a dictionary the key comes first followed by a colon (":") than the value then a comma (",") then another key and so on. This is one situation where a colon doesn't start a block.

wiki_sites = {
    "wikipedia.org": "The free encyclopedia", 
    "wikisource.org": "The free library"
}
wiki_sites["wikipedia.org"]
'The free encyclopedia'

We can loop over the keys in a dictionary to list all of our definitions...

for key in wiki_sites:
    print('The Key is "{0}" and the value is "{1}"'.format(key, wiki_sites[key]))
The Key is "wikisource.org" and the value is "The free library"
The Key is "wikipedia.org" and the value is "The free encyclopedia"

In fact, dictionaries can contain any type of values. Hence, you can have a list as the value of a dictionary:

wiki_language_sites = {
    "wikipedia.org": ["en.wikipedia.org", "ml.wikipedia.org", "ta.wikipedia.org"], 
    "wikisource.org": ["en.wikisource.org", "ml.wikisource.org", "ta.wikisource.org"]
}
wiki_language_sites["wikipedia.org"]
['en.wikipedia.org', 'ml.wikipedia.org', 'ta.wikipedia.org']

We can also get the keys and values as lists:

print("Keys:", list(wiki_sites.keys()))
print("Values:", list(wiki_sites.values()))
Keys: ['wikisource.org', 'wikipedia.org']
Values: ['The free library', 'The free encyclopedia']

Exercise: Loop over the keys, values, and items

  • Loop over a dict using for key in <dict>: and print all the keys. Verify that the same result is got when using for key in <dict>.keys().
  • Loop over the values of a dict by first getting all the values using the <dict>.values() function.
  • Loop over the keys and values of a dict by requesting 2 items from the list. Do for key, val in dict.items(): and try printing both the key and value side by side.
loon = { "wikipedia.org": "The free encyclopedia", 
         "wikisource.org": "The free library"
   
        }
for key in loon:
    print('key is "{1}"'.format(key,loon[key]))
for key in loon.keys():
    print('key is "{1}"'.format(key,loon[key]))
key is "The free library"
key is "The free encyclopedia"
key is "The free library"
key is "The free encyclopedia"
loon = { "wikipedia.org": "The free encyclopedia", 
         "wikisource.org": "The free library"
   
        }



for key in loon.values():
    print("Values:", list(loon.values()))
    
   
Values: ['The free library', 'The free encyclopedia']
Values: ['The free library', 'The free encyclopedia']
loon = { "wikipedia.org": "The free encyclopedia", 
         "wikisource.org": "The free library"
   
        }
for key in loon:
    print("keys:", list(loon.keys()))
for key in loon:
    print("values",list(loon.values()))
keys: ['wikisource.org', 'wikipedia.org']
keys: ['wikisource.org', 'wikipedia.org']
values ['The free library', 'The free encyclopedia']
values ['The free library', 'The free encyclopedia']

9. Functions

To avoid code repetition and to break code into smaller intelligible blocks, functions are useful. A function takes in certain variables (arguments) and gives out a return value. The arguments and return value do not need to define a data type again, as python isn't too worried about data types.

A function is defined using the def keyword:

def <function name>(<arg1>, <arg2>, <arg3>, ..., <argN>):
    <function body>
    return <value to return>
def sum2(x1, x2):
    return x1 + x2

print("2 + 3 =", sum2(2, 3))
print('"a" + "b" =', sum2("a", "b"))
print('[1] + [2,3] =', sum2([1], [2, 3]))
2 + 3 = 5
"a" + "b" = ab
[1] + [2,3] = [1, 2, 3]

Because there's no data type for the arguments and return type, we can give strings, ints, floats, lists, etc. Anything that supports the + operator in our example. But if the arguments given cannot be operated on by + it gives an error. And it is the job of the developer of the function to ensure that the arguments given are sane:

def sum2(x1, x2):
    return x1 + x2

print('1 + "s" =', sum2(1, "s"))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-104-8cc7b8e2b21d> in <module>()
      2     return x1 + x2
      3 
----> 4 print('1 + "s" =', sum2(1, "s"))

<ipython-input-104-8cc7b8e2b21d> in sum2(x1, x2)
      1 def sum2(x1, x2):
----> 2     return x1 + x2
      3 
      4 print('1 + "s" =', sum2(1, "s"))

TypeError: unsupported operand type(s) for +: 'int' and 'str'

Python also supports assigning values using the argument name. When using argument names (also called keyword arguments - kwargs) the order of the arguments do not matter as the name of the argument is used. This gives flexibility, as you don't have to remember which argument comes first and which comes second! Let's see it in action:

def difference2(x1, x2):
    return x1 - x2

print("2 - 3 =", difference2(2, 3))
print("2 - 3 =", difference2(x1=2, x2=3))
print("2 - 3 =", difference2(x2=3, x1=2))  # We give x2 before x1
2 - 3 = -1
2 - 3 = -1
2 - 3 = -1

Not all functions need to return a value. And by default if a return value is not given, it returns None.

Functions can also have default values for arguments:

def power(base, exponent=2):
    return base ** exponent

print('5 power -1 is', power(5, -1))
print('power(5) uses exponent 2 by default:', power(5))
5 power -1 is 0.2
power(5) uses exponent 2 by default: 25

Exercise - Fibonacci function

Write a function that prints the values of the fibonacci series until a given maximum value. For example, print_fibonacci(10) would print:

>>> print_fibonacci(10)
1, 1, 2, 3, 5, 8
def fibonacci(n):
    a=0
    b=1
    c=0
    print(a)
    print(b)   
    while(c<n):
        c=a+b
        print(c)
        a=b
        b=c
fibonacci(10)
0
1
1
2
3
5
8
13

10. Classes

Python also has Object Oriented Programming (OOP) in it's structure. A class is a template which holds class variables and functions (methods) and operates on the class itself.

To use a class, objects which conform to the class template need to be created:

class WebSite:
    def __init__(self):
        self.url = ""
        self.description = ""

w1 = WebSite()  # w1 is an object of class WebSite
print("Type of w1:", type(w1))
w1.description = "The free encyclopedia"
w1.url = "wikipedia.org"
Type of w1: <class '__main__.WebSite'>

The __init__() function is the class constructor and can not return anything. The first argument self is the object that's being created. Hence, in our above class, when the object is created, it has 2 member variables url and description which are assigned to empty string ("") in the constructor.

Other than the variables created in the class, python can dynamically add more variables in the class:

w1.visits = 1000
w1.subsites = ["en.wikipedia.org", "ml.wikipedia.org", "ta.wikipedia.org"]
print(w1.visits, w1.subsites)
1000 ['en.wikipedia.org', 'ml.wikipedia.org', 'ta.wikipedia.org']

Classes can also define functions which can be used to perform functions using the object variables.

Object methods start with the keyword self normally. When w1.function() is used, the object before the period (w1) is passed to function()'s first argument. Hence, function() would be defined as def function(self): where self is a reference to the object itself.

Here's an example:

class WebSite:
    def __init__(self, description="", url=""):
        self.url = url
        self.description = description

    # A class method or a class function which can be called usingt the object using: <obj>.subsite("arg")
    # Which the class receives as: substitute(<obj>, "arg")
    def subsite(self, subname):
        return subname + "." + self.url

w1 = WebSite(description="The free encyclopedia", url="wikipedia.org")  # w1 is an object of class WebSite
w1.subsites = [w1.subsite('en'), w1.subsite('ml'), w1.subsite('ta')]
print(w1.subsites)
['en.wikipedia.org', 'ml.wikipedia.org', 'ta.wikipedia.org']
 
 

Exercise - Create a WebSite object for each item in the wiki_sites dictionary

Loop over every (key, value) pair in the dictionary wiki_sites and create a list of WebSite objects using the WebSite class.

  • The key of the dictionary should be stored in the url of the object.
  • The value of the dictionary should be stored in the description of the object.
# Leave the definitions of the class and dictionary as is
class WebSite:
    def __init__(self, description="", url=""):
        self.url = url
        self.description = description

wiki_sites = {
    "wikipedia.org": "The free encyclopedia", 
    "wikisource.org": "The free library"
}

# Write below. Loop over every item in wiki_sites and create a list of WebSite objects.
 

Exercise - Create a class to store a mediawiki page

Create a class which can store a mediawiki page (has content and name of the page) and create 2 functions to:

  1. Check if the page is empty (the content is an empty string ""): <obj>.empty() which returns True or False
  2. Generate the URL of the page using a base url: <obj>.url() returns "wikipedia.org/wiki/<page name>")

Below you will find some example code to test your class

# Add the class definition here

# The following should work:
page = WikiPage(name="MyPage", content="Hello there ! This is a small example page.")
print("The name of the page is:", page.name)
print("The page has the following content:")
print(page.content)
print("The url of the page is:", page.url())
if page.empty():
    print("The page is empty")
else:
    print("The page is not empty")