I tried to implement as many changes suggested as possible as well as expanding the code base according to the suggested link. The original question can be found here. It does solve 4x4 and 9x9 Sodukus.
What should I do to make it more Pythonic?
This is what I came up with:
from numpy import *
import sys
TEXT = '.123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
NUMBER = arange(len(TEXT)+1)
## Read the file in
def read(name):
with open(name) as fid:
str = fid.read().replace('\n',"")
s = int(sqrt(len(str)))
# check input
if s != int(sqrt(s))**2:
raise Exception("Input needs to be quadratic")
if len(str.strip(TEXT[0:s + 1])) != 0:
raise Exception("Do not use anything beside %s with this base" % TEXT[0:s + 1])
return (str,s)
## Set the value in field and adjust the possibility tensor
def set(col,row,wert,feld,pos,s):
if feld[col,row] != 0:
raise Exception
pos[col,row,:] = 0
pos[:,row,wert] = 0
pos[col,:,wert] = 0
ss = int(sqrt(s))
col_start = col // ss * ss
row_start = row // ss * ss
pos[col_start:col_start + ss,row_start:row_start + ss,wert] = 0
feld[col,row] = wert + 1
## Read in the stuff and create field
def read_and_construct(name):
str,s = read(name)
feld = zeros((s,s),dtype=uint8)
pos = ones((s,s,s),dtype=bool_)
for ind in range(s**2):
if str[ind] is not '0' and str[ind] is not '.':
set(ind // s,ind % s,NUMBER[TEXT.index(str[ind])] - 1,feld,pos,s)
return (feld,pos,s)
## Write the output into a file
def write(name,feld,s):
with open(name,'w') as fid:
for col in range(s):
for row in range(s):
fid.write(TEXT[feld[col,row]])
fid.write('\n')
## Are we there yet?
def done(feld,s):
return count_nonzero(feld) == s**2
## Start the Analytical Process returns 0 if solved
def anal(feld,pos,s):
neu = 1
while neu:
neu = 0
# Scan all dimensions of pos
ind = where(sum(pos,axis = 2) == 1)
for x,y in zip(ind[0],ind[1]):
set(x,y,nonzero(pos[x,y,:])[0][0],feld,pos,s)
ind = where(sum(pos,axis = 1) == 1)
for x,y in zip(ind[0],ind[1]):
set(x,nonzero(pos[x,:,y])[0][0],y,feld,pos,s)
ind = where(sum(pos,axis = 0) == 1)
for x,y in zip(ind[0],ind[1]):
set(nonzero(pos[:,x,y])[0][0],x,y,feld,pos,s)
# get the box
for x in range(s):
for y in range(s):
col_start = x // s * s
row_start = x % s * s
if sum(pos[col_start:col_start + s,row_start:row_start + s,y]) == 1:
ind = nonzero(pos[col_start:col_start + s,row_start:row_start + s,y]);
neu = set(col_start + ind[0][0],row_start + ind[1][0],y,feld,pos,s)
## A brute force method to calculate the missing values returns 0 if solved
def brute(feld,pos,s):
# get a starting point with minimum possibilities
n_pos = sum(pos,axis=2)
n_pos[n_pos == 0] = 255
ind = unravel_index(n_pos.argmin(), n_pos.shape)
# trial and error
for wert in nonzero(pos[ind[0],ind[1],:])[0]:
try_feld = copy(feld)
try_pos = copy(pos)
try:
set(ind[0],ind[1],wert,try_feld,try_pos,s)
anal(try_feld,try_pos,s)
if not done(try_feld,s):
try_feld, try_pos = brute(try_feld,try_pos,s)
if done(try_feld,s):
break
except:
pass
return (try_feld, try_pos)
## main method
if __name__ == "__main__":
if len(sys.argv) != 3:
raise Exception("Give path to input and output file")
feld, pos, s = read_and_construct(sys.argv[1])
anal(feld,pos,s)
if not done(feld,s):
feld,pos = brute(feld,pos,s)
write(sys.argv[2],feld,s)
Comments on the code:
- It's just great that I can use
Exception
to control the program flow. Very nice - I hope did the file I/O now in a Pythonic way.
- I read that vectorization is good in Python. Should I try to vectorize the code even further?
1 Answer 1
Correct idiom for if not cond: raise Exception(message)
is assert cond, message
.
Also, don't use builtin names for something else. When I look at str.strip, I see unbound method. :-(
str[ind] is not '0' and str[ind] is not '.'
is str[ind] not in '0.'
. And don't use is
when you mean ==
.
Don't ever use bare except:
. It catches much more than you think. Either define customized Exceptions subclass, or use assert as I said above.
-
3\$\begingroup\$ To clarify: "Assertions should not be used to test for failure cases that can occur because of bad user input or operating system/environment failures, such as a file not being found." Assertions are for internal consistency checks. In other words, assert conditions that you know to be true, rather than conditions that you hope to be true. For example,
assert len(sys.argv) == 3, "Give path to input and output file"
would definitely not be appropriate. \$\endgroup\$200_success– 200_success2014年07月11日 10:44:14 +00:00Commented Jul 11, 2014 at 10:44 -
\$\begingroup\$ A relevant blog on "the evils of (bare)
except
. \$\endgroup\$jonrsharpe– jonrsharpe2014年07月11日 12:26:01 +00:00Commented Jul 11, 2014 at 12:26 -
\$\begingroup\$ @200_success: If he used precise exceptions (for example, TypeError, ValueError,...), you'd be correct. This way, I stand by my claim that AssertionError is infinitely better than just Exception. Of course they can be turned off, but OP's not gonna do that. :-) \$\endgroup\$Veky– Veky2014年07月19日 07:19:23 +00:00Commented Jul 19, 2014 at 7:19