Jacob S. wrote:
Great! I took the improvements you gave me an added support for keys (So you can type in 1.25+2= instead of having to type the buttons.) As always, I encourage improvements to my code. Maybe that will be my disclaimer... I have always liked and wanted to adopt Liam's.
Here's a few thoughts.... :)
def equal(self,*args): if self.action: self.newnum = self.distext.get() self.newnum = str(eval(self.oldnum+self.action+self.newnum)) self.distext.set(self.newnum) self.oldnum = '0' self.action = '' self.shouldblank = True
Instead of using text representations of the operators, and eval()ing a string, why not use the operator module?
Your operator keys can set self.action to be operator.add, operator.subtract, etc; then your equal() function becomes
def equal(self, *args): if self.action: self.newnum = self.distext.get() self.newnum= str(self.action(float(self.oldnum), \ float(self.newnum))) self.distext.set(self.newnum) self.oldnum = '0' self.action = '' # I'd actually prefer None here... self.shouldblank = True
def add(self,*args): self.handleOperator('+')
becomes
def add(self, *args): self.handleOperator(operator.add)
The handleOperator() function can stay the same.
def clear(self): self.action = '' self.oldnum = '0' self.distext.set('0') self.shouldblank = True
As I mentioned in a comment above, I'd prefer to use None for self.action when it's unset. There's no real practical difference, but conceptually it's more accurate to use a null-object than to use an empty string. Minor style point, I know, but you *did* ask for advice. ;)
def memminus(self): self.memory = str(eval(self.memory+"-"+self.distext.get())) self.shouldblank = True
def memplus(self): self.memory = str(eval(self.memory+"+"+self.distext.get())) self.shouldblank = True
Why use eval() here? You could just as easily do these as
def memminus(self): self.memory = str(float(self.memory) - \ float(self.distext.get())) self.shouldblank = True
I try to avoid using eval() wherever possible, which is almost everywhere. ;) There's a huge security hole in using eval() on arbitrary strings, and it's not the greatest performance either. (Each invocation of eval() will generate bytecode and create a code object that does essentially the same thing as my explicit conversion code does, so by doing it manually you save a parsing/compiling step.) You're not eval()ing arbitrary strings here, at least, but it's still good practice to only use eval() when absolutely necessary.
def __init__(self, master=None): Frame.__init__(self,master) self.master.title("Calculator by Jacob, Inc.") self.pack(expand=True) m = lambda x: self.adddigit(x) self.bl = [lambda *x: self.adddigit('0',x), lambda *x: self.adddigit('1',x), lambda *x: self.adddigit('2',x), lambda *x: self.adddigit('3',x), lambda *x: self.adddigit('4',x), lambda *x: self.adddigit('5',x), lambda *x: self.adddigit('6',x), lambda *x: self.adddigit('7',x), lambda *x: self.adddigit('8',x), lambda *x: self.adddigit('9',x)] for y in range(10): self.bind_all(str(y),self.bl[y]) self.bind_all("+",lambda x: self.add(x)) self.bind_all("-",lambda x: self.subtract(x)) self.bind_all("*",lambda x: self.multiply(x)) self.bind_all("/",lambda x: self.divide(x)) self.bind_all("=",lambda x: self.equal(x)) self.bind_all(".",lambda x: self.adddigitdot(x))
There's absolutely no point to doing lambda x: somefunc(x) -- all you're doing is adding an extra layer of function call. You can replace all of those with something like
self.bind_all('+', self.add)
And actually, because I'm not fond of lambda to begin with, I'd redefine your adddigit() method:
def make_adddigit_callback(self, digit): def adddigit(*args): self.ctb() self.distext.set(self.distext.get()+digit) return adddigit
(You may not need the *args above, if the button callback is expected to be zero parameters -- I don't use Tkinter, so I'm not sure offhand.) Then your button bindings can simply be
self.bl = [ self.make_adddigit_callback('0'), self.make_adddigit_callback('1'), self.make_adddigit_callback('2'), ... ]
Or even --
self.bl = [self.make_adddigit_callback(digit) \ for digit in '0123456789']
Remember, there's nothing particularly special about lambdas -- you can create and pass around regular named functions, too. Each time that make_adddigit_callback() is called, it creates and returns a new function object which captures the current value of 'digit', in exactly the same way that lambda does. A function object (whether named with def, or lambda) like this, which captures a variable's current state, is called a closure. Closures are indispensible for GUI callbacks like this, and many people automatically turn to lambda when they want a closure. For me, though, having a proper def statement somewhere feels clearer. (The merits of lambda vs. def'd functions are a frequent subject of heated debate on comp.lang.python, so if you prefer to stick with the lambdas in this case, I'm sure you'd be able to find plenty of people to support you... ;) )
Jeff Shannon Technician/Programmer Credit International
_______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor