Basic syntax

The Python syntax is uncomplicated whenever possible. Python is arguably one of the most syntactically natural programming languages.

Comments are used throughout this course, let's show a few examples.

In [1]:
# - This is a comment
# - It always begins by "#" (hash)
# - This is more or less quivalent to // in C++

a = 1 + 2  # A comment on a line with a code

"""Multiple line comments do not exist in Python,
unlike /* */ in C++ or Java. One can use multiline
strings, which are simply not assigned to any variable
and thus thrown away immediately. Docstring use this
approach as well, see below."""

Out[1]:
'Multiple line comments do not exist in Python,\nunlike /* */ in C++ or Java. One can use multiline\nstrings, which are simply not assigned to any variable\nand thus thrown away immediately. Docstring use this\napproach as well, see below.'

Plain commands¶

Simply try the following commands and their modifications.

In [2]:
a = 1 + 1.1 / 2    # the result of a simple calculation is assigned to a variable
print(type(a))     # print output (stdout)
a                  # IPython displays the last command result

<class 'float'>

Out[2]:
1.55
In [3]:
a = "Hello" + " world!"                                 # a simple string manipulation
print(a)
print("type(a) = %s, len(a) = %i" % (type(a), len(a)))  # a formated output

Hello world!
type(a) = <class 'str'>, len(a) = 12

In [4]:
import math                 # importing a module
a = math.cos(math.pi / 4)   # using the cos function and the pi constant
print('a = %.3g' % a)

a = 0.707

In [5]:
from math import sin, pi    # importing selected symbols from a module
a = sin(pi / 4)
print('a = %.3g' % a)

a = 0.707

In [6]:
a = cos(pi / 2)             # this should not work --> an exception is thrown

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-6-de2a57539a2d> in <module>()
----> 1 a = cos(pi / 2)             # this should not work --> an exception is thrown

NameError: name 'cos' is not defined

A few important builtin functions¶

There is not too many built-in functions in Python. Let us mention some of them (which we might have already encountered).

• dir -- list symbols (functions, variables, methods) in a given context
• eval -- evaluates a string as a code and returs the result
• help -- helps us (displays the 'docstring')
• len -- the lengths of something (a string, a list, etc.)
• open -- opens a file
• print -- prints out a string
• raw_input -- reads input from keyboard
• str, repr -- returns a text reprezentation of an object
• type -- returns the type of the argument

We shall get introduced to these and other built-in functions soon.

In [7]:
print(str(dir()) + "\n")  # display the current context (variables etc)
print(repr(dir("a")))     # display the symbols of a string object (attributes, methods)

['In', 'Out', '_', '_1', '_2', '__', '___', '__builtin__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_dh', '_i', '_i1', '_i2', '_i3', '_i4', '_i5', '_i6', '_i7', '_ih', '_ii', '_iii', '_oh', '_sh', 'a', 'exit', 'get_ipython', 'math', 'pi', 'quit', 'sin']

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']


We can demonstrate the raw_input and the eval power on a simple "calculator".

In [9]:
a = 2
e = input("a = {}\nEnter an expression that contains a: ".format(a))
print("{} = {}".format(e, eval(e)))

a = 2
Enter an expression that contains a: a * 2 + a**2
a * 2 + a**2 = 8


Python operators¶

+       -       *       **      /       //      %
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=      <>
or      and     not
is      is not  in      not in



Keywords¶

and       del       from      not       while
as        elif      global    or        with
assert    else      if        pass      yield
break     except    import    print
class     exec      in        raise
continue  finally   is        return
def       for       lambda    try

Conditions and other blocks in Python¶

Code blocks (or compound statements) consist of one or more ‘clauses.’ A clause consists of a header and a ‘suite.’ The clause headers of a particular compound statement are all at the same indentation level. Each clause header begins with a uniquely identifying keyword and ends with a colon. A suite is a group of statements controlled by a clause. A suite can be one or more semicolon-separated simple statements on the same line as the header, following the header’s colon, or it can be one or more indented statements on subsequent lines. Only the latter form of suite can contain nested compound statements; the following is illegal, mostly because it wouldn’t be clear to which if clause a following else clause would belong:

The indentation is arbitrary but it must be consistent across a single file. Strongly recommended are four spaces, as noticed in PEP 8 -- Style Guide for Python Code. This document contains important conventions, such as the name conventions for variables, functions, classes etc.

In [10]:
# this is an example of nested if statements
if 2 > 1:
print("It's true that 2 > 1")
if 1 > 2:
print("It's true that 2 > 1 and 1 > 2")
else:
print("It's true that 2 > 1 and not true that 1 > 2")
else:
print("It's not true that 2 > 1")

It's true that 2 > 1
It's true that 2 > 1 and not true that 1 > 2


For conditional statements we have if - elif - else. There is nothing like switch / case (as it can be easily substituted by multiple elif statements).

In [11]:
a = 5
if a > 10:
print("Cannot count this with fingers")
elif a > 5:
print("We need both hands' fingers to count this")
elif a >= 0:
print("This I can count with fingers on single hand")
else:
print("Cannot do negative numbers")

