Как сделать калькулятор в python
Перейти к содержимому

Как сделать калькулятор в python

  • автор:

Сделать калькулятор на Python

Вот текст задачи: Реализуйте простой калькулятор, который позволяет выполнять операции сложения, вычитания, умножения и деления двух целых чисел. Калькулятор должен: Запрашивать у пользователя выбор операции: сложение (1), вычитание (2), умножение (3), или деление (4). Запрашивать два числа для выполнения выбранной операции, каждое в новой строке. Выполнять выбранную операцию над этими числами. Выводить результат операции. Как только пользователь при выборе операции вводит 0, калькулятор завершает работу с текстом «Программа завершена.» Более того, калькулятор обрабатывает ошибки: При выборе деления, если второе число будет 0, он выдаст ошибку «Делить на ноль нельзя!» При вводе неверной команды выдаст ошибку «Неверная команда!» При этом, после вывода ошибок программа продолжает работу, пока не получит команду 0. это мое решение:

operatorr = int(input()) while operatorr != 0: n1 = int(input()) n2 = int(input()) if operatorr == 0: break if operatorr == 1: print(n1 + n2) elif operatorr == 2: print(n1 - n2) elif operatorr == 3: print(n1 * n2) elif operatorr == 4: if n2 == 0: print('Делить на ноль нельзя!') else: print(n1 // n2) else: print('Неверная команда!') operatorr = int(input()) print('Программа завершена.') 

как можно сократить код? Только не нужно через всякие функции и так далее, на уровне именно цикла While, как тут можно покороче сделать, и есть ли какие то ошибки?

Отслеживать

48.7k 17 17 золотых знаков 56 56 серебряных знаков 100 100 бронзовых знаков

Делаем свой кредитный калькулятор на Python

Сегодня поставим Python на практические рельсы — сделаем простой кредитный калькулятор. В приложении будут поля для годовой процентной ставки, срока и суммы кредита, чтобы рассчитывать ежемесячные платежи и общую сумму. В будущем его можно допилить — добавить возможность досрочного погашения, пересчёт процентов и всё остальное.

Если вы ни разу не программировали на Python, почитайте сначала это:

Устанавливаем и подключаем библиотеку

Чтобы создать графический интерфейс нашего кредитного калькулятора, используем библиотеку tkinter. Для этого установим её в систему командой в терминале:

pip install tkinter

Если pip не работает, используйте pip3

pip3 install tkinter

Делаем свой кредитный калькулятор на Python

После этого импортируем библиотеку и подключаем её:

from tkinter import *

Мы уже использовали библиотеку tkinter в других проектах, почитайте, если интересно:

Пишем игру на Python
Без донатов!
Делаем своё приложение для ведения бюджета
Базовая версия
Делаем свой загрузчик видео с Ютуба
Независимый и автономный

Рисуем интерфейс

Нашему кредитному калькулятору нужны поля для ввода данных и кнопка, по нажатию которой он будет производить расчёты. Для этого мы нарисуем:

  • поле для ввода годовой процентной ставки;
  • поле для ввода количества лет в сроке кредита;
  • поле для ввода суммы кредита;
  • кнопку «Рассчитать».

Получится несложный интерфейс и самый простой калькулятор, но для старта этого достаточно. Разместим эти элементы на форме.

# создаём класс для калькулятора class LoanCalculator: # инициализируем класс def __init__(self): # стартуем tkinter, чтобы создать окно графического интерфейса root=Tk() # задаём размеры окна root.geometry("500x300") # задаём название окна калькулятора root.title("Кредитный калькулятор") # задаём цвет окна калькулятора, например пыльно-серый root.config(bg='#a39ea0') # задаём расположение, стиль шрифта и пояснительный текст для поля годовой процентной ставки Label(root,text="Годовая ставка, %", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=10) # задаём расположение, стиль шрифта и пояснительный текст для поля срока кредита Label(root, text="Срок, лет", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=50) # задаём расположение, стиль шрифта и пояснительный текст для поля суммы кредита Label(root, text="Сумма кредита", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=90) # задаём расположение, стиль шрифта и пояснительный текст для вывода ежемесячного платежа Label(root, text="Ежемесячный платёж:", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=150) # задаём расположение, стиль шрифта и пояснительный текст для вывода общей суммы выплаты Label(root, text="Общая сумма выплаты:", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=190) # добавляем поле для ввода годовой процентной ставки self.annualinterestVar=StringVar() Entry(root, textvariable=self.annualinterestVar,font=('Arial,15,bold')).place(x=220,y=10) # добавляем поле для ввода количества лет кредит self.numberofyearsVar=StringVar() Entry(root, textvariable=self.numberofyearsVar,font=('Arial,15,bold')).place(x=220,y=50) # добавляем поле для ввода суммы кредита self.loanamountVar=StringVar() Entry(root, textvariable=self.loanamountVar,font=('Arial,15,bold')).place(x=220,y=90) # добавляем строку вывода расчёта ежемесячного платежа self.monthlypaymentVar=StringVar() Label(root, textvariable=self.monthlypaymentVar,font=('Arial,15,bold'),bg='#a39ea0').place(x=220,y=150) # добавляем строку вывода расчёта общей суммы выплаты self.totalpaymentVar=StringVar() Label(root, textvariable=self.totalpaymentVar,font=('Arial,15,bold'),bg='#a39ea0').place(x=220,y=190) # добавляем кнопку, задаём её расположение, надпись и стиль шрифта Button(root, text="Рассчитать",font=('Arial,15,bold'),command=self.calculateloan).place(x=180,y=240) # запускаем окно root.mainloop()

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

# вызываем класс калькулятора, чтобы запустить программу
LoanCalculator()

Запускаем скрипт и видим готовую форму. Она пока ничего не умеет, но туда уже можно что-то писать:

Делаем свой кредитный калькулятор на Python

Программируем расчёты

Чтобы наш калькулятор работал, нужно указать в коде, что программа должна делать с нашими данными.

Определим функцию для расчёта общей суммы выплаты и переменной ежемесячного платежа. Тут мы будем использовать введённые значения годовой процентной ставки, срока и суммы кредита.

общая сумма выплаты = ежемесячный платёж × 12 × срок

В коде мы используем две команды — float и int. Они работают так: float возвращает значения в десятичном формате, а int — целочисленные. Ещё мы используем форматирование вывода: format(10.2f) , так мы получим 10 символов: семь цифр перед запятой, восьмой символ — десятичная и две цифры после неё.

 # определяем функцию расчёта общей суммы выплаты def calculateloan(self): # определяем формулу, по которой будет рассчитываться ежемесячный платёж по кредиту monthlypayment=self.getmonthlypayment (float(self.loanamountVar.get()),float(self.annualinterestVar.get()) / 1200, int(self.numberofyearsVar.get())) self.monthlypaymentVar.set(format(monthlypayment, '10.2f')) # определяем формулу, по которой будет рассчитываться общая сумма выплаты по кредиту totalpayment=float(self.monthlypaymentVar.get()) * 12 * int(self.numberofyearsVar.get()) self.totalpaymentVar.set(format(totalpayment, '10.2f'))

Для расчёта ежемесячного платежа используем формулу:

ежемесячный платёж = [сумма кредита × ежемесячная процентная ставка / (1 − 1 / 1 + ежемесячная процентная ставка) ^ количество лет × 12].

 # определяем функцию расчёта ежемесячного платежа def getmonthlyPayment(self,loanamount,monthlyinterestrate,numberofyears): # определяем формулу, по которой будет рассчитываться ежемесячный платёж monthlypayment=loanamount * monthlyinterestrate / (1-1 / (1 + monthlyinterestrate) ** (numberofyears * 12)) return monthlypayment

Запускаем калькулятор

Проверим программу в деле: введём конкретные значения во все поля и посмотрим, какой у нас получится ежемесячный платёж и сколько денег нам в итоге придётся заплатить.

Вот что будет на старте до нажатия кнопки «Рассчитать»:

Делаем свой кредитный калькулятор на Python

Теперь нажимаем и смотрим на результат:

Делаем свой кредитный калькулятор на Python

Что дальше

Этот калькулятор можно доработать, например добавить такие штуки:

  • кнопку очистки полей;
  • подсчёт переплаты сверх суммы кредита;
  • расчёт по месяцам, а не по годам;
  • расчёт платежей и остатка при досрочном погашении части кредита.

А самое главное — проверку на правильность введённых данных. Например, чтобы программа сообщала, если мы вместо цифр введём что-то другое, и подсвечивала поля с ошибочными данными.

Попробуйте сделать это сами, мы в вас верим!

Готовый код

# импортируем библиотеку tkinter from tkinter import * # создаём класс для калькулятора class LoanCalculator: # инициализируем класс def __init__(self): # стартуем tkinter, чтобы создать окно графического интерфейса root=Tk() # задаём размеры окна root.geometry("500x300") # задаём название окна калькулятора root.title("Кредитный калькулятор") # задаём цвет окна калькулятора, например пыльно-серый root.config(bg='#a39ea0') # задаём расположение, стиль шрифта и пояснительный текст для поля годовой процентной ставки Label(root,text="Годовая ставка, %", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=10) # задаём расположение, стиль шрифта и пояснительный текст для поля срока кредита Label(root, text="Срок, лет", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=50) # задаём расположение, стиль шрифта и пояснительный текст для поля суммы кредита Label(root, text="Сумма кредита", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=90) # задаём расположение, стиль шрифта и пояснительный текст для вывода ежемесячного платежа Label(root, text="Ежемесячный платёж:", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=150) # задаём расположение, стиль шрифта и пояснительный текст для вывода общей суммы выплаты Label(root, text="Общая сумма выплаты:", font=('Arial,15,bold'),bg='#a39ea0').place(x=10,y=190) # добавляем поле для ввода годовой процентной ставки self.annualinterestVar=StringVar() Entry(root, textvariable=self.annualinterestVar,font=('Arial,15,bold')).place(x=220,y=10) # добавляем поле для ввода количества лет кредит self.numberofyearsVar=StringVar() Entry(root, textvariable=self.numberofyearsVar,font=('Arial,15,bold')).place(x=220,y=50) # добавляем поле для ввода суммы кредита self.loanamountVar=StringVar() Entry(root, textvariable=self.loanamountVar,font=('Arial,15,bold')).place(x=220,y=90) # добавляем строку вывода расчёта ежемесячного платежа self.monthlypaymentVar=StringVar() Label(root, textvariable=self.monthlypaymentVar,font=('Arial,15,bold'),bg='#a39ea0').place(x=220,y=150) # добавляем строку вывода расчёта общей суммы выплаты self.totalpaymentVar=StringVar() Label(root, textvariable=self.totalpaymentVar,font=('Arial,15,bold'),bg='#a39ea0').place(x=220,y=190) # добавляем кнопку, задаём её расположение, надпись и стиль шрифта Button(root, text="Рассчитать",font=('Arial,15,bold'),command=self.calculateloan).place(x=180,y=240) # запускаем окно root.mainloop() # определяем функцию расчёта общей суммы выплаты def calculateloan(self): # определяем формулу, по которой будет рассчитываться ежемесячный платёж по кредиту monthlypayment=self.getmonthlypayment (float(self.loanamountVar.get()),float(self.annualinterestVar.get()) / 1200, int(self.numberofyearsVar.get())) self.monthlypaymentVar.set(format(monthlypayment, '10.2f')) # определяем формулу, по которой будет рассчитываться общая сумма выплаты по кредиту totalpayment=float(self.monthlypaymentVar.get()) * 12 * int(self.numberofyearsVar.get()) self.totalpaymentVar.set(format(totalpayment, '10.2f')) # определяем функцию расчёта ежемесячного платежа def getmonthlyPayment(self,loanamount,monthlyinterestrate,numberofyears): # определяем формулу, по которой будет рассчитываться ежемесячный платёж monthlypayment=loanamount * monthlyinterestrate / (1-1 / (1 + monthlyinterestrate) ** (numberofyears * 12)) return monthlypayment # вызываем класс калькулятора, чтобы запустить программу LoanCalculator()

Как правильно написать калькулятор на питоне с помощью eval()

В комментариях к статьям по синтаксическому анализу я иногда вижу такие:

на питоне калькулятор пишется проще простого — print(eval(input()))

Ну, вобщем‑то — да, но если, например, вы прикрутите такой калькулятор к своему сайту, то любой желающий вместо 2+2*2 может написать exec(«import os; os.removedirs(‘/’)») , предварительно изучив все ваши секретные файлы подобным же образом. Такая перспектива не может радовать, но и отказываться от eval() тоже не стоит.

— А что делать‑то? — спросите вы. Ответ простой: валидировать входящие данные, как вы это всегда делаете. Только не те, которые передаются функции eval() — для этого вам действительно пришлось бы написать свой синтаксический анализатор, а внутренние данные, которыми оперирует реализация eval() .

— А! Я знаю — скажете вы — используем compile() , валидируем код и тогда уже вызываем eval() . Что-ж, возможно и так. Но я не знаю простого способа валидировать байт‑код. Нет. Нам надо валидировать результат питоновского синтаксического анализатора — абстрактное синтаксическое дерево или AST. Это гораздо проще, хоть и звучит пугающе.

Напишем свой eval() так:

def my_eval(expression): tree = ast.parse(expression, mode='eval') code = compile(tree, filename='', mode='eval') return eval(code)

Нам, естественнно, понадобится модуль ast:

>>> import ast

Проверяем — работает ли?

>>> print(my_eval(input())) 2+2*2 6

Итак, у нас есть tree . Вам, конечно же, любопытно, что там внутри, и действительно, надо же знать, что мы собираемся валидировать. Но просто так посмотреть не получится — это объект, и print() или pprint() выдадут всего лишь Я пользовался такой функцией:

def dump(node): def _format(node, indent): if isinstance(node, ast.AST): print('%sAST %s' % (' ' * indent, node.__class__.__name__)) for a, b in ast.iter_fields(node): print('%s%s' % (' ' * indent, a)) _format(b, indent + 4) elif isinstance(node, list): print('%sLIST %s' % (' ' * indent, node.__class__.__name__)) for x in node: _format(x, indent) else: print('%s%s' % (' ' * indent, repr(node))) _format(node, 0)

Можете вставить dump(tree) в my_eval(), поиграться и посмотреть, какое оно — это дерево. Только не надо копипастить мой пример с removedirs() .

Кому играться лениво — вот пара примеров:

>>> print(my_eval(input())) 2+2*2 AST Expression body AST BinOp left AST Constant value 2 kind None op AST Add right AST BinOp left AST Constant value 2 kind None op AST Mult right AST Constant value 2 kind None 6
>>> print(my_eval(input())) print(globals()) AST Expression body AST Call func AST Name id 'print' ctx AST Load args LIST list AST Call func AST Name id 'globals' ctx AST Load args LIST list keywords LIST list keywords LIST list , '__spec__': None, '__annotations__': <>, '__builtins__': , 'my_eval': , 'ast': , 'dump': > None

Видите, чем отличаются безопасные выражения от небезопасных? Безопасные содержат только Constant и BinOp , а небезопасные — всякие Call и Name . Полный список операций и других узлов дерева можно посмотреть в документации к модулю ast. Это поможет определиться, что запретить, а что разрешить. Для простого калькулятора, я считаю, достаточно разрешить основные операции BinOp и UnaryOp . Плюс Constant .

Пора писать валидатор:

_allowed_nodes = ( # базовые узлы: ast.BinOp, ast.UnaryOp, ast.Constant, # основные BinOps: ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, # основные UnaryOps: ast.UAdd, ast.USub ) def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree)

Вот так всё просто. Смело вызывайте эту функцию из my_eval() перед compile() :

def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') return eval(code)

и будет вам счастье. Но, имейте ввиду, документация к функции compile() сообщает:

Warning

It is possible to crash the Python interpreter with a sufficiently large/complex string when compiling to an AST object due to stack depth limitations in Python’s AST compiler.

Так что ограничивайте длину строки, передаваемой my_eval() . И тогда счастье будет настоящим.

— Постойте! — скажете вы — Мне не нужен простой бухгалтерский калькулятор! Где хоть какой-нибудь минимальный набор математических функций? Не отправлять же пользователя в пешее путешествие искать косинус фи?

М-да, пожалуй, соглашусь с вами. Давайте разрешим кое-что из модуля math . Но это будут вызовы функций, а значит — небезопасные области тьмы. Нам придётся добавить ast.Call , ast.Name и ast.Load в _allowed_nodes , и это открывает путь к exec() . Чтобы избежать неприятностей, надо ограничить контекст выполнения, в котором важно наличие пустого __builtins__ :

def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') context = > return eval(code, context)

Плюс, надо добавить поддержку узлов-списков в валидатор:

def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if isinstance(child, list): for grandchild in child: validate_children(grandchild) else: if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree)

Теперь, если мы введём

NameError: name ‘exec’ is not defined

Ура! Ничего лучшего я, конечно, придумать не могу, но надеюсь что те, кто умнее меня, напишут в комментариях как это взломать и, соответственно, защититься.

Итак, функции у нас разрешены, надо добавить нужные в контекст выполнения. Я ограничусь синусом и косинусом, остальное вы и сами сможете. По своему вкусу.

import math def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') context = < '__builtins__': <>, 'sin': math.sin, 'cos': math.cos > return eval(code, context)

Может показаться, что достаточно создать контекст всего один раз, вне функции, как мы это сделали с _allowed_nodes в валидаторе, но! — _allowed_nodes у нас всё-таки immutable, а для контекста есть риск модификации, случайной или преднамеренной. Известные аналогичные грабли — использование dict и list в качестве значений по умолчанию для аргументов. Поэтому лучше создавать контекст каждый раз, внутри функции.

Ну, вот собственно, и всё. Пользуйтесь и наслаждайтесь. Для тех, кому лень собирать копипастой всё в кучу, ловите полный исходник:

import ast import math def my_eval(expression): tree = ast.parse(expression, mode='eval') #dump(tree) validate_ast(tree) code = compile(tree, filename='', mode='eval') context = < '__builtins__': <>, 'sin': math.sin, 'cos': math.cos > return eval(code, context) _allowed_nodes = ( # базовые узлы: ast.BinOp, ast.UnaryOp, ast.Constant, ast.Call, ast.Name, ast.Load, # основные BinOps: ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, # основные UnaryOps: ast.UAdd, ast.USub ) def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if isinstance(child, list): for grandchild in child: validate_children(grandchild) else: if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree) def dump(node): def _format(node, indent): if isinstance(node, ast.AST): print('%sAST %s' % (' ' * indent, node.__class__.__name__)) for a, b in ast.iter_fields(node): print('%s%s' % (' ' * indent, a)) _format(b, indent + 4) elif isinstance(node, list): print('%sLIST %s' % (' ' * indent, node.__class__.__name__)) for x in node: _format(x, indent) else: print('%s%s' % (' ' * indent, repr(node))) _format(node, 0) print(my_eval(input()))
  • Python
  • Программирование

Программа «Простейший калькулятор»

Написать программу, которая выполняет над двумя вещественными числами одну из четырех арифметических операций (сложение, вычитание, умножение или деление). Программа должна завершаться только по желанию пользователя.

Решение задачи на языке программирования Python

Чтобы программа самопроизвольно не завершалась, в ней надо запустить бесконечный цикл. Выход из него будем осуществлять с помощью оператора break , если пользователь вводит определенный символ вместо знака арифметической операции.

Если пользователь ввел знак, который не является ни знаком арифметической операции, ни символом-«прерывателем» работы программы, то вывести сообщение о некорректном вводе.

Если был введен один из четырех знаков операции, запросить ввод двух чисел.

В зависимости от знака операции выполнить соответствующее арифметическое действие.

Если было выбрано деление, необходимо проверить не является ли нулем второе число. Если это так, то сообщить о невозможности деления.

print("0 в качестве знака операции" "\nзавершит работу программы\n") while True: s = input("Знак (+, -, *, /): ") if s == '0': break if s in ('+', '-', '*', '/'): a = float(input("a = ")) b = float(input("b = ")) if s == '+': print("%.2f" % (a + b)) elif s == '-': print("%.2f" % (a - b)) elif s == '*': print("%.2f" % (a * b)) elif s == '/': if b != 0: print("%.2f" % (a / b)) else: print("Деление на ноль!") else: print("Неверный знак операции!")

Пример выполнения программы:

0 в качестве знака операции завершит работу программы Знак (+, -, *, /): / a = -9.34 b = 3.215 -2.91 Знак (+, -, *, /): & Неверный знак операции! Знак (+, -, *, /): - a = 4005 b = 1358 2647.00 Знак (+, -, *, /): 0

Вариант решение задачи с помощью оператора match (появился в Python 3.10):

print("0 в качестве знака - выход из программы\n") while True: s = input("Знак (+, -, *, /): ") if s in ('+', '-', '*', '/'): a = float(input("a = ")) b = float(input("b = ")) match s: case '+': print("%.2f" % (a + b)) case '-': print("%.2f" % (a - b)) case '*': print("%.2f" % (a * b)) case '/': if b != 0: print("%.2f" % (a / b)) else: print("Деление на ноль!") case '0': break case _: print("Неверный знак операции!")

X Скрыть Наверх

Решение задач на Python

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *