Структурирование данных о доходах и ставках налога в виде таблицы (список кортежей или кортежей кортежей) является огромным улучшением. Как показано в этом примере, он позволяет приблизиться к остальной части задачи с помощью табличного подхода (проследуйте вверх по таблице, чтобы найти максимальную ставку для данной заработной платы, затем проследуйте от этой точки, накапливая налоги вниз и учитывая общую зарплату) .
Чтобы сделать все это моим "Pythonic", мы определим функциональность, а также таблицу налоговых ставок над строкой if __name__==
. Это неявно позволило бы нам импортировать наш файл в любой другой код и использовать этот функционал.
Часть под строкой if __name__ ==
является драйвером, который вызывает функциональность с любым данным вводом (или может использоваться для проведения модульных тестов, так что любой модуль может быть вызван для проверки его собственной функциональности).
Так что наш код может выглядеть примерно так:
#!/usr/bin/env python
tax_table = (
(8350, 0.10),
...
)
def compute(salary):
'''Compute taxes for a given salary'''
result = 0
accounted_for = 0
...
return result
if __name__ == "__main__":
import sys
try:
sal = float(raw_input("Please enter salary: ")
except EnvironmentError, err:
print >> sys.stderr, "Error with your input, aborting"
print >> sys.stderr, "The error was:", err
sys.exit(1)
print compute(sal)
Обратите внимание, что теперь мы отделили многократно используемую функциональность от нашего использования ... что позволяет нам повторно использовать код ... но также облегчает разработку и рефакторинг через тестирование. Мы можем написать неинтерактивные тестовые наборы, используя наш тот же API (пока только вызов функции compute () ), и это позволит нам с уверенностью провести рефакторинг (и не затрагивая наше использование ниже --- что является нашим «приложением» в данном случае).
Не ясно, что этот конкретный код выиграл бы от рефакторинга в один или несколько классов. Конечно, была бы полезна возможность создания экземпляра класса с другой таблицей налогов. Тогда налоговая ставка может быть сохранена в другом месте (считано из файла, извлечено с веб-сервера или запрошено из базы данных; Python делает все это почти одинаково легко).
Однако нам не нужно идти «OO», чтобы добавить эту функцию в нашу compute () функцию.
Мы могли бы добавить необязательный параметр к функции вычисления, чтобы он использовал другую таблицу налоговых ставок, если мы ее предоставили, или по умолчанию ту, которую мы жестко запрограммировали в модуле. Для этого мы просто изменим начальную строку определения функции на: def compute(salary, table=tax_table):
... и исправим некоторую обработку для верхнего предела (с учетом коэффициента 0,35 из функции и в таблицу, используя либо «sys.maxint», как наш лимит или объект "Нет").
За такое простое упражнение не стоит сильно волноваться. Но в целом лучше приложить значительные усилия для определения желаемых API заранее. Если вы можете создать надежный и гибкий API, то любая соответствующая реализация, которая соответствует вашим начальным требованиям (правильность и приемлемая производительность), позволит вам доставить ваше приложение.
Что еще важнее, вы можете заново реализовать его по своему желанию. Возможно, нужно искать действительно сложную таблицу налогов, используя что-то вроде модуля bisect , потому что линейный поиск занимает слишком много времени, чтобы найти максимальную ставку налога, или требуются некоторые виды налоговых льгот и вычетов или количества иждивенцев для передачи в функцию compute () и т. д.
В идеале такие изменения могут быть сделаны прозрачно. Ни одно из ваших существующих применений не должно измениться, потому что вы повторно внедрили внутренние компоненты нашего модуля. Даже когда вы добавили функциональность, вам не нужно беспокоиться о существующем использовании (необязательные параметры и аргументы «ключевого слова» (словари, передаваемые после необязательных аргументов), давайте сделаем это для функций, а классы могут добавлять атрибуты и методы без помех любое правильное существующее использование. (Да, возможно, использование подклассов может быть нарушено некоторыми изменениями; но обычно это не должно быть проблемой).
В Python можно написать что-то в виде простого модуля Python, а затем повторно реализовать его как пакет или повторно реализовать его как скомпилированный модуль C или как пакет, содержащий несколько модулей C ... и все это без влияния на использование. С точки зрения пользователя оператор import работает одинаково на модулях, пакетах и скомпилированных модулях Python («общих объектах» или DLL).
Исторически это было огромным преимуществом для Python в его собственной разработке. Они смогли добавить значительную функциональность к существующим библиотекам, и лишь в редких случаях их заставляли использовать искажения «отменить / переименовать». По этой причине в Python 2.7 можно было добавить довольно много функций, запланированных для Python 3.0.