воскресенье, 4 октября 2009 г.

Знакомство с Python 3.1

Последние 3 дня занимался знакомством с Python 3.1. Можно сказать щупал его за разные места.
Поставил перед собой простую задачу: Сделать кредитный калькулятор, ты ему сумму кредита, период и процентную ставку, а он тебе график погашения и сумму переплаты.

Задача была разбита на 3 части
1. Получение входных данных для обработки и принятия решения
2. Вычисление платежей и процентных ставок
3. Вывод данных

---------------------------------------------------------------------
Внимание: В процессе написания скрипта я столкнулся с проблемой объявления глобальных переменных. В 3.1 появились такие директивы как global и nonlocal здесь я понял как использовать эти директивы
---------------------------------------------------------------------

Поскольку для меня сейчас интересна работа с регулярными выражениями, я не искал простых путей получения параметров командной строки, а воспользовался мощью RegExp. Возможно это и не корректно с точки зрения культуры языка, но считаю практику весьма полезной. И так получение входных параметров из командной строки.

# Строковая переменная в которой описываем все команды для скрипта
Help = '''
в командной строке использоваться следующие ключи:
-s исходная сумма кредита без учёта процентов
-r процентная ставка по кредиту
-p срок кредита если в конце слова будет стоять "y" то знаем что это год и необходимо рассчитать количество месяцев
-dfc если указан данный ключ то рассчитываем по дифференциальному алгоритму иначе по аннуитентному
/? Вызов данной справки
'''

'''
Процедура получения входных параметров
Используя регулярные выражения
'''

def GetEnterData():
# nonlocal директива определяющая что использовать необходимо
# переменные объявленные в теле основной процедуры
nonlocal  Summa    #Сумма заёма
nonlocal  Rait          # Процентная ставка
nonlocal  Period     # Срок кредита
nonlocal  TypeOf   # Тип указанного периода (год, месяц)
nonlocal  DiferCalc # Тип алгоритма расчёта

# строим строку с которой запустились
# такую закорючку пришлось строить из за непонимания принципов работы с командной строкой
myString = '' # Командная строка
for arg in sys.argv: # Для всех аргументов в командной строке
myString = myString + arg + ' '

# отображаем исходные параметры
# это сделано для контроля, строки которую мы получили из аргументов
print(myString)

# Определяем регулярное выражение  
reg = re.compile(r"(?:-s *(\d*))|(?:-r *(\d*))|(?:-p *(\d*)(y)?)|(\/\?)|(-dfc)")
# находим все совпадения
find = reg.findall(myString)
# заполняем исходные данные
# Поскольку результат reg.findall возвращает нам в данном случае 2х мерный массив
for res in find:
if (Summa == -1) and (res[0] != ''): # проверяем что бы сумма ещё не была заполнена и разобранная строка была не пустой
Summa = res[0]
else:
if (Rait == -1) and (res[1] != ''):
Rait = res[1]
else:
if (Period == -1) and (res[2] != ''):
Period = res[2]
TypeOf = (res[3] != '') # проверяем не указан ли период в годах.
else:
if (res[4] != '') : # проверяем не запросил ли пользователь справку
print(Help)
exit()
else:
if (res[5] != '') : # определяем алгоритмы расчёта если передан требуемый ключь
DiferCalc = True

'''
Конец процедуры получения исходных параметров
'''


Итак ничего сложного в выше описанном коде не было. Тем ни менее я не уверен, что я правильно работаю с модулем RegExp, поскольку на мой взгляд выглядит это как то кривовато.

Итого с первой задачей я разобрался.
Теперь опишем алгоритмы расчёта кредитных выплат

def Main():

'''
Процедура возведения числа _Main в степень _Power
'''

def DoPowerW(_Main, _Power):
result = 1
start = 1
while start < int(_Power):
result = _Main * result
start = start + 1
return result

'''
Конец процедуры возведения в степень
--------------------------------------------------------------
Начало процедуры диференциального расчёта
'''

def GetDifPayment():
# переменные определённые в основном модуле
nonlocal Summa
nonlocal Rait
nonlocal Period
# определяем переменные для внутреннего пользования
Payment = 1
Total = 0
# открываем файл для записи результата ключ w отмечает что файл открыт для записи и все что было в нём до будет переписанно 
file = open('report.txt', 'w')
# высчитываем платеж за каждый месяц  
while int(Payment) < int(Period) + 1:
CurDuty = (int(Summa) - (int(Summa) / int(Period)) * (int(Payment) - 1))
NowToPay = (int(Summa) / int(Period)) + (((int(CurDuty) / 100) * float(Rait)) / 12)
file.write("номер платежа %s;\t основной долг %s;\t Платёж %s\n" % (Payment, CurDuty, NowToPay))
Total = Total + NowToPay
Payment = Payment + 1
file.write("итого по кредиту к выплате: %s\t переплата: %s\n" % (Total, float(Total) - float(Summa)))
file.close()

'''
Конец процедуры диференциального расчёта
-------------------------------------------------------------------------------------
Начало процедуры аннуитетных платежей
'''

def GetApnPayment():

nonlocal Summa
nonlocal Rait
nonlocal Period
file = open('report.txt', 'w')
# вычисляем процентную ставку в месяц
rait = (float(Rait)/100)/12
# вычисляем относительный платёж
AP = rait + (rait/(DoPowerW((1+rait), Period) - 1))
# рассчитываем ежемесячную выплату
AAP = AP * float(Summa)
res = 1
summ = 0
file.write("Ежемесячный платёж составит %sб, количество платежей %s\n" % (AAP, Period))
while res < int(Period) + 1:
summ = summ + AAP
res = res + 1
file.write("итого по кредиту к выплате %s переплата %s\n" % (summ, float(summ) - float(Summa)))
file.close()

'''
Конец Процедуры аннуитентных платежей
'''
Итак, я описал функции расчёта кредитных ставок, осталось всё это собрать в один работающий скрипт.
'''
Нацало основной программы
'''
# определяем исходные переменные по умолчанию заполнены -1
Summa = -1
Rait = -1
Period = -1
TypeOf = False
MonslyPayment = 0
DiferCalc = False

# получаем входные параметры
GetEnterData()

# проверяем что бы все необходимые данные были переданы
if ((Summa == -1) or (Rait == -1) or (Period == -1)):
print ("Не все параметры введены")
exit()

# проверяем не ввёл ли пользователь год вместо месяца
if TypeOf == 1:
Period = int(Period) * 12

print ("заем составляет %s, процентная ставка %s %%, планируемый срок в месяцах %s" % (Summa, Rait, Period))
print ("Ежемесячные платежи составят:")
if DiferCalc:
GetDifPayment()
else:
GetApnPayment()

'''
Конец основной процедуры
'''

# вызов основной процедуры в модуле
Main() 
Вот собственно и весь калькулятор. P.S. На мой взгляд абсолютно бестолковая статья. Но всё же это был мой первый осознаны опыт работы с новым Python. В целом осталось довольно приятное ощущение от 3.1 несмотря на различия с предыдущими версиями. P.P.S. Спасибо

11 комментариев:

  1. прикольно, сам недавно начал разбираться с питоном. Пока впечатления положительные, а поводом стало то что я узнал что есть такая прикольная вещь как Google Application Engine

    з.ы. какой средой разработки пользуешься?

    ОтветитьУдалить
  2. Google рулит :).
    Среда была Eclips Galileo.

    ОтветитьУдалить
  3. Этот комментарий был удален администратором блога.

    ОтветитьУдалить
  4. А ещё что ни будь пробовал?
    Я ещё в notepad++ писал но там дело было без подсветки синтаксиса.

    ОтветитьУдалить
  5. notepad++ конечно пользовал, у меня даже синтаксис подсвечивал и слова предлагал...правда не в попад)
    а пока эклипсик рулит

    ОтветитьУдалить
  6. Чёрт. Написал большой и гневный коммент, но случаеным движением руки что-то не то нажал и он затёрся :( Переписывать лень. В общем, если интересно моё мнение стучись в джабер.

    ОтветитьУдалить
  7. a. не надо вообще использовать глобальные переменные здесь, можно же передавать все через параметры, возвращать кортежем
    b. для возведение в степень есть оператор ** (думаю он остался и в 3.1)
    c. параметры строки лучше все же парсить getopt

    ОтветитьУдалить
  8. Ага спасибо уже в курсе. Про getopt спасибо. Поковыряю.

    ОтветитьУдалить
  9. Осталось прикруть GUI c возможностью построения графиков и будет вообще шик.
    Да и плюс - возможность разобраться с графическими библиотеками.
    PS: А графики можно еще и в картинки экспортировать.

    ОтветитьУдалить
  10. Это уже прям полноценное приложение какое то получается. Интересно нужно будет заняться :).

    ОтветитьУдалить