Before you turn this problem in, make sure everything runs as expected. First, restart the kernel (in the menubar, select Kernel$\rightarrow$Restart) and then run all cells (in the menubar, select Cell$\rightarrow$Run All).
Make sure you fill in any place that says YOUR CODE HERE
or "YOUR ANSWER HERE", as well as your name and collaborators below:
NAME = ""
COLLABORATORS = ""
Python is a great general-purpose programming language on its own, but with the help of a few popular libraries (numpy, scipy, matplotlib) it becomes a powerful environment for scientific computing.
free()
the memory yourself after you malloc()
the memory. Python manages memory using reference counting. When number of references to a memory location go to zero, the memory is freed.!python3 --version
!mkdir test
You cannot use ! to navigate the file systems. In stead, you need to use % to change the directory.
%cd test
!pwd
Exercise 1: write a command to list all files in the current directory sorted by time (2 marks).
# YOUR CODE HERE
raise NotImplementedError()
Exercise 2: What is the difference between "!cd directory" and "%cd directory"? (2 marks)
YOUR ANSWER HERE
Print the hello world! Unlike C, Python does not "need" a main function to run. All the lines are executed one by one. Semicolons are optional.
print("hello world from Python!")
You can run this single line from a python file.
%%writefile helloworld.py
print("hello world from Python file!")
You can run any file using the python executable.
!python3 helloworld.py
Jupyter cells automatically print the last expression to stdout. Specifically, the last statement must be an expression statement.
For example, you do not need the print()
function to print "hello world!" from Jupyter cell.
"Hello world from Jupyter cell!"
You can suppress the output by making the last statement, not an "expression statement". You can do it by assining this to another variable, for example.
x = "Hello world from Jupyter cell!" # this will not be printed by Juputer cell
Just like Matlab, you can suppress the output of a variable by using semicolon.
x = "Hello world from Jupyter cell!"
x; # This will NOT be printed
The principal built-in types are
sys.float_info
. )Integers and floats work as you would expect from other languages:
x = 3
(x, type(x))
print(x + 1) # Addition
print(x - 1) # Subtraction
print(x * 2) # Multiplication
print(x ** 2) # Exponentiation (not available in C)
x += 1 # x = x + 1
print(x)
x *= 2 # x = x * 2
print(x)
x = 0.2 + 0.1
abs(x - 0.3) < 1e-15
YOUR ANSWER HERE
Note that unlike many languages, Python does not have unary increment (x++) or decrement (x--) operators.
Python also has built-in types for complex numbers; you can find all of the details in the Builtin types documentation.
Python implements all of the usual operators for Boolean logic, but uses English words rather than symbols (&&
, ||
, etc.):
t = True; f = False
print(type(t))
print(t and f) # Logical AND;
print(t or f) # Logical OR;
print(not t) # Logical NOT;
print(t != f) # Logical XOR;
None
constant can be thought of has nullptr
in C. It has its own type NoneType
.
n = None
type(None), type(n)
hello = 'he"llo' # String literals can use single quotes
world = "world'" # or double quotes; it does not matter
great = """Python
also has triple quotes
which can internally contain
newlines, 'single quotes' and "double quotes".
Triple quotes are often used for documentation
and multi-line comments.
"""
print(hello)
print(len(hello))
hw = hello + ', ' + world # String concatenation
print(hw)
num = 12
hw12 = f'{hello:s} {world} {num:d}' # string formatting
hw12
"First, thou shalt count to {0}" # References first positional argument
"Bring me a {}" # Implicitly references the first positional argument
"From {} to {}" # Same as "From {0} to {1}"
"My quest is {name}" # References keyword argument 'name'
"Weight in tons {0.weight}" # 'weight' attribute of first positional arg
"Units destroyed: {players[0]}" # First element of keyword argument 'players'.
String formatting has its own mini language.
format_spec ::= [[fill
]align
][sign
][#][0][width
][grouping_option
][.precision
][type
] fill ::= <any character> align ::= "<" | ">" | "=" | "^" sign ::= "+" | "-" | " " width ::=digit
+ grouping_option ::= "_" | "," precision ::=digit
+ type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
hw12 = '%s %s %d' % (hello, world, 12) # C-style string formatting using % operator
hw12
Since Python 3.8, the dominant way to format strings is to use f-string
hello = "Hell'o"
world = 'World"'
i = 12
hw12 = F'{hello:s} {world} {i:d}'# string formatting using f-strings
hw12
String objects have a bunch of useful methods; for example:
s = "hello"
print(s.capitalize()) # Capitalize a string
print(s.upper()) # Convert a string to uppercase; prints "HELLO"
print(s.rjust(7)) # Right-justify a string, padding with spaces
print(s.center(7)) # Center a string, padding with spaces
print(s.replace('l', '(ell)')) # Replace all instances of one substring with another
You can ask for help on any python function, object or type using "?"
# a jupyter notebook feature, not python
s.upper?
help(s.upper) # Some as s.upper? but a python feature
dir(s) # List all the functions available on a string object
C is statically typed vs Python is dynamically typed. You can change the type of a variable from one line to another.
x = 1
print("1. Type of x = ", type(x))
x = "str"
print("2. Type of x = ", type(x))
A list is the Python equivalent of an array, but is resizeable and can contain elements of different types:
lst = [1, 2, 3, 4, 5]
print(lst)
Index Positions Start at 0, Not 1
lst[0]
xs = [1, 2, 3, 'hello', [4, 5, 6]] # Create a list
print(xs)
print('First element of the array: ', xs[0]) # Index to the list starts with 0
print('Last element of the array: ', xs[4])
print(xs[4][1])
# Negative indices count from the end of the list;
print(xs[-1]) # Index -1 returns the last item in the list
print(xs[-2]) # Index -2 returns the second item from the end of the list
xs[2] = 'foo' # Lists can contain elements of different types
print(xs)
xs.append('bar') # Add a new element to the end of the list
print(xs)
print(xs)
x = xs.pop() # Remove and return the last element of the list
print(x)
print(xs)
xs.insert(1, "new item") # Insert the "new item" at the index 1
print(xs)
xs.remove("foo") # Removing an item by value
print(xs)
As usual, you can find all the details about lists in the documentation.
Exercise 4: Read the the documentation to find out whether Python lists are implemented as array datastructure or linked list datastructure.
YOUR ANSWER HERE
colors = ["Red", "Green", "White", "Black"]
# Print the first color in the above list
print(colors[0])
# Print the last color in the above list
print(colors[-1])
# Append "Blue" to the above list
colors.append('Blue')
print(colors)
# Remove "Green" from the above list
colors.remove('Green')
print(colors)
Exercise 5: Find the length of the list colors
.
In-class Excercises 6: What is the difference between sorted(colors) and colors.sort()?
colors = ["Red", "Green", "White", "Black"]
colors2 = sorted(colors)
colors2, colors
colors.sort()
colors
YOUR ANSWER HERE
In addition to accessing list elements one at a time, Python provides concise syntax to access sublists; this is known as slicing:
The default for start is none or 0.
The default stop is the end of your data structure.
Using a positive number references from the first element, a negative number references from last element in your structure.
# nums = range(5) # Python 2, but error in Python 3
nums = list(range(5)) # range is a built-in function that creates a list of integers
nums = [0, 1, 2, 3, 4]
print(nums) # Prints "[0, 1, 2, 3, 4]"
print(nums[2:4]) # Get a slice from index 2 to 4 (exclusive); prints "[2, 3]"
print(nums[2:]) # Get a slice from index 2 to the end; prints "[2, 3, 4]"
print(nums[:2]) # Get a slice from the start to index 2 (exclusive); prints "[0, 1]"
print(nums[:]) # Get a slice of the whole list; prints ["0, 1, 2, 3, 4]"
print(nums[::-1]) # Slice indices can be negative; prints ["0, 1, 2, 3]"
nums[2:4] = [8, 9] # Assign a new sublist to a slice
print(nums) # Prints "[0, 1, 8, 9, 4]"
lst = list(range(0,100,10))
print(lst)
# Print everything except the last three elements
print(lst[:-3])
# Print everything with odd indices
print(lst[::2])
# Print everything with even indices
print(lst[1::2])
# Print everything in reversed order
print(lst[::-1])
In-class Excercises 7: Select 6th,5th, and 4th element from the following list A
.
A = list(range(1,10,1)) # start,stop,step
print(A)
# YOUR CODE HERE
raise NotImplementedError()
# Last cell should output length of the list `colors`
You can loop over the elements of a list like this:
animals = ['cat', 'dog', 'monkey']
for animal in animals:
print(animal)
You can print squares of first 20 numbers like this:
for i in range(1, 21):
print(f"{i:d}^2 = {i*i:d}")
Unlike C there are no braces for start and end of the for loop. Instead of braces, we have a compbination of ":" colon and indentation. Unlike C, indentation is mandatory in Python.
If you want access to the index of each element within the body of a loop, use the built-in enumerate
function:
Write code to create a list of 20 elements of fibonacci series: $f_0 = 0$, $f_1 = 1$, and $f_n = f_{n-1} + f_{n-2}$ for all $n \ge 2$. Assign the list to variable called FIB20.
When programming, frequently we want to transform one type of data into another. As a simple example, consider the following code that computes square numbers:
nums = [0, 1, 2, 3, 4]
squares = []
for x in nums: # Don't forget the colon
squares.append(x ** 2)
print(squares)
You can make this code simpler using a list comprehension:
nums = [0, 1, 2, 3, 4]
squares = [x ** 2 for x in nums]
print(squares)
List comprehensions can also contain conditions:
nums = [0, 1, 2, 3, 4]
even_squares = [x ** 2 for x in nums if (x % 2 == 0 and x <=2)]
print(even_squares)
def sq(x): return x**2
list(map(sq, nums))
def even(x): return (x % 2 == 0 and x <=2)
list(map(sq, filter(even, nums)))
[0, 4]
[0, 4]
A dictionary stores (key, value) pairs, similar to a Map
in Java or an object in Javascript. It is implemented as a hash table.
You can use it like this:
d = {'cat': 'cute',
'dog': 'furry'} # Create a new dictionary with some data
print(d['cat']) # Get an entry from a dictionary; prints "cute"
key = 'cat'
print(d[key])
print('cat' in d) # Check if a dictionary has a given key; prints "True"
cute cute True
Adding a new key-value pair
d['fish'] = 'wet' # Set an entry in a dictionary
print(d['fish']) # Prints "wet"
d['elephant'] = 'heavy' # Set an entry in a dictionary
print(d['elephant']) # Prints "heavy"
Looping through a dictionary
for key, value in d.items():
print('key = ', key, '\t value = ', value)
for key in d.keys():
print('key = ', key, '\tvalue = ', d[key])
for key in sorted(d.keys()): # sorting the key
print('key = ', key, '\t\tvalue = ', d[key])
for value in sorted(d.values()): # print all values
print('value = ', value)
print('monkey' in d)
# print(d['monkey']) # KeyError: 'monkey' not a key of d
For dictionaries, we can use the get() method to set a default value that will be returned if the requested key doesn’t exist.
print(d.get('monkey', 'N/A')) # Get an element with a default; prints "N/A"
print(d.get('fish', 'N/A')) # Get an element with a default; prints "wet"
Removing a key-value pair
del d['fish'] # Remove an element from a dictionary
# Be aware that the deleted key-value pair is removed permanently.
print(d.get('fish', 'N/A')) # "fish" is no longer a key; prints "N/A"
You can find all you need to know about dictionaries in the documentation.
It is easy to iterate over the keys in a dictionary:
d = {'person': 2, 'cat': 4, 'spider': 8}
for animal, legs in d.items():
print('A {} has {} legs'.format(animal, legs))
Dictionary comprehensions: These are similar to list comprehensions, but allow you to easily construct dictionaries. For example:
nums = [0, 1, 2, 3, 4]
even_num_to_square = {x: x ** 2 for x in nums if x % 2 == 0}
print(even_num_to_square)
def sq(x): return (x, x**2)
def even(x): return x%2 == 0
dict(map(sq, filter(even, nums)))
{0: 0, 2: 4, 4: 16}
A tuple is an (immutable) ordered list of values. Python refers to values that cannot change as immutable, and an immutable list is called a tuple.
dimensions = 800, 600
print(dimensions[0])
print(dimensions[1])
squares = (x**2 for x in range(10))
squares
800 600
<generator object <genexpr> at 0x7ffabc4bf200>
Note that comma creates a tuple not parantheses. Always use parantheses to increase readability.
dimensions = 800, 600
print(dimensions[0])
print(dimensions[1])
# Try this (uncomment)
# dimensions[0] = 1000 # this will return error
A tuple is in many ways similar to a list; one of the most important differences is that tuples can be used as keys in dictionaries and as elements of sets, while lists cannot. Here is a trivial example:
d = {(x, x + 1): x for x in range(10)} # Create a dictionary with tuple keys
t = (5, 6) # Create a tuple
print(type(t))
print(d[t])
print(d[(1, 2)])
Any sequence (list, tuple, set) can be unpacked into individual variables.
x, *rest = 1, 3, 4
print(x, rest)
1 [3, 4]
x, y, z = [1, 2, 3] # Lists can be used for Multiple assignment
print(x, y, z)
x, y, *rest = range(10) # Lists can be used for Multiple assignment
x, y, rest
x, y, z = 1, 2, 3 # Tuples can be used for Multiple assignment
print(x, y, z)
fruits = ["apple", "banana", "cherry"]
for x in fruits:
print(x)
for x in "banana": # string is also a sequence type
print(x)
fruits = ["apple", "banana", "cherry"]
for x in fruits:
if x == "banana":
break
print(x)
fruits = ["apple", "banana", "cherry"]
for x in fruits:
if x == "banana":
continue
print(x)
# Shows while loop. Do not use this style though, prefer for loop.
# This style is considered not Pythonic
# https://peps.python.org/pep-0008/
idx = 0
while (idx < len(fruits)):
print(fruits[idx])
idx += 1
age = 19
if age >= 18:
print("You are old enough to vote!")
elif (age <= 27):
pass
else:
print("Sorry, you are too young to vote.")
age = 12
if age < 4:
price = 0
elif age < 18:
price = 25
else:
price = 40
print(f"Your admission cost is ${price}.")
There is no do {} while()
in Python.
Any objects can be tested for truth value in if
and while
statements. Most objects are considered true, except the following ones which are considered false:
constants defined to be false: None and False.
zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
empty sequences and collections: '', (), [], {}, set(), range(0)
Python functions are defined using the def
keyword. For example:
# Making the argument b and c optional with 4 and 8 as default values
def sum2(a, b=4, c=8):
return a + b + c, a*b*c # Returns a tuple of two values
sum2(1, c=5)
(10, 20)
sum_, prod_ = sum2(1, c=5) # Unpacking a tuple
prod_
20
def sign(x, y=100):
if x > 0:
return 'positive', 123
elif x < 0:
return 'negative', 123
else:
return 'zero', 123
for x in [-1, 0, 1]:
v1, _ = sign(x)
print(v1)
We will often define functions to take optional keyword arguments, like this:
def hello(name, loud=False):
if loud:
print('HELLO, {}'.format(name.upper()))
else:
print('Hello, {}!'.format(name))
hello('Bob')
hello('Fred', loud=True)
Modifing a list in a function
When you pass a list to a function, the function can modify the list.
def fun(list_):
list_.append('new item')
a = [1, 2, 3]
fun(a)
print(a)
If you want to prevent a function from modifying a list, you can send a copy of a list to the function
def fun(lst):
lst.append('new item')
a = [1, 2, 3]
# Preventing a Function from Modifying a List
fun(a[:]) # The slice notation [:] makes a copy of the list
# fun(a.copy())
print(a)
In-class Excercises 8
The LEGB scope lookup rule. When a variable is referenced, Python searches for it in this order: in the local scope, in any enclosing functions’ local scopes, in the global scope, and finally in the built-in scope. The first occurrence wins. The place in your code where a variable is assigned usually determines its scope. In Python 3, nonlocal declarations can also force names to be mapped to enclosing function scopes, whether assigned or not.
# Global Namespaces
# Global scope
X = 99 # X and func assigned in module: global
#print(globals())
def func(Y): # Y and Z assigned in function: locals
# Local scope
X = 3
Z = 0
def funcfunc(Y2):
if True:
nonlocal Z
Z = X + Y2 # X is a global
for i in range(10):
if True:
W = 0
print("funcfunc", locals())
return Z
funcfunc(Y)
print("func", locals())
return Z
func(1) # func in module: result=100
funcfunc {'Y2': 1, 'i': 9, 'W': 0, 'X': 3, 'Z': 4} func {'Y': 1, 'funcfunc': <function func.<locals>.funcfunc at 0x7ffabc4229d0>, 'X': 3, 'Z': 4}
4
X = 99 # Global scope name: not used
def f1():
X = 88 # Enclosing def local
def f2():
print(X) # Reference made in nested def
f2()
f1() # Prints 88: enclosing def local
print(X)
X = 99 # Global X
def func():
X = 88 # Local X: hides global
func()
print(X) # Prints Global X 99: unchanged
The assignment within the function creates a local X that is a completely different variable from the global X in the module outside the function.
X = 99 # Global X
def func():
global X
X = 88 # Local X: overrides global
func()
print(X) # Prints 88: unchanged
def func():
for x in range(100):
pass
# what is x
x = 1
def func(x, y=[1, 2, 3]):
y.append(4)
print(y)
func(1)
func(1)
func(1)
[1, 2, 3, 4] [1, 2, 3, 4, 4] [1, 2, 3, 4, 4, 4]
x = 0
for i in range(100):
if i % 23 == 0:
x = 23
else:
x = 1
# what is x?
x
x = 0
def foo():
for i in range(100):
if i % 23 == 0:
x = 23
else:
x = 1
# what is x?
x
As a way to play with functions and loops, let’s implement a square root function: given a number x, we want to find the number z for which z² is most nearly x.
Computers typically compute the square root of x using a loop. Starting with some guess z, we can adjust z based on how close z² is to x, producing a better guess: $z' = z - (z^2 - x) / (2z) $
Repeating this adjustment makes the guess better and better until we reach an answer that is as close to the actual square root as can be.
Implement this in the def sqrt provided. A decent starting guess for z is 1, no matter what the input. To begin with, repeat the calculation 10 times and print each z along the way. See how close you get to the answer for various values of x (1, 2, 3, …) and how quickly the guess improves.
Next, change the loop condition to stop once the value has stopped changing (or only changes by a very small amount). See if that’s more or fewer than 10 iterations. Try other initial guesses for z, like x, or x/2. How close are your function’s results to the math.sqrt in the standard library?
(Note: If you are interested in the details of the algorithm, the z² − x above is how far away z² is from where it needs to be (x), and the division by 2z is the derivative of z², to scale how much we adjust z by how quickly z² is changing. This general approach is called Newton’s method. It works well for many functions but especially well for square root. Newton’s methods is particular form of gradient descent.)
x = 2; z = sqrt(x)
assert abs(z*z - x) < 1e-8
x = 3; z = sqrt(x)
assert abs(z*z - x) < 1e-8
x = 4; z = sqrt(x)
assert abs(z*z - x) < 1e-8
import random
for _ in range(10):
x = random.randint(1, 100); z = sqrt(x)
assert abs(z*z - x) < 1e-6
'success'
3 * 5
3 ** 5
# Using * to unpack iterables into a list/tuple
numbers = [2, 1, 3, 4, 7]
more_numbers = [*numbers, 11, 18]
print(*more_numbers, sep=', ')
first, *rest = [*numbers, 11, 18]
print(rest, sep=', ')
# Unpack a dictionary
date_info = {'ye ar': "2020", 'month': "01", 'day': "01"}
filename = "{ye ar}-{month}-{day}.txt".format(**date_info)
print(filename)
def func(**kwargs):
return "{ye ar}-{month}-{day}.txt".format(**kwargs)
func(**date_info)
2020-01-01.txt
'2020-01-01.txt'
The zip() function takes iterables (can be zero or more), aggregates them in a tuple, and returns it.
languages = ['Java', 'Python', 'JavaScript']
versions = [14, 3, 6]
result = list(zip(languages, versions))
print(result)
zip
is almost the inverse of itself. It result in the same lists that you started with.
list(zip(*result))
mat = [[1, 2, 3],
[4, 5, 6]]
mat_transpose = list(zip(*mat))
print(mat_transpose)
print(list(zip(*mat_transpose)))
for (x, y) in zip(languages, versions): # pairs of items pulled from two lists
print(x, y)
keys = ['spam', 'eggs', 'toast']
vals = [1, 3, 5]
D2 = {}
for (k, v) in zip(keys, vals):
D2[k] = v
D2
zip function is more general than this example suggests. For instance, it accepts any type of sequence (really, any iterable object, including files), and it accepts more than two arguments. With three arguments, as in the following example, it builds a list of three-item tuples with items from each sequence, essentially projecting by columns (technically, we get an N-ary tuple for N arguments):
T1, T2, T3 = (1,2,3), (4,5,6), (7,8,9)
list(zip(T1, T2, T3))
The * operator can be used in conjunction with zip() to unzip the list.
coordinate = ['x', 'y', 'z']
value = [3, 4, 5]
result = zip(coordinate, value)
result_list = list(result)
print(result_list)
c, v = zip(*result_list)
print('c =', c)
print('v =', v)
The syntax for defining classes in Python is :
class Date:
default_year = 1000
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def days(self):
return (self.year -2000)* 360 + self.month * 30 + self.day
today = Date(2023, 1, 26)
print(today.days())
class Addition:
# first = 0
# second = 0
answer = 0 # Class member
# parameterized constructor
def __init__(self, first, second):
self.first = first
self.second = second
def display(self):
print("First number = " + str(self.first))
print("Second number = " + str(self.second))
print("Addition of two numbers = " + str(self.answer))
def calculate(self):
self.answer = self.first + self.second
# creating object of the class
# this will invoke parameterized constructor
obj = Addition(1000, 2000)
# perform Addition
obj.calculate()
# display result
obj.display()
8336
class Person:
# Constructor
def __init__(self, name):
self.name = name # Create an instance variable
# Instance method
def getName(self):
return self.name
# To check if this person is an employee
def isEmployee(self):
return False
# Making an instance from a class
g = Person('Fred') # Construct an instance of the Greeter class
# Accessing attributes
print(g.name)
# Calling methods
print(g.getName())
In a class, the implicit behavior of passing the object as the first argument is avoided if a method is declared as static, as shown in the following example.
class A(object):
@staticmethod
def stat_meth():
print("Look no self was passed")
a = A()
a.stat_meth()
Inheritance is the capability of one class to derive or inherit the properties from another class. The benefits of inheritance are:
# Inherited or Subclass (Note Person in bracket)
class Employee(Person):
# Constructor
def __init__(self, name, year):
self.years = year # Create an instance variable
super().__init__(name)
# Overriding Methods from the Parent Class
def isEmployee(self):
return True
# Driver code
emp = Person("Geek1") # An Object of Person
print(emp.getName(), emp.isEmployee())
emp = Employee("Geek2", 20) # An Object of Employee
print(emp.getName(), emp.isEmployee())
mytuple = ("apple", "banana", "cherry")
myit = iter(mytuple) # define
print(next(myit)) # apple
print(next(myit)) # banana
print(next(myit)) # cherry
# Try this (uncomment)
# print(next(myit)) # raises StopIteration exception (not all exceptions are errors)
mystr = "banana"
myit = iter(mystr)
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
C-style approach
mytuple = ("apple", "banana", "cherry")
i = 0
while (i < len(mytuple)):
print(mytuple[i])
i += 1
A better approach
mytuple = ("apple", "banana", "cherry")
print(mytuple)
for idx, val in enumerate(mytuple):
print(idx, val)
mytuple = ("apple", "banana", "cherry")
print(mytuple)
for val in mytuple:
print(val)
mystr = "banana"
for x in mystr:
print(x)
# Iterating over dictionary
d = dict()
d['xyz'] = 123
d['abc'] = 345
for i in d :
# print("%s %d" %(i, d[i]))
print("{}\t{}".format(i, d[i]))
To create an object/class as an iterator, you have to implement the methods __iter__() and __next__() to your object.
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myinstance = MyNumbers()
myiter = iter(myinstance)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
Stop after 10 iterations by raising the StopIteration exception
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 10:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
Combine mulitple iterator
# Two separate lists
thing = ["Apple", "Audi", "Pasta", "dog", "UMAINE"]
category = ["fruit", "car", "food", "animal"]
# Combining lists and printing
for t, c in zip(thing, category):
print("{} is {}".format(t, c))
Use "*" operator to unzip
l1,l2 = zip(*[('Apple', 'fruit'),
('Audi', 'car'),
('Pasta', 'food')
])
# Printing unzipped lists
print(l1)
print(l2)
Unlike normal functions that return a value and exit, generator functions automatically suspend and resume their execution and state around the point of value generation. Because of that, they are often a useful alternative to both computing an entire series of values up front and manually saving and restoring state in classes. Because the state that generator functions retain when they are suspended includes their entire local scope, their local variables retain information and make it available when the functions are resumed.
The chief code difference between generator and normal functions is that a generator yields a value, rather than returning one—the yield statement suspends the function and sends a value back to the caller, but retains enough state to enable the function to resume from where it left off. When resumed, the function continues execution immediately after the last yield run. From the function’s perspective, this allows its code to produce a series of values over time, rather than computing them all at once and sending them back in something like a list.
def simpleGeneratorFun():
x = 5
# print(x)
yield 1 + x
x = 2*x
#print(x)
yield 1 + x
x = 2*x
#print(x)
yield 1 + x
simpleGeneratorFun()
the next(X) built-in calls an object’s
X.__next__()
method for us:
x = simpleGeneratorFun()
next(x)
list(simpleGeneratorFun())
Yield are used in Python generators. A generator function is defined like a normal function, but whenever it needs to generate a value, it does so with the yield keyword rather than return. If the body of a def contains yield, the function automatically becomes a generator function.
def nextSquare():
i = 1;
# An Infinite loop to generate squares
while True:
yield i*i
i += 1 # Next execution resumes
# from this point
# Driver code to test above generator
# function
for num in nextSquare():
if num > 100:
break
print(num)
# Packages are stored on PyPi servers by default and can be directly installed from github as well.
!pip install --user import-ipynb
import import_ipynb
%%writefile myModule.py
s = "hi from my module"
Every python file is a module and can be imported. import executes everything in the module.
# YOUR CODE HERE
raise NotImplementedError()
import sys
import os
dirpath = os.getcwd()
print("current directory is : " + dirpath)
foldername = os.path.basename(dirpath)
print("Directory name is : " + foldername)
You can create nested modules by directory structure:
!mkdir -p my_module
You can not import an empty directory. You have to drop (maybe empty) __init__.py
in the directory.
%%writefile my_module/__init__.py
""" My module is the best module ever. """
def identity(x):
""" This function always returns what it is given """
return x
Now you can import my_module.
import my_module
help(my_module)
You can move python files into hierarchies and directories.
!cp myModule.py my_module
import my_module.myModule as mmmm
mmmm.s
Traceback (most recent call last): File "/tmp/ipykernel_28792/254229001.py", line 1, in <module> import my_module.myModule as mmmm ModuleNotFoundError: No module named 'my_module'
def of(self):
print(self)
return "f"
class MyClass:
f = of
def g():
return "g"
@staticmethod
def h():
return "h"
@classmethod
def i(cls):
print(cls)
MyClass().h()
MyClass().f()
MyClass().i()
<__main__.MyClass object at 0x7ffabc4378b0> <class '__main__.MyClass'>
False await else import pass None break except in raise True class finally is return and continue for lambda try as def from nonlocal while assert del global not with async elif if or yield