1/29
Looks like no tags are added yet.
Name | Mastery | Learn | Test | Matching | Spaced |
---|
No study sessions yet.
Launching the Python REPL
Simply execute python3 on your console. By default, this will open a REPL…
Each line you type is interpreted by a virtual machine and executed
The return value of that statement/expression is then printed on the screen
Ctrl -D to exit when you're done
This isn't great for large amounts of code though
Interpreters
An alternative to compilation
Just computer programs
They take each line of source code, parse it, and evaluate it
That code is then executed by your processor/VM immediately
Then it moves on to the next line
Python Command Line Interpreter
Running a Python Script
Simply put your code into a text file with a .py suffix
VS Code makes a good Python code editor too…
Then load that into the interpreter: python3
Note: There is no equivalent to main()
Note: There are no semi-colons…
Python uses newlines to terminate statements instead
Variables
not explicitly declared
automatically created when they are first used
this must be an assignment (=) operation… WHY?
attempting to access a variable that has not been created will create a runtime error
by convention variables are lower case, words separated by underscores
Dynamic Typing
Variables infer their type when they are assigned
Languages that do this are known as dynamic typed languages
Variables can therefore also change type during the duration of your program
Common Built-In Types
Python variables are typed…
You can't see the types quite so explicitly… but they are there…
You can attempt to convert any value to a give type by using the type name as a function
What is Truth?
Many things are true, but some aren’t
Python has explicit True and False constants
note the capitalization, c.f. Java
But many things are "truthy"
Numeric values other the zero
Non-empty data structures
Strings of length >0 • True
Anything that is not "truthy" is "falsy "
Semantic Whitespace
Some argue Python is simpler than other languages because it doesn't have curly brackets
This is only partially truthy
Open brackets just look like colons
Closed brackets are invisible
In Python, the indentation is not just decorative: it conveys meaning
Semantic whitespace applies to all conditionals, loop, functions…
Conditional Statements
if
match
if Statements
They test for any truthy value
Equality and inequality operators are identical to C style languages
< > <= >= ==
Boolean logic operators are different though:
and or not
(rather than && || !)
x = 4
y = 5
if (x < y or 1 == 0):
print("Woo hoo!")
else:
print("Boo hoo!")
Match Statements
Very similar to switch statements
Note the lack of break statements - these are implicit
The use of an underscore (_) for the default case
Some care: This is a very new feature to Python, only available in v3.10 upwards
x = 42
match x:
case "Lancaster":
print("Welcome")
case 42:
print("The answer at last!")
case _:
print("Hmm... can't help you there")
Loops
while
for
while Loop
They follow same syntax as if statements
Semantic whitespace defines which statements are inside or outside the loop
x = 42
while x > 40:
print("Wheeeeeee")
x = x - 1
print (x)
for Loop
The principles are the same as C style languages, but implementation is different
For loops operate only on some type of sequence. This can be a data structure or a range of values
Remember a range is a Python built -in type…
We can use the range() function to generate this:
range (end)
range (start, end)
range (start, end, step)
for i in range(5):
print(i)
#0, 1, 2, 3, 4
for i in range (3, 5):
print(i)
#3, 4
for i in range(3, 15, 2):
print(i)
#3, 5, 7, 9, 11, 13
end is exclusive
start defaults to 0
step defaults to 1
Functions
Declared using a slightly different syntax
Signature:
The def keyword is used to define a function
Functions are defined with no return type, and parameters also carry no defined type. Why?
Parameters
Comma separated and can be optional
Default values optionally may be assigned to parameters when defining the function
The code calling that function may then choose to include or omit that parameter
Return values are also optional
def do_something(times = 1):
for i in range(1, times + 1):
print("Hello")
do_something()
do something(10)
Lists
Lists use the familiar square brackets syntax to hold ordered collections of items
Lists are much more flexible than fixed length arrays
More like Java collections with language optimisations
Lists are created by simply using square brackets
Items are comma separated
Lists have no explicit type, so you can put any combination of types in a list that you like
We can use lists as a sequence parameter to a for loop instead of a range
Lists are mutable and of variable length
List Methods
remove(item)
pop(index)
clear()
can extract items/sublists from a list by using square brackets to define index/range we want
can use the append method to add an item to the end of a list
can use the insert method to add an item to an arbitrary location
x = [10, 42, -8, 42]
x.append(67)
x.pop(0)
y = x[0:2]
print(y)
Object References and Immutability
Everything in Python is an object reference
Much like Java…
So parameter passing of most variables into functions is like passing a reference
Numbers, Strings, Booleans (and Tuples) are treated as immutable, however
Therefore, any of these passed as parameters are effectively duplicated, just like value types in Java
def change_me(x):
x = x + 1
x = 10
change me(x)
print(x) #10
def change_me(x):
x.append(1)
x = [10]
change_me(x)
print(x) #[10,1]
Object Oriented Python
Python is inherently Object Oriented
But it takes a more relaxed approach, due to its adoption of dynamic typing
Python is also multi -paradigm, seamlessly mixing procedural and OO programming
It tries to get the best of both worlds…
Many Python modules (similar to packages in C++/Java) are Object Oriented
User programs tend to focus on integrating those modules to create solutions
This related to our Lego bricks analogy again, but is less purist than languages like Java…
Classes
They create first-class types just like C++/Java
This is a little hidden due to dynamic typing…
Defining a class takes a similar pattern to C++/Java
Use the class keyword followed by a name
Curiously, Python adopts a camel case convention for class names (but nothing else).
Note:
Constructors look different but have the same role as in any other OO language
There seems to be a weird extra parameter…
class Student:
def__init__(self, name, age):
self.name = name
self.age = age
davina = Student("Davina", 20)
Attributes
Attributes are dynamically created when assigned a value
self is required to be used to access attributes
Attributes are normally created only in the class constructor, but it is permitted in any method
Attributes are always visible outside the class - equivalent to public in C++/Java
Python relies on programmers to not fiddle with another programmer's attributes
Attributes with a leading underscore should be treated as private and left alone… but unlike C++/Java nothing will stop you from doing this
class Student:
def__init__(self, name, age):
self.name = name
self.age = age
def show(self):
print(self.name)
print(self.age)
davina = Student("Davina", 20)
davina.show()
Methods
Methods are defined like functions
Methods are invoked just like C++/Java
But there's that weird parameter again
Note:
self is required to be the first parameter in every method – including constructors
self is the exact equivalent to this in Java
self is implicitly filled in by the Python runtime when the method is invoked
self is required to be used to access methods and attributes internally
class Student:
university = "Lancaster"
def _init_(self, name, age):
self.name = name
self.age = age
def show(self):
print(self.name)
print(self.age)
print(Student.university)
def do_something():
print("Something")
davina = Student("Davina", 20)
joe = Student("Joe", 4)
Student.university = "Manchester"
davina.show()
joe.show()
Student.do_something()
Class Attributes and Methods
Attributes defined outside of a method are shared
They act like static variables in Java/C++
There is only one variable – all instances of the class share the same value
They can be accessed without an instance of the class being created
Functions declared inside classes are like static methods in C++/Java
They cannot access attributes
They can be invoked without an instance of the class being created
Note: This is a common thing to do by accident when starting out in OO Python
Inheritance
Python is a multiple inheritance language
Optionally define a parent class in brackets when defining the class
Comma separate them if there is more than one
super() can be used to refer to a parent class
We can use this to explicitly call the constructor in a parent class
Method overriding occurs by redefining the method, just like C++/Java
Method overloading is less common, due to dynamic typing
class Person:
def _init_(self, name, age):
self.name = name
self.age = age
def show(self)
print(self.name)
print(self.age)
class Student(Person):
def _init_(self, name, age, university):
super()._init_(name, age)
self.university = university
def show(self)
super().show()
print(self.university)
davina = Student("Davina", 20, "Lancaster")
davina.show()
Polymorphism
Python adopts a different approach to polymorphism
Polymorphism states that an instance of a class can be treated as a type of its super class
But what does that mean for dynamically typed languages, where we don’t name types at the point of use?
Python does not perform type checking. It just lets the code break
Python uses something called duck typing.
The capability of an object is defined by its exhibited capability – not its name
def do_something(o):
print(o.university)
davina = Student("Davina", 20, "Lancaster")
joe = Student("Joe", 4)
do_something(davina)
do_something(joe)
Error Handling
Programmers try to make their programs reliable
Thinking of all possible scenarios… thorough testing
…but programmers aren’t perfect.
Then there are things beyond our control
Hardware failures
Internal errors e.g. corrupt configuration files, etc…
Other programmers
Classical Solutions to Errors
There are several historic solutions to indicate errors have taken place
Methods could return a Boolean and indication of success
But what sort of error? What is the calling code going to do?
Methods could return an integer indicating success…
Error codes, e.g. 0 success, 1 out of memory…
Frequently employed (see errno.h in Unix!)
Can result in many additional lines of code
What about returning something useful?
We can only return one thing from a method…
r = do_something()
if r:
print("There is an error!")
if r == -1:
print("out of memory")
if r == -2:
print("too much cheese")
if r == -3:
print("it's wednesday")
r = do_something_else()
if not r:
#...
Exceptions
For exceptional circumstances
Language features / classes for supporting run-time error handling
Exceptions are control flow primitives, similar to an ‘if’ statement, but for ‘out of band’ events…
If an exception occurs during your program, control moves immediately from your normal code into a specified exception handler
These processes are known as raising and catching an exception respectively
Exceptions are not necessarily errors
try:
v = '2' + 2
except TypeError:
print("You can't do that!")
Python Exceptions
Two new keywords: try and except
We place code inside a try code block that we think could raise an exception
We follow that by an except code block. If an exception is raised inside the try block, control immediately jumps to the except block.
We can list multiple exception types to catch. The first matching except case is executed.
Once an exception handler has completed, the program will continue from the end of the try/except block
An unhandled exception propagates it way up the call stack. If it reaches the top, it prints to the console and exits your program
Raising Exceptions
Exceptions are a good thing
Bugs are a bad thing. Don’t mix them up
An exception is just an object
We can therefore create our own exceptions, just by creating a class that is a subclass of Exception
We can raise an exception with the raise keyword
As exceptions are just objects, they can also carry whatever addition contextual data we want as one or more attributes
class StillInBedError)Exception):
def _init_(self):
self.details = "SNORRE!!!"
def do_some_gardening(time):
if time < 9:
raise StillInBedError()
print("OK, OK... mowing..")
try:
do_some_gardening(5)
except StillInBedError as reason:
print("Aww, have a nice nap")
print(" " + reason.details)