# 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


with open('pi_digits.txt') as file_object:
print(contents.rstrip())


3.1415926535
8979323846
2643383279


### File Path

• relative path


with open('text_files/filename.txt') as file_object: # Linux or OSX
pass
with open('text_files\filename.txt') as file_object: # Windows
pass

• absolute path


# Linux or OSX
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
pass

# Windows
file_path = r'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:
pass



filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line)


3.1415926535

8979323846

2643383279



filename = 'pi_digits.txt'
with open(filename) as file_object:
for line in file_object:
print(line.rstrip())


3.1415926535
8979323846
2643383279

• Making a List of Lines from a File


filename = 'pi_digits.txt'

with open(filename) as file_object:

• Working with a File's Contents


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


3.141592653589793238462643383279 # string
32


### Writing to a File


filename = 'programming.txt'

with open(filename, 'w') as file_object:
file_object.write("I love programming.")

• The second argument, 'w', tells Python that we want to open the file in write mode. You can open a file 198 Chapter 10 in read mode ('r'), write mode ('w'), append mode ('a'), or a mode that allows you to read and write to the file ('r+'). If you omit the mode argument, Python opens the file in read-only mode by default.

• 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.

• Writing Multiple Lines


filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.")
file_object.write("I love creating new games.")


I love programming.I love creating new games.


filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write("I love programming.\n")
file_object.write("I love creating new games.\n")



I love programming.
I love creating new games.

• Appending to a File


filename = 'programming.txt'
message.py
with open(filename, 'a') as file_object:
file_object.write("I also love finding meaning in large datasets.\n")
file_object.write("I love creating apps that can run in a browser.\n")


I love programming.
I love creating new games.
I also love finding meaning in large datasets.
I love creating apps that can run in a browser.

• Handling the FileNotFoundError Exception


filename = 'alice.txt'
with open(filename) as f_obj:


filename = 'alice.txt'
try:
with open(filename) as f_obj:
except FileNotFoundError:
msg = f"Sorry, the file {filename} does not exist."
print(msg)


Sorry, the file alice.txt does not exist.

• ZeroDivisionError

• try-except-else

filename = 'alice.txt'
try:
with open(filename) as f_obj:
except FileNotFoundError:
msg = f"Sorry, the file {filename} does not exist."
print(msg)
else:
# Count the approximate number of words in the file.
words = contents.split()
num_words = len(words)
print(f"The file {filename} 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.

import json

numbers = [2, 3, 5, 7, 11, 13]
filename = 'numbers.json'
with open(filename, 'w') as f_obj:
json.dump(numbers, f_obj)


import json

filename = 'numbers.json'
with open(filename) as f_obj:
print(numbers)


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

• Saving and Reading User-Generated Data


import json

with open(filename, 'w') as f_obj:
print(f"We'll remember you when you come back, {username}!")


import json

with open(filename) as f_obj:


import json

try:
with open(filename) as f_obj:
except FileNotFoundError:
with open(filename, 'w') as f_obj:
print(f"We'll remember you when you come back, {username}!")
else: