3
\$\begingroup\$

This program goes through 7 common logical functions (AND, OR, NOT, NOR, XOR, XNOR, and NAND) and generates and prints truth tables for them all.

#!/usr/bin/env python2
# coding:utf-8
import operator
import itertools

table = list(itertools.product([False, True], repeat=2))


ops={'AND':  ('∧', (lambda a,b:bool(operator.and_(a,b)))),
     'OR':   ('∨', (lambda a,b:bool(operator.or_(a,b)))),
     'NOT':  ('¬', (lambda a,b:bool(operator.not_(a)))),
     'NOR':  ('↓', (lambda a,b:bool(operator.not_(operator.or_(a,b))))),
     'XOR':  ('↮', (lambda a,b:bool(operator.xor(a,b)))),
     'XNOR': ('↔', (lambda a,b:bool(operator.not_(operator.xor(a,b))))),
     'NAND': ('↑', (lambda a,b:bool(operator.not_(operator.and_(a,b)))))}

pad = lambda x:(str(x) if len(str(x)) == 5 else str(x) + ' ')

for i in ops.keys():
    print i +',',ops[i][0],'\n' + '-'*25

    for j in table:
        print '|', pad(j[0]), '|', pad(j[1]), '|', pad(ops[i][1](j[0],j[1])), '|'

    print '-'*25, '\n'

I don't care much about the speed, but how can I improve the style and follow the PEP guidelines in this program?

\$\endgroup\$
6
\$\begingroup\$

You should make a class. This lets you consolidate the implementation to be consistent, and simplifies a number of the details. You can also implement __str__ and __repr__ to make it easier to print them. Use string formatting for this

class LogicalOperator(object):

    def __init__(self, name, symbol, functor):
        self.name = name
        self.symbol = symbol
        self.functor = functor

    def __call__(self, a, b):
        return bool(functor(a, b))

    def __str__(self):
        return self.name

    def __repr__(self):
        return "{}, {}".format(self.name, self.symbol)

Inheriting from namedtuple makes this easier (thanks to Caridorc for pointing out in the comments).

from collections import namedtuple

class LogicalOperator(namedtuple('LogicalOperator', 'name symbol functor')):
    # no __init__ now
    # rest is unchanged

This way you've separated your concerns. The class encapsulates "what is a logical operator and how do I make it print pretty", and then we use it as necessary to get value and formatting for it.

Then we can still create all of our logical operators, but we do it with classes and a tuple instead of a dict and tuples.

ops = (
    LogicalOperator('AND', '∧', operator.and_),
    LogicalOperator('OR', '∨', operator.or_),
    LogicalOperator('NOT', '¬', lambda a, _: not a),
    LogicalOperator('NOR', '↓', lambda a, b: not (a or b)),
    LogicalOperator('XOR', '↮', operator.xor),
    LogicalOperator('XNOR', '↔', lambda a, b: not (a ^ b)),
    LogicalOperator('NAND', '↑', lambda a, b: not (a and b))
)

A better way to pad strings is to use string.rjust. An even better way is to use the Format Specification Mini-Language.

Then we can put it all together.

if __name__ == '__main__':
    column_width = 25
    table = list(itertools.product([False, True], repeat=2))
    for op in ops:
        print(op)
        print("-"*column_width)
        for left, right in table:
            print("|{:^5}|{:^5}|{:^5}|".format(left, right, op(left, right)))
        print("-"*column_width)

Also, you could use more whitespace throughout. Makes it easier to read.

\$\endgroup\$
  • \$\begingroup\$ Thanks for answering. Also, I don't know if you noticed but I'm using python 2. I didn't see any places where it made a difference, but the print statements still had parentheses. \$\endgroup\$ – Riker Jul 6 '16 at 20:41
  • \$\begingroup\$ having the parenthesis doesn't hurt anything in Python 2, and it'll make it much easier if you ever decide to port it to Python 3 \$\endgroup\$ – Dannnno Jul 6 '16 at 21:34
  • 1
    \$\begingroup\$ Inheriting from named tuple allows you to avoid the verbose __init__ \$\endgroup\$ – Caridorc Jul 7 '16 at 13:44
  • \$\begingroup\$ Also, you could save some memory by adding __slots__ = () to your namedtuple definition. \$\endgroup\$ – pppery Jul 7 '16 at 21:00
  • \$\begingroup\$ @ppperry you could, but there isn't really a reason to. Saving memory (esp in Python) is generally not worthwhile; only do it when you need to. Furthermore, it can be less accessible to people who aren't familiar with it, and prevents introspection with vars() \$\endgroup\$ – Dannnno Jul 7 '16 at 21:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.