This I can count with fingers on single hand


while blocks use the same indentation rules:

In [12]:
a = 0
while a < 5:
print(a)
a += 1

0
1
2
3
4


while blocks (as well as for but we'll explain for later) can have an else part, which is executed when the condition is false.

In [13]:
a = 10
while a < 5:
print("a = %i" % a)
a += 1
else:
a = 1
print("The end, a = %i" % a)

The end, a = 1


break interrupts a cycle, continue makes a new iteration, i.e. skips the commands below.

In [14]:
a = 0
while a < 5:
a += 1
# even number are not printed
if a % 2 == 0:
continue
print("a = %i" % a)

a = 1
a = 3
a = 5


Let's find the largest three-digit number divisible by 19.

In [15]:
a = 999
while a > 0:
if a % 19 == 0:
print("The answer is: %i" % a)
break
a -= 1
else:
# break nespustí else část

The answer is: 988


Multiline expressions¶

We can split long lines using \.

In [16]:
a_long_variable_name = "this is just an abudantly long text, " + \
"that does not have any meaning ..."
a_long_variable_name

Out[16]:
'this is just an abudantly long text, that does not have any meaning ...'

We can (and should) often ommit \ in expressions inside parenthesis.

In [17]:
a_long_variable_name = (10000000000000 + 2222222222222222 + 9999999999999999 + 3987493874 +
444444444444444 + 23987342978 + 9874 + 555555555555555555 +
987349987 - 9999999999999999999)
a_long_variable_name

Out[17]:
-9431767748815581066

One should not create lines longer than 80 characters (see PEP8), although this convention is often not very convenient.

Function definition¶

Functions are defined using the def keyword.

In [18]:
# a simple function with any return value
def hello():
print("Hello Python!")
# now call our function
hello()

Hello Python!


This function is not very useful as usually we need inputs and/or outputs.

In [19]:
# subj is an argument of the hello function
def hello(subj):
phrase = "Hello %s!" % subj
# return stops the function execution and returns a value
return phrase
print(hello("Prague"))

Hello Prague!


We can distinguish positional and keyword (named) arguments. Arguments can have implicit values, which makes such arguments optional.

In [20]:
# greet is an argument with an implicit value
def hello(subj, greet="Hello"):
phrase = "%s %s!" % (greet, subj)
return phrase
# we don't specify the optional freet argument
print(hello("Prague"))
# we use greet as a keyword argument
# positional arguments must come first
print(hello("Praho", greet="Nazdar"))
# we can use both subj and greet as keyword arguments
print(hello(greet="Nazdar", subj="Praho"))

Hello Prague!
Nazdar Praho!
Nazdar Praho!


Functions can have variable arguments but we'll explain this later when we learn about containers.

Side effects of functions¶

Funtions can have side efects, i.e. they can change the input arguments, if these arguments are so called mutable. What mutable and immutable means will be explained later. Nonetheless, it's strongly recommended to avoid creating functions with side effects unless it has a good reason and the name of the function indicates this behaviour. Let us show an example of a function with side effects.

In [21]:
def my_function(l):
# l should be a list
l.append("I'm here")
print("l inside my_function")
print('l = {}'.format(l))
print("the original list")
x = ["in my list"]
print(x)
my_function(x)
print("x after calling my_function")
print(x)

the original list
['in my list']
l inside my_function
l = ['in my list', "I'm here"]
x after calling my_function
['in my list', "I'm here"]


An unpleasant surprise appears when an argument's implicit value is mutable.

In [22]:
def foo(l=[]):
# this is changing l
l.append("appended")
print(l)
# first call with an explicit input parameter
foo([])
# now using the implicit value - the result should be identical
foo()
# now repeat the previous calls again
# we expect the results are indetical but they aren't
foo([])
foo()

['appended']
['appended']
['appended']
['appended', 'appended']


We can avoid side effect by copying (either using the copy module or by copy methods). It is usually better to assing results to new variables, e.g.

In [23]:
def foo(l=[]):
p = l + ["appended"]
print(p)
# first call with an explicit input parameter
foo([])
# now using the implicit value - the result should be identical
foo()
# now repeat the previous calls again
# we expect the results are indetical
foo([])
foo()

['appended']
['appended']
['appended']
['appended']


Using modules¶

Complex codes are usually contained in modules. We can think about modules as simple containers with reusable functions, variables or classes. We will show how to create modules later; nevertheless using modules is basically unavoidable. The standard (built-in) Python library is in fact a collection of modules.

In [24]:
# import (i.e. read and use) the math module
import math
# cos is a function from the math module
print(math.cos(0))

1.0


Apart from importing whole modules we can import only certain symbols. To import the cos function:

In [25]:
from math import cos
# cos can be now called without math.
print(cos(0))

1.0


We can also import everything from a module by using from ... import *. We have to be careful if symbols from the module do not collide with other symbols as they would be overwritten.

In [26]:
from math import *
print(sin(pi/2))

1.0