# Python Programming

## 9.1 Map, Filter, Lambda

### map()

def f(x):
return x * x

y = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(y))

[1, 4, 9, 16, 25, 36, 49, 64, 81]

print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))

['1', '2', '3', '4', '5', '6', '7', '8', '9']

### filter()

def k(x):
return x%2 == 0

y = list(filter(k, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
print(y)

[2, 4, 6, 8]

### Anonymous function: lambda

return x + y

lambda x, y: x + y

lambda x, y = 2: x + y
lambda *z: z
• Sometimes the anonymous function is convenient.

• It has only one expression. (You do not have to use return)

print(list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])))

print(list(filter(lambda x: x%2==0, [1, 2, 3, 4, 5, 6, 7, 8, 9])))

### Comprehension Syntax

• [ expression for value in iterable if condition ]

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']

>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']

## 9.2 Return a Function

### Closure Function

def print_msg(msg):
def printer():
print(msg)
return printer

another = print_msg("zen of python")
print(another())
• We must have a nested function (function inside a function).

• The nested function must refer to a value defined in the enclosing function.

• The enclosing function must return the nested function.

When to use closures?

• Closures can avoid the use of global values and provides some form of data hiding. It can also provide an object oriented solution to the problem.

### Example

def lazy_sum(*args):
def calc_sum():
ax = 0
for n in args:
ax = ax + n
return ax
return calc_sum

f = lazy_sum(1, 3, 5, 7, 9)
print(f())
print(type(f))

25
<class 'function'>

def make_multiplier_of(n):
def multiplier(x):
return x * n
return multiplier

times3 = make_multiplier_of(3)
times5 = make_multiplier_of(5)

print(times3(9))
print(times5(3))
print(times5(times3(2)))

### Decorator

• Decorators are "wrappers", which means that they let you execute code before and after the function they decorate without modifying the function itself.

def new_decorator(a_function_to_decorate):
def the_wrapper():
print("Before the function runs")
a_function_to_decorate()
print("After the function runs")
return the_wrapper

def a_function():
print("I am a stand alone function.")

a_function_decorated = new_decorator(a_function)
a_function_decorated()

Before the function runs
I am a stand alone function.
After the function runs

@new_decorator
def another_stand_alone_function():
print("Leave me alone")

another_stand_alone_function()

Before the function runs
Leave me alone
After the function runs

def wrapper():
print("")
func()
print("<\______/>")
return wrapper

def ingredients(func):
def wrapper():
print("#tomatoes#")
func()
return wrapper

def sandwich(food="--ham--"):
print(food)

sandwich()

--ham--

@ingredients # The order matters here.
def sandwich(food="--ham--"):
print(food)

sandwich()

#tomatoes#
--ham--
<\______/>
• Taking decorators to the next level

• Passing arguments to the decorated function

def passing_arguments(function_to_decorate):
def wrapper(arg1, arg2):
print(f"I got args! Look: {arg1}, {arg2}")
function_to_decorate(arg1, arg2)
return wrapper

@passing_arguments
def print_name(first_name, last_name):
print(f"My name is {first_name}, {last_name}")

print_name("Peter", "Venkman")

I got args! Look: Peter, Venkman
My name is Peter, Venkman
• If you're making general-purpose decorator--one you'll apply to any function or method, no matter its arguments--then just use *args, **kwargs:

def passing_arbitrary_arguments(function_to_decorate):
def wrapper(*args, **kwargs):
print("Do I have args?:")
print(args)
print(kwargs)
function_to_decorate(*args, **kwargs)
return wrapper

@passing_arbitrary_arguments
def function_with_no_argument():
print("Python is cool, no argument here.")

function_with_no_argument()

Do I have args?:
()
{}
Python is cool, no argument here.

@passing_arbitrary_arguments
def function_with_arguments(a, b, c, d):
print(a, b, c, d)

function_with_arguments(1,2,3,4)

Do I have args?:
(1, 2, 3, 4)
{}
1 2 3 4

function_with_arguments(1,2,3,d = "banana")

Do I have args?:
(1, 2, 3)
{'d': 'banana'}
1 2 3 banana

## 9.3 File

• pi_digits.txt

3.1415926535
8979323846
2643383279

from pathlib import Path

path = Path('pi_digits.txt')
contents = contents.rstrip()
print(contents)

3.1415926535
8979323846
2643383279

### File Path

• relative path

path = Path('text_files/filename.txt')
• absolute path

path = Path('/home/eric/data_files/text_files/filename.txt')

path = Path('c:/text_files/filename.txt')
path = Path(r'c:\text_files\filename.txt')
path = Path('c:\\text_files\\filename.txt')

path = Path('c:\text_files\filename.txt')  #error
• Making a List of Lines from a File

from pathlib import Path

path = Path('pi_digits.txt')

lines = contents.splitlines()
print(lines)
• Working with a File's Contents

pi_string = ''
for line in lines:
pi_string = pi_string + line
print(pi_string)
print(len(pi_string))

['3.1415926535', '8979323846', '2643383279']
3.141592653589793238462643383279 # string
32

### Writing to a File

from pathlib import Path

path = Path('programming.txt')

path.write_text("I love programming.")
• Python can only write strings to a text file. If you want to store numerical data in a text file, you'll have to convert the data to string format first using the str() function.

from pathlib import Path

contents = "I love programming.\n"
contents += "I love creating new games.\n"
contents += "I also love working with data.\n"

path = Path('programming.txt')
path.write_text(contents)

I love programming.
I love creating new games.
I also love working with data.
• Handling the FileNotFoundError Exception

from pathlib import Path

path = Path('alice.txt')

from pathlib import Path

path = Path('alice.txt')
try:
except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")

Sorry, the file alice.txt does not exist.
• ZeroDivisionError

• try-except-else

from pathlib import Path

path = Path('alice.txt')
try:
except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
else:
# Count the approximate number of words in the file:
words = contents.split()
num_words = len(words)
print(f"The file {path} has about {num_words} words.")

### JSON

• JSON (JavaScript Object Notation, pronounced /ˈdʒeɪsən/; also /ˈdʒeɪˌsɒn/) is an open standard file format and data interchange format that uses human-readable text to store and transmit data objects consisting of attribute–value pairs and arrays (or other serializable values). It is a common data format with diverse uses in electronic data interchange, including that of web applications with servers.
• JSON is a language-independent data format. It was derived from JavaScript, but many modern programming languages include code to generate and parse JSON-format data. JSON filenames use the extension .json.

from pathlib import Path
import json

numbers = [2, 3, 5, 7, 11, 13]

path = Path('numbers.json')
contents = json.dumps(numbers)
path.write_text(contents)

from pathlib import Path
import json

path = Path('numbers.json')

print(numbers)

[2, 3, 5, 7, 11, 13]
• Saving and Reading User-Generated Data

from pathlib import Path
import json

path.write_text(contents)

print(f"We'll remember you when you come back, {username}!")

from pathlib import Path
import json

from pathlib import Path
import json

if path.exists():
else:
return None

path.write_text(contents)

def greet_user():
"""Greet the user by name."""
else: