Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

Python module 2

Module II
Building Python Programs: Data types, variables, operators. Control statements – branching
controls, simple if, if - else, if - elif -else; looping, while, for. Functions - defining, calling,
returning values, functions with default arguments, recursive functions, nested functions and
lamda functions. Strings - operation, string functions. Work with dates and times. (10 HRS)

Building Python Programs: # DataType Output: str

x = "Hello World"
Data types
# DataType Output: int
Data types are the classification or categorization of data items. It represents the kind of value
x = 50
that tells what operations can be performed on a particular data. Since everything is an object
# DataType Output: float
in Python programming, data types are actually classes and variables are instances (object)
of these classes. The following are the standard or built-in data types in Python: x = 60.5

# DataType Output: complex


Numeric
x = 3j
Sequence Type
# DataType Output: list

Boolean x = [ "geeks" , "for" , "geeks" ]

Set # DataType Output: tuple

Dictionary x = ( "geeks" , "for" , "geeks" )

# DataType Output: range


Binary Types( memoryview, bytearray, bytes)
x = range ( 10 )
To define the values of various data types and check their data types we use the type()
# DataType Output: dict
function.
x = { "name" : "Suraj" , "age" : 24 }

# DataType Output: set

x = { "geeks" , "for" , "geeks" }

# DataType Output: frozenset

x = frozenset ({ "geeks" , "for" , "geeks" })

# DataType Output: bool

x = True

# DataType Output: bytes

Python module 2 1 Python module 2 2


x = b "Geeks" Creating String
# DataType Output: bytearray
Strings in Python can be created using single quotes or double quotes or even triple quotes.
x = bytearray( 4 ) # Python Program for

# DataType Output: memoryview # Creation of String

# Creating a String
x = memoryview(bytes( 6 ))
# with single Quotes
# DataType Output: NoneType
String1 = 'Welcome to the Geeks World'
x = None
print ( "String with the use of Single Quotes: " )

Numeric Data Type in Python print (String1)

# Creating a String
The numeric data type in Python represents the data that has a numeric value. A numeric
# with double Quotes
value can be an integer, a floating number, or even a complex number. These values are
defined as Python int, Python float, and Python complex classes in Python. String1 = "I'm a Geek"

print ( "\nString with the use of Double Quotes: " )


Integers – This value is represented by int class. It contains positive or negative whole
print (String1)
numbers (without fractions or decimals). In Python, there is no limit to how long an
print ( type (String1))
integer value can be.
# Creating a String
Float – This value is represented by the float class. It is a real number with a floating- # with triple Quotes
point representation. It is specified by a decimal point. Optionally, the character e or E
String1 = '''I'm a Geek and I live in a world of "Geeks"'''
followed by a positive or negative integer may be appended to specify scientific
print ( "\nString with the use of Triple Quotes: " )
notation.
print (String1)

Complex Numbers – Complex number is represented by a complex class. It is specified print ( type (String1))

as (real part) + (imaginary part)j. For example – 2+3j # Creating String with triple

# Quotes allows multiple lines


Sequence Data Type in Python String1 = '''Geeks
The sequence Data Type in Python is the ordered collection of similar or different data types. For
Sequences allow storing of multiple values in an organized and efficient fashion. There are Life'''
several sequence types in Python – print ( "\nCreating a multiline String: " )

Python String print (String1)

Python List Output:

Python Tuple
String with the use of Single Quotes:
String Data Type Welcome to the Geeks World

Strings in Python are arrays of bytes representing Unicode characters. A string is a collection String with the use of Double Quotes:
I'm a Geek
of one or more characters put in a single quote, double-quote, or triple-quote. In python there
<class 'str'>
is no character data type, a character is a string of length one. It is represented by str class.
String with the use of Triple Quotes:

Python module 2 3 Python module 2 4


I'm a Geek and I live in a world of "Geeks"
Creating List
<class 'str'>
Lists in Python can be created by just placing the sequence inside the square brackets[].
Creating a multiline String:
Geeks # Creating a List
For
Life List = []

print ( "Initial blank List: " )

print ( List )
Accessing elements of String
# Creating a List with
In Python, individual characters of a String can be accessed by using the method of # the use of a String
Indexing. Negative Indexing allows negative address references to access characters from
List = [ 'GeeksForGeeks' ]
the back of the String, e.g. -1 refers to the last character, -2 refers to the second last character,
print ( "\nList with the use of String: " )
and so on.
print ( List )
Python3 # Creating a List with
# Python Program to Access # the use of multiple values
# characters of String
List = [ "Geeks" , "For" , "Geeks" ]
String1 = "GeeksForGeeks" print ( "\nList containing multiple values: " )
print ( "Initial String: " ) print ( List [ 0 ])
print (String1) print ( List [ 2 ])
# Printing First character # Creating a Multi-Dimensional List
print ( "\nFirst character of String is: " ) # (By Nesting a list inside a List)
print (String1[ 0 ])
List = [[ 'Geeks' , 'For' ], [ 'Geeks' ]]
# Printing Last character
print ( "\nMulti-Dimensional List: " )
print ( "\nLast character of String is: " )
print ( List )
print (String1[ - 1 ])

Output:
Output:

Initial blank List:


Initial String: []
GeeksForGeeks
List with the use of String:
First character of String is: ['GeeksForGeeks']
G
List containing multiple values:
Last character of String is: Geeks
s Geeks

Multi-Dimensional List:
List Data Type [['Geeks', 'For'], ['Geeks']]

Lists are just like arrays, declared in other languages which is an ordered collection of data. It
is very flexible as the items in a list do not need to be of the same type. Python Access List Items

Python module 2 5 Python module 2 6


In order to access the list items refer to the index number. Use the index operator [ ] to access In Python, tuples are created by placing a sequence of values separated by a ‘comma’ with or
an item in a list. In Python, negative sequence indexes represent positions from the end of the without the use of parentheses for grouping the data sequence. Tuples can contain any
array. Instead of having to compute the offset as in List[len(List)-3], it is enough to just write number of elements and of any datatype (like strings, integers, lists, etc.). Note: Tuples can
List[-3]. Negative indexing means beginning from the end, -1 refers to the last item, -2 refers also be created with a single element, but it is a bit tricky. Having one element in the
to the second-last item, etc. parentheses is not sufficient, there must be a trailing ‘comma’ to make it a tuple
# Creating an empty tuple
Python3
# Python program to demonstrate Tuple1 = ()

# accessing of element from list print ( "Initial empty Tuple: " )

# Creating a List with print (Tuple1)

# the use of multiple values # Creating a Tuple with

# the use of Strings


List = [ "Geeks" , "For" , "Geeks" ]

# accessing a element from the Tuple1 = ( 'Geeks' , 'For' )

# list using index number print ( "\nTuple with the use of String: " )

print ( "Accessing element from the list" ) print (Tuple1)

print ( List [ 0 ]) # Creating a Tuple with

print ( List [ 2 ]) # the use of list

# accessing a element using list1 = [ 1 , 2 , 4 , 5 , 6 ]


# negative indexing print ( "\nTuple using List: " )
print ( "Accessing element using negative indexing" ) print ( tuple (list1))
# print the last element of list # Creating a Tuple with the
print ( List [ - 1 ]) # use of built-in function
# print the third last element of list
Tuple1 = tuple ( 'Geeks' )
print ( List [ - 3 ])
print ( "\nTuple with the use of function: " )

Output: print (Tuple1)

# Creating a Tuple

# with nested tuples


Accessing element from the list
Geeks
Tuple1 = ( 0 , 1 , 2 , 3 )
Geeks
Accessing element using negative indexing
Tuple2 = ( 'python' , 'geek' )
Geeks
Geeks
Tuple3 = (Tuple1, Tuple2)

print ( "\nTuple with nested tuples: " )


Tuple Data Type print (Tuple3)

Just like a list, a tuple is also an ordered collection of Python objects. The only difference
Output:
between a tuple and a list is that tuples are immutable i.e. tuples cannot be modified after it is
created. It is represented by a tuple class.
Initial empty Tuple:
Creating a Tuple ()

Python module 2 7 Python module 2 8


Tuple with the use of String:
('Geeks', 'For')
Boolean Data Type in Python
Data type with one of the two built-in values, True or False. Boolean objects that are equal to
Tuple using List:
(1, 2, 4, 5, 6) True are truthy (true), and those equal to False are falsy (false). But non-Boolean objects can
be evaluated in a Boolean context as well and determined to be true or false. It is denoted by
Tuple with the use of function:
('G', 'e', 'e', 'k', 's') the class bool.

Tuple with nested tuples: Note – True and False with capital ‘T’ and ‘F’ are valid booleans otherwise python will
((0, 1, 2, 3), ('python', 'geek'))
throw an error.

Python3
Note – The creation of a Python tuple without the use of parentheses is known as Tuple
# Python program to demonstrate boolean type
Packing.
print(type(True))
Access Tuple Items
print ( type ( False ))
In order to access the tuple items refer to the index number. Use the index operator [ ] to
print (type(true)) #gives error
access an item in a tuple. The index must be an integer. Nested tuples are accessed using
nested indexing.
<class 'bool'>
Python3 <class 'bool'>

# Python program to

# demonstrate accessing tuple

tuple1 = tuple ([ 1 , 2 , 3 , 4 , 5 ])
Set Data Type in Python
# Accessing element using indexing In Python, a Set is an unordered collection of data types that is iterable, mutable and has no
print ( "First element of tuple" ) duplicate elements. The order of elements in a set is undefined though it may consist of
print (tuple1[ 0 ]) various elements.
# Accessing element from last Create a Set in Python
# negative indexing
Sets can be created by using the built-in set() function with an iterable object or a sequence
print ( "\nLast element of tuple" )
by placing the sequence inside curly braces, separated by a ‘comma’. The type of elements in
print (tuple1[ - 1 ])
a set need not be the same, various mixed-up data type values can also be passed to the set.
print ( "\nThird last element of tuple" )
# Python program to demonstrate
print (tuple1[ - 3 ])
# Creation of Set in Python

Output: # Creating a Set

set1 = set ()
First element of tuple print ( "Initial blank Set: " )
1
print (set1)
Last element of tuple # Creating a Set with
5
# the use of a String
Third last element of tuple
set1 = set ( "GeeksForGeeks" )
3
print ( "\nSet with the use of String: " )

Python module 2 9 Python module 2 10


print (set1) # for loop

# Creating a Set with print ( "\nElements of set: " )

# the use of a List


for i in set1:

set1 = set ([ "Geeks" , "For" , "Geeks" ]) print (i, end = " " )

print ( "\nSet with the use of List: " ) # Checking the element

print (set1) # using in keyword

# Creating a Set with


print ( "Geeks" in set1)
# a mixed type of values

# (Having numbers and strings) Output:


set1 = set ([ 1 , 2 , 'Geeks' , 4 , 'For' , 6 , 'Geeks' ])

print ( "\nSet with the use of Mixed Values" ) Initial set:


{'Geeks', 'For'}
print (set1)
Elements of set:
Output: Geeks For

True

Initial blank Set:


set()

Set with the use of String:


Dictionary Data Type in Python
{'F', 'o', 'G', 's', 'r', 'k', 'e'}
A dictionary in Python is an unordered collection of data values, used to store data values
Set with the use of List: like a map, unlike other Data Types that hold only a single value as an element, a Dictionary
{'Geeks', 'For'}
holds a key: value pair. Key-value is provided in the dictionary to make it more optimized.
Set with the use of Mixed Values Each key-value pair in a Dictionary is separated by a colon : , whereas each key is separated
{1, 2, 4, 6, 'Geeks', 'For'}
by a ‘comma’.

Create a Dictionary
Dictionary Data Type in Python
In Python, a Dictionary can be created by placing a sequence of elements within curly {}
Access Set Items braces, separated by ‘comma’. Values in a dictionary can be of any datatype and can be
Set items cannot be accessed by referring to an index, since sets are unordered the items has duplicated, whereas keys can’t be repeated and must be immutable. The dictionary can also
no index. But you can loop through the set items using a for loop, or ask if a specified value be created by the built-in function dict(). An empty dictionary can be created by just placing
is present in a set, by using the in the keyword. it in curly braces{}. Note – Dictionary keys are case sensitive, the same name but different
cases of Key will be treated distinctly.
Python3
# Python program to demonstrate Python3
# Accessing of elements in a set # Creating an empty Dictionary

# Creating a set Dict = {}

set1 = set ([ "Geeks" , "For" , "Geeks" ]) print ( "Empty Dictionary: " )

print ( "\nInitial set" ) print ( Dict )

print (set1) # Creating a Dictionary

# Accessing element using # with Integer Keys

Python module 2 11 Python module 2 12


# Python program to demonstrate
Dict = { 1 : 'Geeks' , 2 : 'For' , 3 : 'Geeks' }
# accessing a element from a Dictionary
print ( "\nDictionary with the use of Integer Keys: " )
# Creating a Dictionary
print ( Dict )

# Creating a Dictionary Dict = { 1 : 'Geeks' , 'name' : 'For' , 3 : 'Geeks' }

# with Mixed keys # accessing a element using key

print ( "Accessing a element using key:" )


Dict = { 'Name' : 'Geeks' , 1 : [ 1 , 2 , 3 , 4 ]}
print ( Dict [ 'name' ])
print ( "\nDictionary with the use of Mixed Keys: " )
# accessing a element using get()
print ( Dict )
# method
# Creating a Dictionary
print ( "Accessing a element using get:" )
# with dict() method
print ( Dict .get( 3 ))
Dict = dict ({ 1 : 'Geeks' , 2 : 'For' , 3 : 'Geeks' })

print ( "\nDictionary with the use of dict(): " ) Output:


print ( Dict )

# Creating a Dictionary Accessing a element using key:


# with each item as a Pair For
Accessing a element using get:
Dict = dict ([( 1 , 'Geeks' ), ( 2 , 'For' )]) Geeks

print ( "\nDictionary with each item as a pair: " )

print ( Dict )

Output:

Empty Dictionary:
{}

Dictionary with the use of Integer Keys:


{1: 'Geeks', 2: 'For', 3: 'Geeks'}

Dictionary with the use of Mixed Keys:


{1: [1, 2, 3, 4], 'Name': 'Geeks'}

Dictionary with the use of dict():


{1: 'Geeks', 2: 'For', 3: 'Geeks'}

Dictionary with each item as a pair:


{1: 'Geeks', 2: 'For'} String Concatenation
You can join two or more strings to form a new string using the concatenation operator +.
Accessing Key-value in Dictionary Here is an example:

In order to access the items of a dictionary refer to its key name. Key can be used inside
"Hi " + "there, " + "Ken!"
square brackets. There is also a method called get() that will also help in accessing the 'Hi there. Ken!'
element from a dictionary.

Python3

Python module 2 13 Python module 2 14


The * operator allows you to build a string by repeating another string a given number of operators
times. The left operand is a string, and the right operand is an integer. For example, if you
Python divides the operators in the following groups:
want the string "Python" to be preceded by 10 spaces, it would be easier to use the * operator
with 10 and one space than to enter the 10 spaces by hand. The next session shows the Arithmetic operators
use of the * and + operators to achieve this result: Assignment operators

Comparison operators
" " * 10 + "Python"
‘ Python' Logical operators

Identity operators
Variables Membership operators
Variables are containers for storing data values.
Bitwise operators
Creating Variables Python Arithmetic Operators
Python has no command for declaring a variable. Arithmetic operators are used with numeric values to perform common mathematical
A variable is created the moment you first assign a value to it. Variables do not need to be operations:
declared with any particular type, and can even change type after they have been set.

x = 5

y = "John"

print(x)

print(y)

x = 4 # x is of type int

x = "Sally" # x is now of type str

print(x)

If you want to specify the data type of a variable, this can be done with casting. Python Assignment Operators

Assignment operators are used to assign values to variables:


x = str(3) # x will be '3'

y = int(3) # y will be 3

z = float(3) # z will be 3.0

You can get the data type of a variable with the type() function.
Variable names are case-sensitive.

Python module 2 15 Python module 2 16


Python Identity Operators

Identity operators are used to compare the objects, not if they are equal, but if they are
actually the same object, with the same memory location:

Python Comparison Operators

Comparison operators are used to compare two values:


Python Membership Operators

Membership operators are used to test if a sequence is presented in an object:

Python Logical Operators Python Bitwise Operators

Logical operators are used to combine conditional statements: Bitwise operators are used to compare (binary) numbers:

Python module 2 17 Python module 2 18


#output:
#You are eligible to vote.

2. if-else statement:
The if-else statement is used to execute one block of code if a condition is true and
another block of code if the condition is false.

if condition:
# Code to execute if condition is true
else:
# Code to execute if condition is false

Example:

temperature = 25
if temperature > 30:
print("It's hot outside.")
else:
print("It's not very hot outside.")

#output:
#It's not very hot outside.
Control statements
Control statements in programming are used to control the flow of a program based on
3. if-elif-else statement:
certain conditions or to repeat a block of code multiple times. Two common types of control
The if-elif-else statement allows you to check multiple conditions in sequence and
statements are branching control (if statements) and looping control (while and for loops).
execute different code blocks based on which condition is true.
Let's go through each of these control statements with their syntax and simple example
programs.
if condition1:
1. Simple if statement: # Code to execute if condition1 is true
elif condition2:
The if statement is used to execute a block of code if a specified condition is true. If # Code to execute if condition2 is true
the condition is false, the code block is skipped. else:
# Code to execute if neither condition1 nor condition2 is true

if condition:
# Code to execute if condition is true Example:

Example: score = 85
if score >= 90:
print("You got an A.")
elif score >= 80:
age = 18
print("You got a B.")
if age >= 18:
else:
print("You are eligible to vote.")
print("You got a C or below.")

Python module 2 19 Python module 2 20


#output:
Continue statement:
#You got a B.
In Python, the continue statement is used inside loops (such as for or while ) to skip the
rest of the current iteration and move to the next iteration. It allows you to skip certain
4. While loop: iterations based on a condition.
A while loop repeatedly executes a block of code as long as a specified condition is
true.
for i in range(5):
if i == 2:
continue # Skip iteration when i is 2
while condition:
print(i)
# Code to execute while condition is true
#output:
0
Example: 1
3
4
count = 0
while count < 5:
print("Count is", count) In this example, when i is 2, the continue statement skips the print(i) line for that
count += 1
iteration.
#output:
#Count is 0
#Count is 1
#Count is 2
Break statement:
#Count is 3 The break statement is used inside loops to exit the loop prematurely, even if the loop
#Count is 4
condition is still true.

5. For loop: for i in range(5):


A for loop is used to iterate over a sequence (such as a list, tuple, or string) and execute if i == 3:
break # Exit the loop when i is 3
a block of code for each item in the sequence. print(i)

#output:
for item in sequence: 0
# Code to execute for each item in the sequence 1
2

Example:
In this example, when i is 3, the break statement terminates the loop.

fruits = ["apple", "banana", "cherry"]


for fruit in fruits: Functions
print("I like", fruit)
Functions are a fundamental concept in programming that allow you to encapsulate and reuse
#output: blocks of code. Here, I'll explain how to define, call, return values from, use default
#I like apple
#I like banana
arguments in functions, create recursive functions, and define nested functions in Python.
#I like cherry
Defining Functions:

Python module 2 21 Python module 2 22


result = add(3, 5)
In Python, you define a function using the def keyword, followed by the function name, a
print(result) # Output: 8
pair of parentheses, and a colon. The code block indented under the def statement defines
the function's body.
Functions with Default Arguments:
Syntax:
You can provide default values for function parameters. These default values are used when
the caller doesn't provide a value for that parameter.
def function_name(parameters):
# function body
Syntax:
# ...

def greet_with_default(name="Guest"):
Example: print(f"Hello, {name}!")

greet_with_default() # Output: Hello, Guest!


def greet(name): greet_with_default("Alice") # Output: Hello, Alice!
print(f"Hello, {name}!")

# Calling the function


Recursive Functions:
greet("Alice")
# Output: Hello, Alice!
A recursive function is a function that calls itself. It's commonly used for tasks that can be
broken down into simpler, similar subtasks.
Calling Functions: Example: Calculating factorial using recursion
You call a function by using its name followed by parentheses, passing any required
arguments within the parentheses. def factorial(n):
if n == 0:
Syntax: return 1
else:
return n * factorial(n-1)
function_name(arguments)
result = factorial(5)
print(result) # Output: 120

Example:
Nested Functions:
greet("Bob")
# Output: Hello, Bob!
You can define a function inside another function. These are called nested functions or inner
functions.

Returning Values from Functions: Example:

You can use the return statement to return a value from a function. The function can return
def outer_function(x):
one or more values. def inner_function(y):
return y * 2
Syntax: return inner_function(x)

result = outer_function(3)
def add(a, b): print(result) # Output: 6
return a + b

Python module 2 23 Python module 2 24


Nested functions have access to variables in their enclosing scope (closure).
def my_function(child3, child2, child1):
print("The youngest child is " + child3)
my_function(child1 = "Emil", child2 = "Tobias", child3 = "Linus")
In Python, you can define functions that accept different types of arguments, including:
#output:The youngest child is Linus
1. Arbitrary Arguments (Positional):

These are also known as positional arguments. 1. Arbitrary Keyword Arguments:


They allow you to pass a variable number of arguments to a function. These allow you to pass a variable number of keyword arguments to a function.
You can use the * operator before the argument name to indicate that it should You can use the ** operator before the argument name to indicate that it should
accept multiple values. accept multiple keyword arguments as a dictionary.
Example: Example:

def sum_numbers(*args): def print_info(**kwargs):


result = 0 for key, value in kwargs.items():
for num in args: print(f"{key}: {value}")
result += num
return result print_info(name="John", age=25, city="New York")
# Output:
result = sum_numbers(1, 2, 3, 4, 5) # name: John
print(result) # Output: 15 # age: 25
# city: New York

If you do not know how many arguments that will be passed into your function,
If you do not know how many keyword arguments that will be passed into your function,

def my_function(*kids):
print("The youngest child is " + kids[2]) def my_function(**kid):
print("His last name is " + kid["lname"])
my_function("Emil", "Tobias", "Linus")
#output:The youngest child is Linus my_function(fname = "Tobias", lname = "Refsnes")

#output:His last name is Refsnes


1. Keyword Arguments:

These are arguments that are passed to a function using their parameter names. In the examples above, you can see how to use these argument types in Python functions.
They allow you to specify values for specific parameters of a function. For arbitrary arguments, we use *args to accept multiple positional arguments.
Example: For keyword arguments, we specify the parameter names when calling the function.

For arbitrary keyword arguments, we use **kwargs to accept multiple keyword


def greet(name, age):
arguments as a dictionary.
print(f"Hello, {name}! You are {age} years old.")

greet(name="Alice", age=30)
# Output: Hello, Alice! You are 30 years old.
Strings - operation, string functions.

Python module 2 25 Python module 2 26


Strings in Python are sequences of characters and are widely used for working with text. Here
text = "Python"
are some common string operations and string functions in Python: upper_text = text.upper()
lower_text = text.lower()
Common String Operations: print(upper_text) # Output: PYTHON
print(lower_text) # Output: python
1. Concatenation: You can combine two strings using the + operator.

str1 = "Hello"
2. str.strip() : Removes leading and trailing whitespace from a string.
str2 = "World"
result = str1 + " " + str2
print(result) # Output: Hello World text = " Python "
stripped_text = text.strip()
print(stripped_text) # Output: "Python"

2. Length: You can find the length of a string using the len() function.
3. str.split() : Splits a string into a list of substrings based on a delimiter.
text = "Python"
length = len(text)
print(length) # Output: 6 text = "apple,banana,cherry"
fruits = text.split(",")
print(fruits) # Output: ['apple', 'banana', 'cherry']

3. Indexing: You can access individual characters in a string using indexing.


4. str.replace() : Replaces occurrences of a substring with another substring.
text = "Python"
first_char = text[0] # Indexing starts from 0
print(first_char) # Output: P text = "I love programming in Python."
new_text = text.replace("Python", "Java")
print(new_text) # Output: I love programming in Java.

4. Slicing: You can extract substrings using slicing.


5. str.find() and str.index() : These functions find the index of the first occurrence of a
text = "Python Programming" substring.
sub_string = text[0:6] # Get the first 6 characters
print(sub_string) # Output: Python
text = "Python is fun and Python is easy."
index = text.find("Python")
5. String Formatting: You can format strings using f-strings or the str.format() method. print(index) # Output: 0

index = text.index("Python")
print(index) # Output: 0
name = "Alice"
age = 30
message = f"My name is {name} and I am {age} years old."
print(message) # Output: My name is Alice and I am 30 years old. These are some of the common string operations and functions in Python. Strings are
versatile, and Python provides many built-in functions to manipulate and work with them
effectively.
Common String Functions:
1. str.upper() and str.lower() : These functions return the uppercase and lowercase

versions of a string, respectively.

Python module 2 27 Python module 2 28

You might also like