Reconsider Design Decision

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

In this section we reconsider a design decision made earlier.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/FileHandlingGroup(["`File Handling`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/ErrorandExceptionHandlingGroup(["`Error and Exception Handling`"]) python(("`Python`")) -.-> python/AdvancedTopicsGroup(["`Advanced Topics`"]) python(("`Python`")) -.-> python/PythonStandardLibraryGroup(["`Python Standard Library`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/FileHandlingGroup -.-> python/with_statement("`Using with Statement`") python/BasicConceptsGroup -.-> python/variables_data_types("`Variables and Data Types`") python/BasicConceptsGroup -.-> python/numeric_types("`Numeric Types`") python/BasicConceptsGroup -.-> python/strings("`Strings`") python/BasicConceptsGroup -.-> python/booleans("`Booleans`") python/ControlFlowGroup -.-> python/conditional_statements("`Conditional Statements`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/ControlFlowGroup -.-> python/break_continue("`Break and Continue`") python/ControlFlowGroup -.-> python/list_comprehensions("`List Comprehensions`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/DataStructuresGroup -.-> python/sets("`Sets`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("`Catching Exceptions`") python/ErrorandExceptionHandlingGroup -.-> python/raising_exceptions("`Raising Exceptions`") python/FileHandlingGroup -.-> python/file_opening_closing("`Opening and Closing Files`") python/AdvancedTopicsGroup -.-> python/iterators("`Iterators`") python/PythonStandardLibraryGroup -.-> python/data_collections("`Data Collections`") python/PythonStandardLibraryGroup -.-> python/os_system("`Operating System and System`") python/BasicConceptsGroup -.-> python/python_shell("`Python Shell`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-132719{{"`Reconsider Design Decision`"}} python/with_statement -.-> lab-132719{{"`Reconsider Design Decision`"}} python/variables_data_types -.-> lab-132719{{"`Reconsider Design Decision`"}} python/numeric_types -.-> lab-132719{{"`Reconsider Design Decision`"}} python/strings -.-> lab-132719{{"`Reconsider Design Decision`"}} python/booleans -.-> lab-132719{{"`Reconsider Design Decision`"}} python/conditional_statements -.-> lab-132719{{"`Reconsider Design Decision`"}} python/for_loops -.-> lab-132719{{"`Reconsider Design Decision`"}} python/break_continue -.-> lab-132719{{"`Reconsider Design Decision`"}} python/list_comprehensions -.-> lab-132719{{"`Reconsider Design Decision`"}} python/lists -.-> lab-132719{{"`Reconsider Design Decision`"}} python/tuples -.-> lab-132719{{"`Reconsider Design Decision`"}} python/sets -.-> lab-132719{{"`Reconsider Design Decision`"}} python/function_definition -.-> lab-132719{{"`Reconsider Design Decision`"}} python/importing_modules -.-> lab-132719{{"`Reconsider Design Decision`"}} python/standard_libraries -.-> lab-132719{{"`Reconsider Design Decision`"}} python/catching_exceptions -.-> lab-132719{{"`Reconsider Design Decision`"}} python/raising_exceptions -.-> lab-132719{{"`Reconsider Design Decision`"}} python/file_opening_closing -.-> lab-132719{{"`Reconsider Design Decision`"}} python/iterators -.-> lab-132719{{"`Reconsider Design Decision`"}} python/data_collections -.-> lab-132719{{"`Reconsider Design Decision`"}} python/os_system -.-> lab-132719{{"`Reconsider Design Decision`"}} python/python_shell -.-> lab-132719{{"`Reconsider Design Decision`"}} python/build_in_functions -.-> lab-132719{{"`Reconsider Design Decision`"}} end

Filenames versus Iterables

Compare these two programs that return the same output.

## Provide a filename
def read_data(filename):
    records = []
    with open(filename) as f:
        for line in f:
            ...
            records.append(r)
    return records

d = read_data('file.csv')
## Provide lines
def read_data(lines):
    records = []
    for line in lines:
        ...
        records.append(r)
    return records

with open('file.csv') as f:
    d = read_data(f)
  • Which of these functions do you prefer? Why?
  • Which of these functions is more flexible?

Deep Idea: "Duck Typing"

Duck Typing is a computer programming concept to determine whether an object can be used for a particular purpose. It is an application of the duck test.

If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.

In the second version of read_data() above, the function expects any iterable object. Not just the lines of a file.

def read_data(lines):
    records = []
    for line in lines:
        ...
        records.append(r)
    return records

This means that we can use it with other lines.

## A CSV file
lines = open('data.csv')
data = read_data(lines)

## A zipped file
lines = gzip.open('data.csv.gz','rt')
data = read_data(lines)

## The Standard Input
lines = sys.stdin
data = read_data(lines)

## A list of strings
lines = ['ACME,50,91.1','IBM,75,123.45', ... ]
data = read_data(lines)

There is considerable flexibility with this design.

Question: Should we embrace or fight this flexibility?

Library Design Best Practices

Code libraries are often better served by embracing flexibility. Don't restrict your options. With great flexibility comes great power.

Exercise 3.17: From filenames to file-like objects

You've now created a file fileparse.py that contained a function parse_csv(). The function worked like this:

>>> import fileparse
>>> portfolio = fileparse.parse_csv('portfolio.csv', types=[str,int,float])
>>>

Right now, the function expects to be passed a filename. However, you can make the code more flexible. Modify the function so that it works with any file-like/iterable object. For example:

>>> import fileparse
>>> import gzip
>>> with gzip.open('portfolio.csv.gz', 'rt') as file:
...      port = fileparse.parse_csv(file, types=[str,int,float])
...
>>> lines = ['name,shares,price', 'AA,100,34.23', 'IBM,50,91.1', 'HPE,75,45.1']
>>> port = fileparse.parse_csv(lines, types=[str,int,float])
>>>

In this new code, what happens if you pass a filename as before?

>>> port = fileparse.parse_csv('portfolio.csv', types=[str,int,float])
>>> port
... look at output (it should be crazy) ...
>>>

Yes, you'll need to be careful. Could you add a safety check to avoid this?

Exercise 3.18: Fixing existing functions

Fix the read_portfolio() and read_prices() functions in the report.py file so that they work with the modified version of parse_csv(). This should only involve a minor modification. Afterwards, your report.py and pcost.py programs should work the same way they always did.

Summary

Congratulations! You have completed the Design Discussion lab. You can practice more labs in LabEx to improve your skills.

Other Python Tutorials you may like