1. Workshop
On
Python
S. Mahbub - Uz - Zaman (09301004)
BRAC UNIVERSITY
Department of Computer Science and Engineering
2.
3.
4. Introduction
● Python is a dynamic object-oriented programming language that can be used for many kinds of
software development.
● It offers strong support for integration with other languages and tools, comes with extensive standard
libraries
● Python runs on Windows, Linux/Unix, Mac OS X, OS/2, Amiga, Palm Handhelds, and Nokia mobile
phones. Python has also been ported to the Java and .NET virtual machines.
● Python is distributed under an OSI-approved open source license that makes it free to use, even for
commercial products.
● It appeared in 1991 and influenced by languages like ABC, ALGOL 68, C, C++, Dylan, Haskell, Icon,
Java, Lisp, Modula-3, and Perl. Python has a large and comprehensive standard library.
● Libraries like NumPy, SciPy and Matplotlib allow Python to be used effectively in scientific computing.
1
Current Version is 3.3.0
We will be using 2.7
[1] https://www.facebook.com/groups/274240126027143/doc/285833394867816/
5. Where can I use Python ?
– Web and Internet Development
– Database Access
– Desktop GUIs
– Scientific and Numeric Computing
– Education
– Network Programming
– Software Development – Game and 3D Graphics2
Google is powered by python3
[2] https://www.facebook.com/groups/274240126027143/doc/285870364864119/
[3] http://techreport.com/blog/16713/google-python-world-domination
7. Starting
You will start programming in IDLE which comes with Python. IDLE is a special text editors
Integrated Development Environment (IDE) which is a bit like Microsoft Word, except it
understands Python and helps you get your code right. IDLE is itself, a Python application 4
http://docs.python.org/ This web site is the definitive Python reference. The “Library
Reference” is probably the most useful as you start to learn more Python (and need to look
up details about functions that you have forgotten)5
[4] https://www.facebook.com/groups/274240126027143/doc/285863108198178/
[5] https://www.facebook.com/groups/274240126027143/doc/285859274865228/
8.
9. Python programs
• Program (or script) is a sequence of definitions and
commands
– Definitions evaluated and commands executed by
Python interpreter in a shell
– Can be typed directly into a shell, or stored in a
file that is read into the shell and evaluated
• Command (or statement) instructs interpreter to do
something
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
10. Objects
• At heart, programs will manipulate data objects
• Each object has a type that defines the kinds of things
programs can do to it
• Objects are:
– Scalar (i.e. cannot be subdivided), or
– Non-scalar (i.e. have internal structure that can be accessed)
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
11. Scalar objects
• int – used to represent integers, e.g., 5 or 10082!
• float – used to represent real numbers, e.g., 3.14 or 27.0
• bool – used to represent Boolean values True and False
• The built in Python function type returns the type of an
object
>>> type(3)
<type ‘int’>
>>> type(3.0)
<type ‘float’>
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
12. Expressions
• Objects and operators can be combined to form
expressions, each of which denotes an object of some type
• The syntax for most simple expressions is: –
<object> <operator> <object>
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
13. Operators on ints and floats
• i + j – sum – if both are ints, result is int,
if either is float, result is float
• i - j – difference
• i * j –product
• i / j – division – if both are ints, result is int,
representing quotient without remainder
• i % j – remainder
• i ** j – i raised to the power of j
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
14. Some simple examples
>>> 3 + 5
8
>>> 3.14 * 20
62.8
>>> (2 + 3)*4
20
>>> 2 + 3*4
14
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
15. Performing simple operations
• Parentheses define sub-computations – complete these to
get values before evaluating larger expression
– (2+3)*4 – 5*4
– 20
• Operator precedence:
– In the absence of parentheses (within which expressions
are first reduced), operators are executed le[ to right, first
using **, then * and /, and then + and -
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
16. Comparison operators on ints and floats
• i > j – returns True if strictly greater than
• i >= j – returns True if greater than or equal
• i < j
• i <= j
• i == j – returns True if equal
• i != j – returns True if not equal
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
17. Operators on bools
• a and b is True if both are True
• a or b is True if at least one isTrue
• not a is True if a is False; it is False if a is True
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
18. Type conversions (type casting)
• We can o[en convert an object of one type to another, by
using the name of the type as a function
– float(3) has the value of 3.0
– int(3.9) truncates to 3
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
19.
20. Non-scalar objects
• We will see many different kinds of compound objects
• The simplest of these are strings, objects of type str
• Literals of type string can be written using single or double
quotes
– ‘abc’
– “abc”
– ‘123’ – this is a string of characters, not the number
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
21. Operators on strings
>>> 3 * ‘a’
‘aaa’
>>> ‘a’ + ‘a’
‘aa’
>>> ‘a’ + str(3)
‘a3’
>>> len(‘abc’)
3
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
22. Extracting parts of strings
• Indexing:
– ‘abc’[0] returns the string ‘a’
– ‘abc’[2] returns the string ‘c’
– ‘abc’[3] is an error (as we cannot go beyond the
boundaries of the string)
– ‘abc’[-1] returns the string ‘c’ (essentially counting
backwards from the start of the string)
• Slicing:
– If s is a string, the expression s[start:end] denotes the
substring that starts at start, and ends at end-1
• ‘abc’[1:3] has the value ‘bc’
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
23.
24. Providing input
• If we are going to write programs or scripts, we will need
a way to incorporate input from a user.
• We use the Python function raw_input, as in:
>>> name = raw_input(‘Enter your name: ‘)
Enter your name: Eric Grimson
>>> print(‘Are you ‘ + name + ‘?’)
Are you Eric Grimson?
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
25.
26. Branching programs
• The simplest branching
statement is a conditional
– A test (expression that evaluates to True or False)
– A block of code to execute if the test is True
– An optional block of code to execute if the test is False
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
27. A simple example
x = int(raw_input('Enter an integer: '))
if x%2 == 0:
print(‘’)
print('Even')
else:
print(‘’)
print('Odd')
print(’Done with conditional')
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
28. Some observations
• The expression x%2 == 0 evaluates to True
when the remainder of x divided by 2 is 0!
• Note that == is used for comparison, since = is
reserved for assignment
• The indentation is important – each indented set of
expressions denotes a block of instructions
– For example, if the last statement were indented, it would
be executed as part of the else block of code
• Note how this indentation provides a visual structure that
reflects the semantic structure of the program
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
29. We can have nested conditionals
if x%2 == 0:
if x%3 == 0:
print('Divisible by 2 and 3’)
else:
print('Divisible by 2 and not by 3’)
elif x%3 == 0:
print('Divisible by 3 and not by 2’)
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
30. And we can use compound Booleans
if x < y and x < z:
print('x is least’)
elif y < z:
print('y is least’)
else:
print('z is least’)
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_1/Lecture_2/
31.
32. NASA Open Source Software written in Python
The SunPy project is an effort to create an open-source software library for solar physics using
the Python programming language. More information at http://www.sunpy.org.6
[6] http://code.nasa.gov/language/python/
33. Capturing computation as a function
• Idea is to encapsulate this computation within a scope
such that can treat as primi%ve
– Use by simply calling name, and providing input
– Internal details hidden from users
• Syntax
def <function name> (<formal parameters>):
<function body>
• def is a keyword
• Name is any legal Python name
• Within parenthesis are zero or more formal parameters –
each is a variable name to be used inside function body
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_2/Lecture_4/
34. A simple example
def max(x, y):
if x > y:
return x
else:
return y
• We can then invoke by
z = max(3, 4)
• When we call or invoke max(3, 4), x is bound to 3, y is
bound to 4, and then body expression(s) are evaluated
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_2/Lecture_4/
35. Function returns
• Body can consist of any number of legal Python
expressions
• Expressions are evaluated unit
– Run out of expressions, in which case special value
None is returned
– Or until special keyword return is reached, in which
case subsequent expression is evaluated and that value is
returned as value of function call
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_2/Lecture_4/
36. Summary of function call
1.
1. Expressions for each parameter are evaluated, bound to
formal parameter names of function
2. Control transfers to first expression in body of function
3. Body expressions executed until return keyword
reached (returning value of next expression) or run out of
expressions (returning None)
4. Invocation is bound to the returned value
5. Control transfers to next piece of code
2.
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_2/Lecture_4/
40. Tuples
• Ordered sequence of
t1 = (1, ‘two’, 3)
print(t1) #(1, 'two', 3)
elements (similar to strings)
• Elements can be more than just characters
t2 = (t1, ‘four’)
print(t2)# ((1, 'two', 3), 'four')
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
41. Operations on tuples
t1 = (1, ‘two’, 3)
t2 = (t1, ‘four’)
• Concatenation print(t1+t2)
• Indexing print((t1+t2)[3])
• Slicing print((t1+t2)[2:5])
• Singletons t3 = (‘five’,)
print(t1+t2+t3)
(1, 'two', 3, (1, 'two', 3), 'four')
(1, 'two', 3)
(3, (1, 'two', 3), 'four')
(1, 'two', 3, (1, 'two', 3), 'four', 'five')
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
42. Tuples (Example)
num = (2, 3, 5, 7)
total = 0
for i in num:
total += i
print(total) #17
num = (2, 3, 5, 7)
emp = ()
for i in num:
emp += (i, ) # why not emp += i
print(emp) #(2, 3, 5, 7)
43. Lists
• Look a lot like tuples
– Ordered sequence of values, each identified by an index
– Use [1, 2, 3] rather than (1, 2, 3)
– Singletons are now just [4] rather than (4, )
• BIG DIFFERENCE
– Lists are mutable
– While tuple, int, float, str are immutable
– So lists can be modified aMer they are created
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
44. Why should this matter?
• Some data objects we want to treat as fixed – Can create
new versions of them
– Can bind variable names to them
– But don’t want to change them
– Generally valuable when these data objects will be
referenced frequently but elements don’t change
• Some data objects may want to support modifications to
elements, either for efficiency or because elements are
prone to change
• Mutable structures are more prone to bugs in use, but
provide great flexibility
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
45. Visualizing lists
Techs = [‘MIT’,‘Cal Tech’]
Ivys = [‘Harvard’,‘Yale’, ‘Brown’]
>>>Ivys[1]
‘Yale’
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
46. Structures of lists
• Consider
Univs = [Techs, Ivys]!
Univs1 = [[‘MIT’, ‘Cal Tech’], [‘Harvard’, ‘Yale’, ‘Brown’]]
• Are these the same thing?
– They print the same thing
– But let’s try adding something to one of these
[['MIT', 'Cal Tech'], ['Harvard', 'Yale', 'Brown']]
[['MIT', 'Cal Tech'], ['Harvard', 'Yale', 'Brown']]
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
47. Mutability of lists
Let’s evaluate
Techs.append(‘RPI’)
• Append is a method (hence the .) that has
a side effect
– It doesn’t create a new list, it mutates the existing
one to add a new element to the end
• So if we print Univs and Univs1 we get
different things
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
48. print(Univs)
[[‘MIT’,‘Cal Tech’,‘RPI’],[‘Harvard’, ‘Yale’, ‘Brown’]]
Print(Univs1)
[[‘MIT’, ‘Cal Tech’],[‘Harvard’, ‘Yale’, ‘Brown’]]
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
49. Why?
• Bindings before append • Bindings aMer append
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
50. Observation
Elements of Univs are not copies of the lists to which Techs and Ivys are
bound, but are the lists themselves
• This effect is called aliasing:
– There are two distinct paths to a data object
• One through the variable Techs
• A second through the first element of list object to which Univs is bound
– Can mutate object through either path, but effect will be visible through both
– Convenient but treacherous
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
51. We can directly change elements
>>> Techs
['MIT', 'Cal Tech', 'RPI']
>>> Techs[2] = 'WPI’!
>>> Techs
['MIT', 'Cal Tech', 'WPI']
Cannot do this with tuples!
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
52. Dictionaries
• Dict is generalization of lists, but now indices don’t have to
be integers – can be values of any immutable type
• Refer to indices as keys, since arbitrary in form
• A dict is then a collec$on of <key, value> pairs
• Syntax
– monthNumbers = { ‘Jan’:1, ‘Feb’:2, ‘Mar’:3, 1:’Jan’,
2:’Feb’, 3:’Mar’}
53. We access by using a key
monthNumbers =
{‘Jan’:1, ‘Feb’:2,
‘Mar’:3, 1:’Jan’,
2:’Feb’, 3:’Mar’}
monthNumbers[‘Jan’]
returns 1
monthNumbers[1]
returns ‘Jan’
Entries in a dict are unordered, and can only be accessed by a key, not an index
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
54. Operations on dicts
• Insersion
monthNumbers[‘Apr’] = 4
#{1: 'Jan', 2: 'Feb', 'Mar': 3, 'Feb': 2, 'Apr': 4, 'Jan':
1, 3: 'Mar'}
• Iteration
collect = []
for e in monthNumbers:
collect.append(e)
collect is now
[1, 2, 'Mar', 'Feb', 'Apr', 'Jan', 3]
Compare to
monthNumbers.keys() #[1, 2, 'Mar', 'Feb', 'Apr', 'Jan', 3]
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
55. Keys can be complex
myDict = {(1,2): 'twelve', (1,3): 'thirteen'}
myDict[(1,2)]
returns ‘twelve’
Note that keys must be immutable, so have to use a tuple, not
a list
Slides are taken from Professor Eric Grimson
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_3/Lecture_6/
56. SymPy
SymPy is a Python library for symbolic mathematics. It
aims to become a full-featured computer algebra system
(CAS) while keeping the code as simple as possible in
order to be comprehensible and easily extensible. SymPy
is written entirely in Python and does not require any
external libraries.
solve([x + 5*y - 2, -3*x + 6*y - 15], [x, y])
{x: -3, y: 1}
http://sympy.org/en/index.html
58. Exceptions
● What to do when procedure execution is stymied by an
error condition?
– Fail silently: substitute default values, continue execution • Bad idea! User gets
no indication results may be suspect
– Return an “error” value
• What value to chose? None?
• Callers must include code to check for this special value and deal with
consequences ⇒ cascade of error values up the call tree
– Stop execution, signal error condition
• In Python: raise an exception
raise Exception(“descriptive string”)
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_6/Lecture_10/
59. Dealing with Exceptions
• Python code can provide handlers for exceptions
try:
f = open(‘grades.txt’)
# ...code to read and process grades
except:
raise Exception(“Can’t open grades file”)
• Exceptions raised by statements in body of try are handled by the
except statement and execution continues with the body of the except
statement.
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_6/Lecture_10/
60. Handling Specific Exceptions
• Usually the handler is only meant to deal with a particular
type of exception. And sometimes we need to clean-up
before continuing.
try:
f = open(‘grades.txt’)
# ...code to read and process grades
except IOError,e:
print “Can’t open grades file: ” + str(e)
sys.exit(0)
except ArithmeticError,e:
raise ValueError(“Oops, bug in grade calculation! "
+ str(e))
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_6/Lecture_10/
61. Types of Exceptions
● We’ve seen the common errors:
SyntaxError: Python can’t parse program
NameError: local or global name not found AttributeError: attribute
reference fails
TypeError: operand doesn’t have correct type
ValueError: operand type okay, but value is illegal IOError: IO system
reports malfunction (eg, file not found)
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_6/Lecture_10/
62. Other extensions to try
• else:
– Body of this clause is executed when execution of the associated try body
completes with no exceptions
• finally:
– Body of this clause is always executed after try, else and except
clauses, even they’ve raised another error or executed a break, continue
or return.
– Useful for clean-up code that should be run (e.g., closing files) no matter
what else happened.
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_6/Lecture_10/
63. Code Example
def thisRaisesAZeroDivisionError():
x = 1/0
def thisDoesNotRaiseAnyErrors():
z = 'just a string'
def thisRaisesAValueError():
y = int('Five')
def tryExercise():
print 'A',
try:
# Line Of Code Is Inserted Here # # thisDoesNotRaiseAnyErrors()
# thisRaisesAZeroDivisionError() # thisRaisesAValueError() # return
print 'B',
except ZeroDivisionError as e:
print 'C',
else:
print 'D',
finally:
print 'E',
print 'F'
# A B D E F # A C E F # A E # A E
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_6/Lecture_10/
64. Cython
Cython is a language that makes writing C extensions for the Python language as easy as
Python itself. It is based on the well-known Pyrex, but supports more cutting edge functionality
and optimizations.7
[7] http://www.cython.org/
65. Objects!
Python supports many different kinds of data:
1234 int 3.14159 float “Hi there!” str
[1, 2, 3, 5, 7, 11] list
{“MA”: “Massachusetts”, “ME”: “Maine”} dict
Each of the above is an object.
Objects have
• a type (a particular object is said to be an instance of a type) • an
internal data representation
• a set of procedures for interacting with the object
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx/6.00x/2012_Fall/courseware/Week_6/Lecture_10/
66. Defining New Types
In Python, the class statement is used to define a new type
class Coordinate(object):
... define attributes here...
Like with def, indentation is used to indicate which statements are part
of the definition.
Classes can inherit attributes from other classes, in this case
Coordinate inherits from the object class. Coordinate is said to be a
subclass of object, object is a superclass of Coordinate. One can
override an inherited attribute with a new definition in the class
statement.
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx
67. Creating an Instance
Usually when creating an instance of a type, we’ll want to provide some initial
values for the internal data. To do this, define an __init__ method:
class Coordinate(object):
def __init__(self,x,y):
self.x = x
self.y = y
When calling a method of an object, Python always passes the object as the
first argument. By convention Python programmers use self as the name for the
first argument of methods.
The “.” operator is used to access an attribute of an object. So the __init__
method above is defining two attributes for the new Coordinate object: x and y.
Data attributes of an instance are often called instance variables.
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx
68. Creating an Instance
class Coordinate(object):
def __init__(self,x,y):
self.x = x
self.y = y
c = Coordinate(3,4)
origin = Coordinate(0,0)
print c.x, origin.x
Slides are taken from Professor CHRIS TERMAN
https://www.edx.org/courses/MITx
69. Other Libraries
matplotlib is a python 2D plotting library which produces publication quality
figures in a variety of hardcopy formats and interactive environments across
platforms. matplotlib can be used in python scripts, the python and ipython shell
(ala MATLAB®* or Mathematica®†), web application servers, and six graphical
user interface toolkits.
http://matplotlib.org/
NumPy is the fundamental package for scientific computing with Python.
http://www.numpy.org/
SciPy (pronounced "Sigh Pie") is open-source software for mathematics,
science, and engineering
http://www.scipy.org/
http://www.scipy.org/PyLab
72. Special Thanks
1. Professor Eric Grimson
2. Professor Chris Terman
3. Tanjina Islam
4. Engineer Yousuf Ibrahim
5. https://www.facebook.com/groups/274240126027143/
6. https://www.edx.org/