This is my first post and I'm quite new at programming, so I might not be able to convey my question appropriately, but I'll do my best!
tries_dict = {1:'first', 2:'second', 3:'third', 4:'fourth', ub_tries:'last'}
ub_tries = user input
tries = 1
input ('\nCome on make your ' + tries_dict.get(tries) + guess: ')
These 3 elements are part of a number guess game I created, and I included them in a while
loop where tries += 1
after each wrong answer.
As you can see, in my dictionary there are custom values for the first 4 answers and the last possible chance before the game is over, so here is what I tried to do:
I wanted to find a way to have the 'NEXT' value for every answer/key between 'fourth' and 'last'.
As in:
tries = 5
Come on make your next guess
tries = 6
Come on make your next guess
and so on
I did find a way with some complex looping, but being the curious type I wanted to know of more efficient/practical ways to accomplish this.
Here are some options i thought about but couldn't get to work:
- Using a range as a key
- Finding a way to generate a list with values between 4 and
ub_tries
and using that list as a key
So generally speaking: how can one create a way to have this general answer (next or whatever) for keys that aren't specified in a dictionary?
Any feedback would be greatly appreciated, feel free to ask for clarifications since I can tell myself my question is kind of messy.
I hope I get more crafty both at programming and asking related questions, so far my programming is nearly as messy as my summary skills, sigh!
I'm not sure whether this is what you want, but dict.get
may be the answer:
>>> ub_tries = 20
>>> tries_dict = {1:'first', 2:'second', 3:'third', 4:'fourth', ub_tries:'last'}
>>> tries_dict.get(1, 'next')
'first'
>>> tries_dict.get(4, 'next')
'fourth'
>>> tries_dict.get(5, 'next')
'next'
>>> tries_dict.get(20, 'next')
'last'
>>> tries_dict.get(21, 'next')
'next'
Of course you could wrap this up in a function, in various different ways. For example:
def name_try(try_number, ub_tries):
tries_dict = {1:'first', 2:'second', 3:'third', 4:'fourth', ub_tries:'last'}
return tries_dict.get(try_number, 'next')
At any rate, dict.get(key, default=None)
is like dict[key]
, except that if key
is not a member, instead of raising a KeyError
, it returns default
.
As for your suggestions:
using a range as a key??
Sure, you can do that (if you're in Python 2 instead of 3, use xrange
for range
), but how would it help?
d = { range(1, 5): '???',
range(5, ub_tries): 'next',
range(ub_tries, ub_tries + 1): 'last' }
That's perfectly legal—but d[6]
is going to raise a KeyError
, because 6
isn't the same thing as range(5, ub_tries)
.
If you want this to work, you could build a RangeDictionary
like this:
class RangeDictionary(dict):
def __getitem__(self, key):
for r in self.keys():
if key in r:
return super().__getitem__(r)
return super().__getitem__(key)
But that's well beyond "beginners' Python", even for this horribly inefficient, incomplete, and non-robust implementation, so I wouldn't suggest it.
finding a way to generate a list with values between 4 and ub_tries and using such list as a key
You mean like this?
>>> ub_tries = 8
>>> tries_dict = {1:'first', 2:'second', 3:'third', 4:'fourth', ub_tries:'last'}
>>> tries_dict.update({i: 'next' for i in range(5, ub_tries)})
>>> tries_dict
{1: 'first', 2: 'second', 3: 'third', 4: 'fourth', 5: 'next', 6: 'next', 7: 'next', 8: 'last'}
>>> tries_dict[6]
'next'
That works, but it's probably not as good a solution.
Finally, you could use defaultdict
, which lets you bake the default value into the dictionary, instead of passing it as part of each call:
>>> from collections import defaultdict
>>> tries_dict = defaultdict(lambda: 'next',
... {1:'first', 2:'second', 3:'third', 4:'fourth', ub_tries:'last'})
>>> tries_dict
defaultdict(<function <lambda> at 0x10272fef0>, {8: 'last', 1: 'first', 2: 'second', 3: 'third', 4: 'fourth'})
>>> tries_dict[5]
'next'
>>> tries_dict
defaultdict(<function <lambda> at 0x10272fef0>, {1: 'first', 2: 'second', 3: 'third', 4: 'fourth', 5: 'next', 8: 'last'})
However, note that this permanently creates each element the first time you ask for it—and you have to create a function that returns the default value. This makes it more useful for cases where you're going to be updating values, and just want a default as a starting point.
Have I captured your intent for the game here?
max_tries = 8
tries_verbage = {
1: 'first',
2: 'second',
3: 'third',
4: 'fourth',
max_tries: 'last'
}
for i in xrange(1, max_tries + 1):
raw_input('Make your %s guess:' % tries_verbage.get(i, 'next'))
returns
Make your first guess:1
Make your second guess:2
Make your third guess:3
Make your fourth guess:4
Make your next guess:5
Make your next guess:6
Make your next guess:7
Make your last guess:8
Why don't you just use a list?
MAX_TRIES = 10
tries_list = ["first", "second", "third", "fourth"]
for word in (tries_list[:MAX_TRIES-1] +
(["next"] * (MAX_TRIES - len(tries_list) - 1)) + ["last"]):
result = raw_input("Come on, make your {} guess:".format(word))
Note this code won't work as planned for MAX_TRIES = 0
, but I don't think that will be a problem.
You can use a dictionary with range as a key:
def switch(x):
return { 1<x<4: 'several', 5<x<40: 'lots'}[1]
x = input("enter no. of monsters ")
print switch(x)
Hope this helps. :)