1
\$\begingroup\$

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?
asked Jul 11, 2014 at 9:42
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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.

answered Jul 11, 2014 at 10:32
\$\endgroup\$
3
  • 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\$ Commented Jul 11, 2014 at 10:44
  • \$\begingroup\$ A relevant blog on "the evils of (bare) except. \$\endgroup\$ Commented 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\$ Commented Jul 19, 2014 at 7:19

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.