Введение
В этом разделе мы пересматриваем решение по дизайну, принятое ранее.
Имена файлов versus Итерируемые объекты
Сравните эти два программы, которые возвращают один и тот же вывод.
## Предоставьте имя файла
def read_data(filename):
records = []
with open(filename) as f:
for line in f:
...
records.append(r)
return records
d = read_data('file.csv')
## Предоставьте строки
def read_data(lines):
records = []
for line in lines:
...
records.append(r)
return records
with open('file.csv') as f:
d = read_data(f)
- Какую из этих функций вы предпочитаете? Почему?
- Какая из этих функций более гибкая?
Глубокая идея: "Duck Typing"
Duck Typing - это концепция компьютерного программирования для определения, может ли объект быть использован для определенной цели. Это приложение теста утки.
Если оно выглядит как утка, плавает как утка и крякает как утка, то, вероятно, это утка.
Во второй версии read_data() выше функция ожидает любого итерируемого объекта. Не только строки файла.
def read_data(lines):
records = []
for line in lines:
...
records.append(r)
return records
Это означает, что мы можем использовать его с другими строками.
## CSV-файл
lines = open('data.csv')
data = read_data(lines)
## Архивированный файл
lines = gzip.open('data.csv.gz','rt')
data = read_data(lines)
## Стандартный ввод
lines = sys.stdin
data = read_data(lines)
## Список строк
lines = ['ACME,50,91.1','IBM,75,123.45',...]
data = read_data(lines)
В этом дизайне есть значительная гибкость.
Вопрос: Следует ли мы принимать или бороться с этой гибкостью?
Лучшие практики по дизайну библиотек
Библиотеки кода часто лучше всего служат, когда они придерживаются гибкости. Не ограничивайте свои возможности. С большой гибкостью приходит большая сила.
Упражнение 3.17: От имен файлов до объектов, подобных файлам
Теперь вы создали файл fileparse.py, в котором содержалась функция parse_csv(). Функция работала так:
>>> import fileparse
>>> portfolio = fileparse.parse_csv('portfolio.csv', types=[str,int,float])
>>>
В настоящее время функция ожидает, что ей передадут имя файла. Однако вы можете сделать код более гибким. Измените функцию так, чтобы она работала с любым объектом, подобным файлу/итерируемым объектом. Например:
>>> 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])
>>>
В этом новом коде что произойдет, если вы передадите имя файла, как раньше?
>>> port = fileparse.parse_csv('portfolio.csv', types=[str,int,float])
>>> port
... посмотрите на вывод (он должен быть странным)...
>>>
Да, вам нужно быть осторожными. Возможно, вы можете добавить проверку безопасности, чтобы избежать этого?
Упражнение 3.18: Исправление существующих функций
Исправьте функции read_portfolio() и read_prices() в файле report.py, чтобы они работали с модифицированной версией parse_csv(). Это должно потребовать только незначительных изменений. Затем ваши программы report.py и pcost.py должны работать так же, как и всегда.
Резюме
Поздравляем! Вы завершили лабораторную работу по обсуждению дизайна. Вы можете практиковаться в более многих лабораторных работах в LabEx, чтобы улучшить свои навыки.