Last active
December 21, 2019 10:25
-
-
Save AndreyeuIvan/1c8e1aec053a6d609f6939ba6167a732 to your computer and use it in GitHub Desktop.
Calculator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
from operations import Operation | |
class UnknownOperation(NotImplementedError): pass | |
class UnknownError(Exception): pass | |
class Calculator(object): | |
REGEXP_ALL = r"((\d{1,20}(\.|\,)\d{1,20})|(\d{1,20})|(\+|\-|\*|\^|\#|\/|\(|\)|\.))" | |
def __init__(self, *args, **kwargs): | |
self.OPERATIONS = Operation.get_operations() | |
self.start() | |
def get_data(self): | |
""" | |
This function for getting data from user | |
return: list of elements | |
""" | |
input_str = input("Введите операцию: ") | |
matches = re.finditer(self.REGEXP_ALL, input_str, re.MULTILINE) | |
return [match.group() for match in matches] | |
def check_data(self, elements): # проверяет исключения, все, чтобы не было ошибок | |
if elements[0] in self.OPERATIONS or elements[-1] in self.OPERATIONS: # проверяем | |
raise UnknownError | |
for element in elements: | |
if isinstance(element, str) and element not in self.OPERATIONS: | |
raise UnknownOperation(element) | |
def compress(self, elements, index): | |
first = elements[index-1] | |
second = elements[index+1] | |
cls = self.OPERATIONS.get(elements[index], None) | |
operation = cls() | |
left_finish = 0 if index == 1 else index - 1 | |
right_first = len(elements) if index == len(elements) - 2 else index + 2 | |
return elements[:left_finish] + [operation.calculate(first, second)] + elements[right_first:] | |
def get_priority(self, elements): | |
result = [] | |
for index, element in enumerate(elements): | |
if not isinstance(element, (int, float)): | |
result += [(index, self.OPERATIONS.get(element).priority)] | |
import pdb; pdb.set_trace() | |
return sorted(result, key=lambda k: k[1]) | |
def calc(self, elements): | |
for index, element in enumerate(elements): | |
try: | |
element = float(element.replace(',', '.')) | |
except ValueError: | |
pass | |
elements[index] = element | |
self.check_data(elements) | |
print(elements) | |
while elements.count(")") > 0: | |
end = elements.index(")") | |
start = (end - 1) - list(reversed(elements[:end])).index("(") | |
cutted = elements[start + 1:end] | |
while len(cutted) > 1: | |
priority = self.get_priority(cutted) | |
cutted = self.compress(cutted, priority[0][0]) | |
elements[start:end + 1] = cutted | |
print(elements) | |
while len(elements) > 1: | |
priority = self.get_priority(elements) | |
print(priority) | |
elements = self.compress(elements, priority[0][0]) | |
print(elements) | |
return elements[0] | |
def start(self): | |
print("Для выхода нажмите <Ctrl> - C") | |
while True: | |
try: | |
elements = self.get_data() | |
except KeyboardInterrupt: | |
print("Завершение программы") | |
break | |
try: | |
result = self.calc(elements) | |
print(result) | |
except ZeroDivisionError: | |
print("На ноль делить нельзя!") | |
except UnknownOperation as err: | |
print('UnknownOperation', err) # определяем какая неизвестная операция | |
except UnknownError: | |
print('UnknownError!') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from calc import Calculator, UnknownOperation, UnknownError | |
INPUT_FILENAME = "input.txt" | |
OUTPUT_FILENAME = "output.txt" | |
def get_data(): | |
with open(INPUT_FILENAME) as file: | |
data = file.readlines() | |
data = [x.strip() for x in data] | |
return data | |
def write_data(output_data): | |
output_data = [str(x)+"\n" for x in output_data] | |
print(output_data) | |
with open(OUTPUT_FILENAME, "w") as out_file: | |
out_file.writelines(output_data) | |
if __name__ == "__main__": | |
calc = Calculator(start_auto=False) | |
data = get_data() | |
results = [] | |
for line in data: | |
elements = calc.prepare_data(line) | |
try: | |
result = calc.calc(elements) | |
results.append(result) | |
except ZeroDivisionError: | |
results.append("На ноль делить нельзя!") | |
except UnknownOperation as err: | |
results.append('UnknownOperation') # определяем какая неизвестная операция | |
except UnknownError: | |
results.append('UnknownError!') | |
write_data(results) | |
print(results) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from tkinter import Tk, Frame, Label, Button | |
from calc import Calculator, UnknownOperation, UnknownError | |
class Main(Frame): | |
formula = "0" | |
def __init__(self, root): | |
super().__init__(root) | |
self.build() | |
def build(self): | |
self.calc = Calculator(start_auto=False) | |
self.lbl_err = Label( | |
text="", | |
font=("Times New Roman", 21, "bold"), | |
bg="#000", foreground="#FF0" | |
) | |
self.lbl_err.place(x=11, y=5) | |
self.lbl = Label( | |
text=self.formula, | |
font=("Times New Roman", 21, "bold"), | |
bg="#000", foreground="#FFF" | |
) | |
self.lbl.place(x=11, y=50) | |
btns = [ | |
"C", "DEL", "*", "=", | |
"1", "2", "3", "/", | |
"4", "5", "6", "+", | |
"7", "8", "9", "-", | |
"(", "0", ")", "X^2", | |
] | |
x = 10 | |
y = 100 | |
for btn in btns: | |
command = lambda x=btn: self.command(x) | |
Button( | |
text=btn, bg="#FFF", | |
font=("Times New Roman", 15), command=command | |
).place( | |
x=x, y=y, width=115, height=79 | |
) | |
x += 117 | |
if x > 400: | |
x = 10 | |
y += 81 | |
def get_result(self, string): | |
result = "0" | |
error = "" | |
elements = self.calc.prepare_data(string) | |
try: | |
result = self.calc.calc(elements) | |
except ZeroDivisionError: | |
error = "На ноль делить нельзя!" | |
except UnknownOperation as err: | |
error = 'UnknownOperation' | |
except UnknownError: | |
error = 'UnknownError!' | |
return str(result), str(error) | |
def command(self, symbol): | |
need_update = True | |
if symbol == "C": | |
self.formula = "0" | |
elif symbol == "DEL": | |
self.formula = self.formula[0:-1] | |
elif symbol == "=": | |
result, error = self.get_result(self.formula) | |
if error: | |
self.lbl_err.configure(text=error) | |
need_update = False | |
else: | |
self.formula = result | |
elif symbol == "X^2": | |
error = "" | |
try: | |
float(self.formula) | |
except ValueError: | |
result1, error = self.get_result(self.formula) | |
try: | |
if error: | |
raise ValueError(error) | |
result, error = self.get_result(result1 + '^2') | |
if error: | |
raise ValueError(error) | |
self.formula = result | |
except ValueError as error: | |
self.lbl_err.configure(text=str(error)) | |
need_update = False | |
else: | |
if self.formula == "0": | |
self.formula = "" | |
self.formula += symbol | |
if need_update: | |
self.update() | |
def update(self): | |
if self.formula == "": | |
self.formula = "0" | |
self.lbl.configure(text=self.formula) | |
self.lbl_err.configure(text="") | |
if __name__ == "__main__": | |
root = Tk() | |
root["bg"] = "#000" | |
root.title("Калькулятор") | |
root.geometry("485x550+200+200") | |
root.resizable(False, False) | |
app = Main(root) | |
app.pack() | |
root.mainloop() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from calc import Calculator | |
calc = Calculator(start_auto=False) | |
calc.start() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SUM = "+" | |
DEC = "-" | |
MUL = "*" | |
DIV = "/" | |
EXP = "^" | |
ROT = "#" | |
BR1 = "(" | |
BR2 = ")" | |
END = "." | |
class Operation(object): | |
__priority = 0 | |
__identificator = None | |
def calculate(self, *args, **kwargs): | |
raise NotImplementedError | |
@classmethod | |
def get_operations(cls): | |
return { | |
SUM: SumOperation, | |
DEC: DecOperation, | |
MUL: MultiplicationOperation, | |
DIV: DivisionOperation, | |
EXP: ExponentialOperation, | |
ROT: RootOfOperation, | |
BR1: BracketOperation, | |
BR2: BracketOperation, | |
} | |
@classmethod | |
def priority(cls): | |
return cls.__priority | |
class SumOperation(Operation): | |
__priority = 3 | |
__identificator = [SUM] | |
def calculate(self, *args, **kwargs): | |
return sum(args) | |
class DecOperation(Operation): | |
__priority = 3 | |
__identificator = [DEC] | |
def calculate(self, *args, **kwargs): | |
result = args[0] | |
for arg in args[1:]: | |
result -= arg | |
return result | |
class MultiplicationOperation(Operation): | |
__priority = 2 | |
__identificator = [MUL] | |
def calculate(self, *args, **kwargs): | |
result = args[0] | |
for arg in args[1:]: | |
result *= arg | |
return result | |
class DivisionOperation(Operation): | |
__priority = 2 | |
__identificator = [DIV] | |
def calculate(self, *args, **kwargs): | |
result = args[0] | |
for arg in args[1:]: | |
result /= arg | |
return result | |
class ExponentialOperation(Operation): | |
__priority = 1 | |
__identificator = [EXP] | |
def calculate(self, a, n): | |
return a ** n | |
class RootOfOperation(Operation): | |
__priority = 1 | |
__identificator = [ROT] | |
def calculate(self, a, n): | |
return a ** (1 / n) | |
class BracketOperation(Operation): | |
__priority = 1 | |
__identificator = [BR1, BR2] | |
def calculate(self, *args, **kwargs): | |
return | |
if __name__ == "__main__": | |
import traceback | |
TEST_CASES = [ | |
{"cls": SumOperation, "args": [1, 2, 3], "expected_result": 6}, | |
{"cls": SumOperation, "args": [2, 3], "expected_result": 5}, | |
{"cls": DecOperation, "args": [1, 2, 3], "expected_result": -4}, | |
{"cls": MultiplicationOperation, "args": [8, 8, 2, 2], "expected_result": 256}, | |
{"cls": DivisionOperation, "args": [16, 4, 2, 1], "expected_result": 2}, | |
{"cls": ExponentialOperation, "args": [8, 2], "expected_result": 64}, | |
{"cls": RootOfOperation, "args": [16, 2], "expected_result": 4}, | |
{"cls": BracketOperation, "args": [16, 2], "expected_result": None}, | |
] | |
green = 0 | |
red = 0 | |
for test_case in TEST_CASES: | |
cls = test_case["cls"] | |
op = cls() | |
expected_result = test_case["expected_result"] | |
result = op.calculate(*test_case["args"]) | |
try: | |
assert result == expected_result, f"calculate returns: {result}, expected: {expected_result}, operation is {cls.__name__}" | |
green += 1 | |
except AssertionError: | |
red += 1 | |
print(traceback.format_exc()) | |
all = green + red | |
print(f"Run {all} tests, {green} passed, {red} failed") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Тесткейсы
Работа с Файлами
@classmethod
Экземляр класса и класс (чертеж дома и дом)
Пишите свой код