I just wrote a little Python 3 program to spit out every possible combination of a set of 99 characters.
It gets the job done, but I would be very interested in what you think of it.I am just a few days into Python, so I would be grateful for even seemingly obvious advice.
import sys # List of 99 characters and a blank string: lib=["","0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","°","!","\"","§","$","%","&","/","(",")","=","ß","´","`","+","#","-",".",",",">","<","@","€","|","^","~","–","{","[","]","}","Ä","Ö","Ü","ä","ö","ü"] # 10 counters for up to 10-digit-combinations: counter0=-1 counter1=0 counter2=0 counter3=0 counter4=0 counter5=0 counter6=0 counter7=0 counter8=0 counter9=0 # Repetitive if-statements adding to the counters: for i in range(sys.maxsize**99999): counter0+=1 if counter0>99: counter0=counter0*0 counter1+=1 elif counter1>99: counter1=counter1*0 counter2+=1 elif counter2>99: counter2=counter2*0 counter3+=1 elif counter3>99: counter3=counter3*0 counter4+=1 elif counter4>99: counter4=counter4*0 counter5+=1 elif counter5>99: counter5=counter5*0 counter6+=1 elif counter6>99: counter6=counter6*0 counter7+=1 elif counter7>99: counter7=counter7*0 counter8+=1 elif counter8>99: counter8=counter8*0 counter9+=1 elif counter9>99: print("DONE.") # Printing the translation from counters to character - and deleting the former output so it stays in one line: else: print(lib[counter0]+lib[counter1]+lib[counter2]+lib[counter3]+lib[counter4]+lib[counter5]+lib[counter6]+lib[counter7]+lib[counter8]+lib[counter9], end="\r") sys.stdout.write("\b"*10+" "*10+"\b"*10)
Answer
-
We can convert a plain string to a list rather than maintain a list for the characters.
It is far easier to modify and read the following then a list.
lib = [''] + list('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz°!"§$%&/()=ß´`+#-.,><@€|^~–{[]}ÄÖÜäöü')
-
When we have
counter0
,counter1
, …,countern
it’s a hint that we should use a list.counters = [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
We can then use
counters[0]
as a drop in replacement forcounter0
. -
Now that we have
counters
which is a list we can simplify your print from the following.print(lib[counters[0]] + lib[counters[1]] + lib[counters[2]] + lib[counters[3]] + > lib[counters[4]] + lib[counters[5]] + lib[counters[6]] + lib[counters[7]] + lib[counters[8]] + lib[counters[9]], end="\r")
We can use a for loop to loop through counters, index
lib
and print the character. We’ll useend=""
to get the same format that you have. Since we’ve changed from"\r"
to""
we’ll need to print that afterwards.for counter in counters: print(lib[counter], end="") print(end="\r")
-
It would be better to use
len(lib)
rather than hard coding99
in your ifs. If we change the content oflib
it’s much easier to edit justlib
rather thanlib
and 10 99’s. -
Rather than
counter0=counter0*0
it would make more sense to remove the multiplication and just set the value to 0.counter0 = 0
-
It’s convention in Python to put a space either side of operators. This means
a+b
should instead bea + b
. This is as it makes it easier to see what is and is not an operator and either side of an operator. -
It’s convention in Python to use
_
as a ‘throw away’ variable. This means it’s normal to use_
rather thani
in your for loop.
Together this gets:
import sys
lib = [''] + list('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz°!"§$%&/()=ß´`+#-.,><@€|^~–{[]}ÄÖÜäöü')
counters = [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
for _ in range(sys.maxsize**99999):
counters[0] += 1
if counters[0] >= len(lib):
counters[0] = 0
counters[1] += 1
elif counters[1] >= len(lib):
counters[1] = 0
counters[2] += 1
elif counters[2] >= len(lib):
counters[2] = 0
counters[3] += 1
elif counters[3] >= len(lib):
counters[3] = 0
counters[4] += 1
elif counters[4] >= len(lib):
counters[4] = 0
counters[5] += 1
elif counters[5] >= len(lib):
counters[5] = 0
counters[6] += 1
elif counters[6] >= len(lib):
counters[6] = 0
counters[7] += 1
elif counters[7] >= len(lib):
counters[7] = 0
counters[8] += 1
elif counters[8] >= len(lib):
counters[8] = 0
counters[9] += 1
elif counters[9] >= len(lib):
print("DONE.")
else:
for counter in counters:
print(lib[counter], end="")
print(end="\r")
sys.stdout.write("\b"*10 + " "*10 + "\b"*10)
There are still some changes that we can make to your code to make it easier to work with.
These are a bit more advanced so don’t worry if you don’t get them straight away.
-
We can change your big
if
elif
block into a singlefor
loop.
Lets see what we have so far:if counters[0] > len(lib): counters[0] = 0 counters[1] += 1
We know that this section is repeated each time for each index.
So we can make this generic by changing0
toindex
and1
toindex + 1
.if counters[index] >= len(lib): counters[index] = 0 counters[index + 1] += 1
Now we just need to loop over
range(len(counters) - 1)
to duplicate the block 9 times.for index in range(len(counters) - 1): if counters[index] >= len(lib): counters[index] = 0 counters[index + 1] += 1
-
We can use some Python sugar to make your print for loop ‘cleaner’.
Firstly we can remove all theprint
s by building a list.combination = [] for counter in counters: combination.append(lib[counter])
From here we can join all the strings together with
"".join
and pass it toprint
like you did before. This will join the list by empty strings so it converts it’s like manually doingcombination[0] + combination[1] + ...
.print("".join(combination), end="\r")
We can then use a list comprehension to build
combination
in one line.
This is just syntaxtic sugar and is the same as the for loop we used before.
It’s just different, cleaner, syntax.combination = [lib[counter] for counter in counters]
-
We can use either a
while True
loop oritertools.count
rather thanrange(sys.maxsize**99999)
to loop infinitely.while True: counters[0] += 1
import itertools for _ in range(itertools.count()): counters[0] += 1
-
We can probably just use
print
rather thansys.stdout.write
.To make it so there is no newline we can pass
end=""
.
This however doesn’t flush the stream straight away, and so we need to passflush=True
.
lib = [''] + list('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz°!"§$%&/()=ß´`+#-.,><@€|^~–{[]}ÄÖÜäöü')
counters = [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
while True:
counters[0] += 1
for index in range(len(counters) - 1):
if counters[index] >= len(lib):
counters[index] = 0
counters[index + 1] += 1
if counters[9] >= len(lib):
print("DONE.")
else:
print("".join([lib[counter] for counter in counters]), end="\r")
print("\b"*10 + " "*10 + "\b"*10, end="", flush=True)
Attribution
Source : Link , Question Author : Sandra , Answer Author : Peilonrayz