 <p>If <code>bitarray</code> can't be installed, the 1d solution in <a href="http://stackoverflow.com/questions/35208160/dot-product-in-python-without-numpy">Dot Product in Python without NumPy</a> can be used with the same nested comprehension (<a href="http://stackoverflow.com/a/35241087/901925">http://stackoverflow.com/a/35241087/901925</a>). This does not take advantage of the 0/1 nature of the data. So basically it's an exercise in nested iterations.</p> <pre><code>def dot1d(a,b): return sum(x*y for x,y in zip(a,b)) def dot_2cmp(a): return [[dot1d(r,c) for c in a] for r in a] </code></pre> <p><code>itertools.product</code> can be used to iterate over the row and column combinations, but the result is a 1d list, which then needs to be grouped (but this step is fast):</p> <pre><code>def dot2d(a): aa=[dot1d(x,y) for x,y in itertools.product(a,a)] return [aa[i::len(a)] for i in range(len(a))] </code></pre> <p>testing:</p> <pre><code>a=[[1,0,1,0],[0,1,0,1],[0,0,1,1],[1,1,0,0]] In [246]: dot2d(a) Out[246]: [[2, 0, 1, 1], [0, 2, 1, 1], [1, 1, 2, 0], [1, 1, 0, 2]] In [247]: dot_2cmp(a) Out[247]: [[2, 0, 1, 1], [0, 2, 1, 1], [1, 1, 2, 0], [1, 1, 0, 2]] In [248]: np.dot(np.array(a),np.array(a).T).tolist() Out[248]: [[2, 0, 1, 1], [0, 2, 1, 1], [1, 1, 2, 0], [1, 1, 0, 2]] </code></pre> <p>In timings on a larger list, the 2 list operations take the same time. The array version, even with the in/out array conversion is considerably faster.</p> <pre><code>In [254]: b=np.random.randint(0,2,(100,100)).tolist() In [255]: timeit np.dot(np.array(b),np.array(b).T).tolist() 100 loops, best of 3: 5.46 ms per loop In [256]: timeit dot2d(b) 10 loops, best of 3: 177 ms per loop In [257]: timeit dot_2cmp(b) 10 loops, best of 3: 177 ms per loop </code></pre> <p>The result is symmetric, so it might be worth the effort to skip the duplicate calculations. Mapping them back on to the nested list will be more work than in <code>numpy</code>.</p> <pre><code>In [265]: timeit [[dot1d(r,c) for c in b[i:]] for i,r in enumerate(b)] 10 loops, best of 3: 90.1 ms per loop </code></pre> <p>For what it's worth I don't consider any of these solutions 'more Pythonic' than the others. As long as it is written in clear, running, Python it is Pythonic. </p>