Ed
Если есть желание разделять UI и логику, то по-моему лучше это делать не путем наследования класса с логикой от класса с UI. Даже с точки зрения здравого смысла это, кхм, вызывает вопросы. Попробуйте вместо наследования просто передавать объект UI в качестве параметра в конструктор вашего калькулятора.
Soteric
Здесь не требуется наследование
Переделал. Правильно?
#! /usr/bin/python
'''Calculator in object oriented style.
For start GUI or Console use the run method of the object.
Install Tkinter for use GUI.
'''
from functools import partial as send
try:
from Tkinter import \
Tk, Frame, Entry, Button, \
END, TOP, LEFT, RIGHT
except ImportError:
print 'For GUI Tkinter needed'
class Logic:
def __init__(self):
self.result = ''
self.buffer = ''
self.binary_operation = ''
self.first_number_string = ''
self.second_number_string = ''
self.negative_number = False
operation = [('.', self.dot), ('=', self.calculate), ('c', self.reset)]
operation += [(command, send(self.operation, command))
for command in '+-*/']
numbers = [(str(number), send(self.combination, str(number)))
for number in range(10)]
self.switch = dict(numbers + operation)
def write(self, char):
if char in self.switch:
self.switch[char]()
def combination(self, digit):
'''Combination the number from digits'''
if self.negative_number:
# This number is negative
digit = '-' + digit
self.negative_number = False
if self.result:
# Old result is not needed
self.result = ''
self.first_number_string += digit
self.buffer = self.first_number_string
def operation(self, char):
'''Arithmetic operations'''
if self.result:
# Use the result of the previous calculation
self.first_number_string = self.result
self.result = ''
if self.first_number_string:
if self.second_number_string:
# Continue to enter numbers
self.calculate()
self.first_number_string = self.result
self.result = ''
self.second_number_string = self.first_number_string
self.first_number_string = ''
self.binary_operation = char
self.buffer = self.binary_operation
elif char == '-':
# We remember that the number is less than zero
self.negative_number = True
self.buffer = '-'
def calculate(self):
if self.first_number_string \
and self.second_number_string and self.binary_operation:
if '.' in self.second_number_string:
number = float(self.second_number_string)
else:
number = int(self.second_number_string)
if '.' in self.first_number_string:
number2 = float(self.first_number_string)
else:
number2 = int(self.first_number_string)
if self.binary_operation == '+':
result = number + number2
elif self.binary_operation == '-':
result = number - number2
elif self.binary_operation == '/':
try:
result = float(number) / number2
except ZeroDivisionError:
result = 'ERROR division by zero'
elif self.binary_operation == '*':
result = number * number2
self.first_number_string = ''
self.second_number_string = ''
self.binary_operation = ''
# Save result
self.result = str(result)
self.buffer = self.result
if self.result == 'ERROR division by zero':
self.result = ''
def reset(self):
self.result = ''
self.binary_operation = ''
self.first_number_string = ''
self.second_number_string = ''
self.negative_number = False
self.buffer = '0'
def dot(self):
if self.first_number_string == '':
# Forgot to enter a zero before '.'
self.combination('0.')
elif '.' in self.first_number_string:
# Repeated, incorrect entry '.'
pass
else:
self.combination('.')
def get_buffer(self):
'''The result is the last action'''
return self.buffer
class GUI:
def __init__(self, title='My calc',
width_root=160, height_root=170,
width_number_button=1, width_math_button=2):
self.logic = Logic()
self.root = Tk()
self.root.title(title)
self.root.maxsize(width = width_root, height = height_root)
self.root.minsize(width = width_root, height = height_root)
frame = Frame(self.root)
frame_entry = Frame(self.root)
frame_math = Frame(self.root)
frame_numbers = Frame(frame)
frame_numbers2 = Frame(frame)
frame_numbers3 = Frame(frame)
frame_numbers4 = Frame(frame)
self.entry = Entry(frame_entry)
self.entry.insert(END, '0')
self.entry.pack(side = TOP)
frame_entry.pack(side = TOP)
frame.pack(side = LEFT)
frame_math.pack(side = RIGHT)
frame_numbers.pack(side = TOP)
frame_numbers2.pack(side = TOP)
frame_numbers3.pack(side = TOP)
frame_numbers4.pack(side = TOP)
Button(frame_entry, text = 'C', width = width_math_button,
command = send(self.write, 'c')).pack(side = RIGHT)
for char in '789':
Button(frame_numbers, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '456':
Button(frame_numbers2, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '123':
Button(frame_numbers3, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '0.=':
Button(frame_numbers4, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '+-/*':
Button(frame_math, text = char, width = width_math_button,
command = send(self.write, char)).pack(side = TOP)
def show(self, value):
self.entry.delete(first = 0, last = END)
self.entry.insert(END, value)
def write(self, char):
self.logic.write(char)
self.show(self.logic.get_buffer())
def run(self):
'''Run a GUI calculator'''
self.root.mainloop()
class Console:
def __init__(self):
self.logic = Logic()
def run(self):
'''Run a console calculator'''
print '[c] reset, [q] quit, [=] result'
while True:
for char in raw_input('calc>'):
if char == 'q':
raise SystemExit('Quit')
elif char == '=':
self.write(char)
print self.logic.get_buffer()
else:
self.write(char)
def write(self, char):
self.logic.write(char)
if __name__ == '__main__':
if 'g' in raw_input('GUI or console? ([g] or [c])?\n'):
GUI().run()
else:
Console().run()
Если я все правильно понял - это называется композиция. Но особых преимуществ перед наследованием не вижу. Как в первом, так и во втором варианте есть повторное использование кода класса Logic, классы вызывают только методы. В чем суть?
Ed
лямбду можно заменить на partial из functools
Чем partial лучше лямбды?
Ed
В сторону MVC смотрели?
Нет, никогда не сталкивался. Правильно ли я понял шаблон MVC?
#! /usr/bin/python
'''Calculator in object oriented style.
For start GUI or Console use the run method of the object.
Install Tkinter for use GUI.
'''
from functools import partial as send
try:
from Tkinter import \
Tk, Frame, Entry, Button, \
END, TOP, LEFT, RIGHT
except ImportError:
print 'For GUI Tkinter needed'
class Logic:
def __init__(self):
self.result = ''
self.buffer = ''
self.binary_operation = ''
self.first_number_string = ''
self.second_number_string = ''
self.negative_number = False
operation = [('.', self.dot), ('=', self.calculate), ('c', self.reset)]
operation += [(command, send(self.operation, command))
for command in '+-*/']
numbers = [(str(number), send(self.combination, str(number)))
for number in range(10)]
self.switch = dict(numbers + operation)
def write(self, char):
if char in self.switch:
self.switch[char]()
def combination(self, digit):
'''Combination the number from digits'''
if self.negative_number:
# This number is negative
digit = '-' + digit
self.negative_number = False
if self.result:
# Old result is not needed
self.result = ''
self.first_number_string += digit
self.buffer = self.first_number_string
def operation(self, char):
'''Arithmetic operations'''
if self.result:
# Use the result of the previous calculation
self.first_number_string = self.result
self.result = ''
if self.first_number_string:
if self.second_number_string:
# Continue to enter numbers
self.calculate()
self.first_number_string = self.result
self.result = ''
self.second_number_string = self.first_number_string
self.first_number_string = ''
self.binary_operation = char
self.buffer = self.binary_operation
elif char == '-':
# We remember that the number is less than zero
self.negative_number = True
self.buffer = '-'
def calculate(self):
if self.first_number_string \
and self.second_number_string and self.binary_operation:
if '.' in self.second_number_string:
number = float(self.second_number_string)
else:
number = int(self.second_number_string)
if '.' in self.first_number_string:
number2 = float(self.first_number_string)
else:
number2 = int(self.first_number_string)
if self.binary_operation == '+':
result = number + number2
elif self.binary_operation == '-':
result = number - number2
elif self.binary_operation == '/':
try:
result = float(number) / number2
except ZeroDivisionError:
result = 'ERROR division by zero'
elif self.binary_operation == '*':
result = number * number2
self.first_number_string = ''
self.second_number_string = ''
self.binary_operation = ''
# Save result
self.result = str(result)
self.buffer = self.result
if self.result == 'ERROR division by zero':
self.result = ''
def reset(self):
self.result = ''
self.binary_operation = ''
self.first_number_string = ''
self.second_number_string = ''
self.negative_number = False
self.buffer = '0'
def dot(self):
if self.first_number_string == '':
# Forgot to enter a zero before '.'
self.combination('0.')
elif '.' in self.first_number_string:
# Repeated, incorrect entry '.'
pass
else:
self.combination('.')
def get_buffer(self):
'''The result is the last action'''
return self.buffer
class GUI:
def __init__(self, controller, title='My calc',
width_root=160, height_root=170,
width_number_button=1, width_math_button=2):
self.controller = controller
self.root = Tk()
self.root.title(title)
self.root.maxsize(width = width_root, height = height_root)
self.root.minsize(width = width_root, height = height_root)
frame = Frame(self.root)
frame_entry = Frame(self.root)
frame_math = Frame(self.root)
frame_numbers = Frame(frame)
frame_numbers2 = Frame(frame)
frame_numbers3 = Frame(frame)
frame_numbers4 = Frame(frame)
self.entry = Entry(frame_entry)
self.entry.insert(END, '0')
self.entry.pack(side = TOP)
frame_entry.pack(side = TOP)
frame.pack(side = LEFT)
frame_math.pack(side = RIGHT)
frame_numbers.pack(side = TOP)
frame_numbers2.pack(side = TOP)
frame_numbers3.pack(side = TOP)
frame_numbers4.pack(side = TOP)
Button(frame_entry, text = 'C', width = width_math_button,
command = send(self.write, 'c')).pack(side = RIGHT)
for char in '789':
Button(frame_numbers, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '456':
Button(frame_numbers2, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '123':
Button(frame_numbers3, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '0.=':
Button(frame_numbers4, text = char, width = width_number_button,
command = send(self.write, char)).pack(side = LEFT)
for char in '+-/*':
Button(frame_math, text = char, width = width_math_button,
command = send(self.write, char)).pack(side = TOP)
def show(self, value):
self.entry.delete(first = 0, last = END)
self.entry.insert(END, value)
def write(self, char):
self.controller.write(char)
self.controller.show()
def run(self):
'''Run a GUI calculator'''
self.root.mainloop()
class Console:
def __init__(self, controller):
self.controller = controller
def run(self):
'''Run a console calculator'''
print '[c] reset, [q] quit, [=] result'
while True:
for char in raw_input('calc>'):
if char == 'q':
raise SystemExit('Quit')
elif char == '=':
self.controller.write(char)
self.controller.show()
else:
self.controller.write(char)
def write(self, char):
self.controller.write(char)
def show(self, value):
print value
class Controller:
'''Model-view-controller'''
def __init__(self):
self.logic = Logic()
if 'g' in raw_input('GUI or console? ([g] or [c])?\n'):
self.user_interface = GUI(self)
else:
self.user_interface = Console(self)
def write(self, char):
self.logic.write(char)
def show(self):
self.user_interface.show(self.logic.get_buffer())
def run(self):
self.user_interface.run()
if __name__ == '__main__':
Controller().run()