Python Programming

Lecture 9 Advanced Features, File

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


def add( x, y ):
    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 bread(func):
    def wrapper():
        print("")
        func()
        print("<\______/>")
    return wrapper

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

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

sandwich()


--ham--

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

sandwich()


 #tomatoes#
 --ham--
 ~salad~
<\______/>
  • 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

Reading from a File

  • pi_digits.txt


3.1415926535
8979323846
2643383279

from pathlib import Path

path = Path('pi_digits.txt')
contents = path.read_text()
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')
contents = path.read_text()

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')
contents = path.read_text(encoding='utf-8') #Error

from pathlib import Path

path = Path('alice.txt')
try:
    contents = path.read_text(encoding='utf-8')
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:
    contents = path.read_text(encoding='utf-8')
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.
  • json.dumps() and json.loads()

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')
contents = path.read_text()
numbers = json.loads(contents)

print(numbers)

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


from pathlib import Path
import json

username = input("What is your name? ")

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

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

from pathlib import Path
import json

path = Path('username.json')
contents = path.read_text()
username = json.loads(contents)

print(f"Welcome back, {username}!")

from pathlib import Path
import json


def get_stored_username(path):
    """Get stored username if available."""
    if path.exists():
        contents = path.read_text()
        username = json.loads(contents)
        return username
    else:
        return None

def get_new_username(path):
    """Prompt for a new username."""
    username = input("What is your name? ")
    contents = json.dumps(username)
    path.write_text(contents)
    return username

def greet_user():
    """Greet the user by name."""
    path = Path('username.json')
    username = get_stored_username(path)
    if username:
        print(f"Welcome back, {username}!")
    else:
        username = get_new_username(path)
        print(f"We'll remember you when you come back, {username}!")

greet_user()

What is your name? Eric #First time
We'll remember you when you come back, Eric!

Welcome back, Eric!

Summary

  • File
    • Reading: Python Crash Course, Chapter 10