Skip to content

Hanna Kovalenok #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions final_task/pycalc/CheckAndChange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import re
import pycalc.operators as operators
import pycalc.difcalc as difcalc
from numbers import Number
import importlib.util
from os import path
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Порядок объявления импортов описан в PEP8



class CheckAndChange():

def do_all_changes(self, expr, module):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для читаемости можно добавлять аннотации функций/методов

self.add_args(module)

if not re.search(r'[0-9]+', expr):
const = re.search(r'[A-ZAa-z]+', expr)
if not const:
raise Exception("No Numbers in expression")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Создание кастомных эксепшенов тоже улучшает читаемость кода

else:
if const[0] not in difcalc.ComplexCalc.const and const[0] not in difcalc.ComplexCalc.math_functions:
raise Exception("Check your const or function")

expr = expr.replace("//", "&")
self.correct_brackets(expr)
self.correct_spaces(expr)
expr = expr.replace(" ", "")

return expr

def add_args(self, modul):
if modul:
base = path.basename(modul)

module_name = path.splitext(base)[0]
spec = importlib.util.spec_from_file_location(module_name, modul)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

new_functions = {
attr: getattr(module, attr) for attr in dir(module) if callable(getattr(module, attr))
}
difcalc.ComplexCalc.math_functions.update(new_functions)

new_const = {
attr: getattr(module, attr) for attr in dir(module)if isinstance(getattr(module, attr), Number)
}
difcalc.ComplexCalc.const.update(new_const)

def correct_spaces(self, expr):
searcher = expr.find(" ")
expression = expr

while searcher != -1 and expression != "":
if searcher != len(expression) - 1 and searcher != 0:
if expression[searcher -
1].isdigit() and expression[searcher + 1].isdigit():
raise Exception("must not be 'digit' 'space' 'digit'")

if expression[searcher - 1] in operators.operators \
and expression[searcher + 1] in operators.operators:
raise Exception(
"must not be 'operator' 'space' 'operator'")

if expression[searcher - 1] in difcalc.ComplexCalc.compare \
and expression[searcher + 1] in difcalc.ComplexCalc.compare:
raise Exception("Check your spaces betwin")

expression = expression[searcher + 1:]
searcher = expression.find(" ")
else:
if searcher == len(expression) - 1:
break
if searcher == 0:
expression = expression[1:]

def correct_brackets(self, expr):
counter = 0
for one in expr:

if one == "(":
counter += 1
elif one == ")":
counter -= 1
if counter < 0:
raise Exception("check brackets! ")
else:
if counter != 0:
raise Exception("check brackets! ")
File renamed without changes.
151 changes: 151 additions & 0 deletions final_task/pycalc/difcalc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import re
import pycalc.easyCalculation as easyCalculation
import math
from numbers import Number


class ComplexCalc(easyCalculation.Calculator):
"""more functional calculator than "Calculator" """

def __init__(self):
"""initializing data from "math" """
ComplexCalc.const = {
**{attr: getattr(math, attr) for attr in dir(math) if isinstance(getattr(math, attr), Number)},
**{"True": 1, "False": 0}
}

ComplexCalc.math_functions = {
**{attr: getattr(math, attr) for attr in dir(math) if callable(getattr(math, attr))},
**{"abs": lambda a: abs(a),
"round": lambda a: round(a),
"pow": lambda a, b: pow(a, b)}
}

def expression_search(self, expr):
"""search function or constant and calc power if function negative"""

while True:

func = re.search(r'[A-ZAa-z]+1?0?', expr)

if func is None:
return self.search_braсkets(expr)

afterExpr = func.end()
place = func.start()
if func[0] in self.const:

rezult = self.const[func[0]]
expr = expr[:place] + str(rezult) + expr[afterExpr:]
continue

searcher = 0
count = 1
for one in expr[afterExpr + 1:]:

searcher += 1
if one == ")":
count -= 1
if one == "(":
count += 1
if count == 0:
break
end = searcher + afterExpr
# выкинуть если конец строки
if expr[afterExpr] != '(':

raise Exception(
"the expression must be written in the following way 'function(expression)'")

else:

rezult = self._find_replacement(
func[0], expr[afterExpr + 1:end])
expr = expr[:place] + rezult + expr[end + 1:]
if float(rezult) < 0:
end = place + len(rezult) - 1
expr = self._calc_if_power(expr, place, end)

def _find_replacement(self, func, expr):
"""count all arguments from function"""
if func in ComplexCalc.math_functions:
allargs = self._commasplit(expr)

float_args = []
for each in allargs:
float_args.append(float(self.expression_search(each)))

rezult = '{:.15f}'.format(
ComplexCalc.math_functions[func](
*float_args))

else:

raise Exception("Indefined function")
return str(rezult)

def _commasplit(self, expr: str):
"""search arguments for function"""
breketscounter = 0
preve = 0
count = 1
split = []
for each in expr:
if breketscounter == 0 and each == ",":
split.append(expr[preve:count - 1])
preve = count

elif each == "(":
breketscounter += 1
elif each == ")":
breketscounter -= 1
count += 1

split.append(expr[preve:count])

return split
compare = {

">": lambda a, b: a > b,
">=": lambda a, b: a >= b,
"<=": lambda a, b: a <= b,
"==": lambda a, b: a == b,
"<": lambda a, b: a < b,
"!=": lambda a, b: a != b

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачем такое количество пустых строк?


}

def calculate(self, expr):
"""begin of calculatoion, search compare"""
place = re.search(r'(>=)|(>)|(<=)|(<)|(!=)|(==)', expr)

while place:
after = re.search(r'(>=)|(>)|(<=)|(<)|(!=)|(==)',
expr[place.end():])
number_one = self.expression_search(expr[:place.start()])

if not after:
number_two = self.expression_search(expr[place.end():])
else:
number_two = self.expression_search(
expr[place.end():after.start() + place.end()])

if number_one is not None and number_two is not None:
rezult = ComplexCalc.compare[place[0]](number_one, number_two)
end = ""

if after:
if after.start() == 0:
raise Exception("no symbols between compare")
end = expr[after.end() + place.end():]
expr = str(rezult) + after[0] + end
else:
return rezult

else:
raise Exception(
"uncorrect expression must be 'expr' operator 'expr'")
place = re.search(r'(>=)|(>)|(<=)|(<)|(!=)|(==)', expr)

return self.expression_search(expr)
140 changes: 140 additions & 0 deletions final_task/pycalc/easyCalculation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import re
import pycalc.operators as operators


class Calculator():
"""calculator for counting simple expression"""

def _calculation(self, expr):
"""find all binary operation, "^" work in a different way """

place = expr.rfind("^")

while place != -1:
findBefore = self.search_simple_number(expr[:place])
begin = place - findBefore.end()
findAfter = self.search_number_from_begin(expr[place:])
end = place + findAfter.end()
expr = self.__binary_operation(expr, begin, place, end)

place = expr.rfind("^")

place = re.search(r'/|\*|%|&', expr)

while place:
point = place.start()
findBefore = self.search_number_from_end(expr[:point])
begin = point - findBefore.end() + 1
findAfter = self.search_number_from_begin(expr[point:])
end = point + findAfter.end()
expr = self.__binary_operation(expr, begin, point, end)
place = re.search(r'/|\*|%|&', expr)

return self.sum(expr)

def sum(self, expr):
"""sum all elements as elements with binary minus"""

if expr[-1] == "+" or expr[-1] == "-":
raise Exception(
"'+' or '-' and bla-bla-blah mustn' be the last even in brackets")

summing = 0
number = 0
while expr != "":
find = re.search(r'([+-]+)?([0-9]+([.][0-9]*)?|[.][0-9]+)', expr)
if find.start() != 0:
raise Exception("Undefine operator")
number = find[0]

expr = expr[find.end():]
a = self.unary_rezult(number)
summing += a
return summing

def unary_rezult(self, number):
"""just coubt all minuses and define symbol of number"""
minus = number.count("-")
plus = number.count("+")
real_number = number[plus + minus:]
if minus % 2 == 1:
real_number = float("-" + real_number)
else:
real_number = float(real_number)
return real_number

def search_braсkets(self, expr):
"""search lowest bracket's level"""
expr = expr.replace(" ", "")

while "(" in expr:

end = expr.find(")")
if end != len(expr) - 1\
and expr[end + 1] not in operators.operators:
Exception("no operator after brackets")

begin = expr[:end].rfind("(")
if begin + 1 == end:
raise Exception("no Number in brakets")

if begin != 0 and expr[begin - 1] not in operators.operators \
and expr[begin - 1] != '-' and expr[begin - 1] != '+':
raise Exception("no operators before brackets")

rezult = self._calculation("+" + expr[begin + 1:end])
expr = expr[:begin] + str(rezult) + expr[end + 1:]

if rezult < 0:
end = begin + len(str(rezult)) - 1
expr = self._calc_if_power(expr, begin, end)

rezult = self._calculation("+" + expr)

return rezult

def _calc_if_power(self, expr, begin, braket):
"""power doesn't know about unary minus, therefore it's calculate separately"""
place = braket + 1
if braket is not len(expr) - 1 and expr[place] is "^":
findAfter = self.search_number_from_begin(expr[place:])
end = place + findAfter.end()
expr = self.__binary_operation(expr, begin, place, end)
return expr

def __binary_operation(self, expr, begin, place, end):
"""calculate binary operation"""
rezult = '{:.15f}'.format(operators.operators[expr[place]](
self.unary_rezult(expr[begin:place]), self.unary_rezult(expr[place + 1:end])))

before = expr[:begin]
after = expr[end:]
expr = before + rezult + after
return expr

def search_number_from_begin(self, expr):
"""searching number after operatior"""
number = re.search(
r'([+-]+)?([0-9]+([.][0-9]*)?|[.][0-9]+)', expr)

if not number or number.start() != 1:
raise Exception(
"the expression should be written in the following form 'number operator number'")

return number

def search_number_from_end(self, expr):
"""searching number before operator"""
number = re.search(
r'([0-9]+([.][0-9]*)?|[.][0-9]+)([+-]+)?', expr[::-1])

if not number or number.start() != 0:
raise Exception(
"the expression should be written in the following form 'number operator number'")

return number

def search_simple_number(self, expr):
"""search number before power"""
number = re.search(r'([0-9]+([.][0-9]*)?|[.][0-9]+)', expr[::-1])
return number
10 changes: 10 additions & 0 deletions final_task/pycalc/operators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import operator


operators = {
"^": operator.pow,
"*": operator.mul,
"/": operator.truediv,
"%": operator.mod,
"&": operator.floordiv,
}
Loading