Beginner question

E

eschneider92

Is there a more efficient way of doing this? Any help is gratly appreciated.


import random
def partdeux():
print('''A man lunges at you with a knife!
Do you DUCK or PARRY?''')
option1=('duck')
option2=('parry')
optionsindex=[option1, option2]
randomizer=random.choice(optionsindex)
while randomizer==option1:
if input() in option1:
print('he tumbles over you')
break
else:
print('he stabs you')
break
while randomizer==option2:
if input() in option2:
print('you trip him up')
break
else:
print('he stabs you')
break
partdeux()
 
C

Carlos Nepomuceno

That doesn't even works because input() is the same as eval(raw_input()). So you'll get a NameError exception.

I think you know that. Perhaps you mean raw_input() instead of input().
In that case the answer is yes, it can be more 'efficient' because the if-then-else clause always breaks the while loop.
I think you are looking for is a switch statement, which Python don't have.

You can use the following structure to emulate a switch statement:

def function1():
if raw_input() in option1:
print('he tumbles over you')
else:
print('he stabs you')

def function2():
if raw_input() in option2:
print('you trip him up')
else:
print('he stabs you')

def default():
print 'DEFAULT'

switch = {
option1: function1,
option2: function2
}
switch.get(randomizer, default)()

Note that switch is a dictionary and you can use it without creating a variable, for example:

{ option1: function1,
option2: function2
}.get(randomizer, default)()
 
A

Anssi Saari

Is there a more efficient way of doing this? Any help is gratly appreciated.

Efficiency in a short program isn't a big thing. You have some pretty
weird things in there, there's no need make single element tuples out of
your strings and then putting those in a list. Just put the strings in a
tuple and go. Likewise there's really no point in having while loops
where you exit on the first round now is there? Just use an if.

BTW, did I get the logic correctly, the end result is random? If true
then the logic can be simplified greatly, you can just discard the user
input and print a random choice of your three result strings...
 
J

John Ladasky

That doesn't even works because input() is the same as eval(raw_input()).So you'll get a NameError exception.

I think you know that. Perhaps you mean raw_input() instead of input().

But the OP's code shows print() functions... which is not the habit of Python 2 programmers, even though it's legal code. And the OP says s/he's a beginning programmer... so why start learning Python 2 in 2013? Let me ask the OP, are you programming in Python 2 or Python 3?

If the answer is indeed Python 3: raw_input() has been banished from Python3, in favor of plain-old input().
 
C

Carlos Nepomuceno

Date: Tue, 4 Jun 2013 00:53:04 -0700
Subject: Re: Beginner question
From: (e-mail address removed)
To: (e-mail address removed)



But the OP's code shows print() functions... which is not the habit of Python 2 programmers, even though it's legal code. And the OP says s/he's a beginning programmer... so why start learning Python 2 in 2013? Let me ask the OP, are you programming in Python 2 or Python 3?

If the answer is indeed Python 3: raw_input() has been banished from Python 3, in favor of plain-old input().

Didn't know that. Thanks!
 
C

Chris Angelico

You're right! I'm guessing that's not what the OP wants?

I'm guessing that's exactly what the OP wants. This is a fairly
classic programming puzzle; on the surface it appears that you have
some influence on the outcome, but ultimately you're playing
rock-paper-scissors with the Random Number God.

ChrisA
 
P

Peter Otten

Chris said:
I'm guessing that's exactly what the OP wants. This is a fairly
classic programming puzzle; on the surface it appears that you have
some influence on the outcome, but ultimately you're playing
rock-paper-scissors with the Random Number God.

As it is written, don't you always win if you hit enter?
It may be the approved cheat code, though...

OP:

("some string")

is not a tuple, it is the same as just

"some string"

therefore

option1 = "some string"
if input() in option1:
print("yes")

prints 'yes' if the user types in a substring of option1, and the shortest
substring of any string is "".

For a single-item tuple the trailing comma is mandatory:
('some string',)

In general a tuple is consituted by the comma(s), not the parentheses:
('one', 'two')
 
C

Carlos Nepomuceno

Started answering... now I'm asking! lol

I've tried to use dict() to create a dictionary to use like the switch statement providing variable names instead of literals, such as:
{'A': 0, 'B': 1}

That's ok! But if I use dict() declaration:
{'a': 0, 'b': 1} #here variable names are taken as literals

What's going on? Is there a way to make dict() to resolve the variables?
 
F

Fábio Santos

Started answering... now I'm asking! lol

I've tried to use dict() to create a dictionary to use like the switch
statement providing variable names instead of literals, such as:
{'A': 0, 'B': 1}

That's ok! But if I use dict() declaration:

{'a': 0, 'b': 1} #here variable names are taken as literals

What's going on? Is there a way to make dict() to resolve the variables?

Well yes.

dict(**{a:0,b:1})

The dict() constructor makes a dictionary from keyword arguments. So you
just have to feed it keyword arguments using **.

And if you're in a bad day,

dict(**locals())
 
C

Carlos Nepomuceno

On 4 Jun 2013 12:28 said:
Well yes.
dict(**{a:0,b:1})
The dict() constructor makes a dictionary from keyword arguments. So you just have to feed it keyword arguments using **.
And if you're in a bad day,
dict(**locals())

That's exactly the same!True

Are there any benefits from using dict() instead of {}?
 
F

Fábio Santos

[...]
variables?
Well yes.
dict(**{a:0,b:1})
The dict() constructor makes a dictionary from keyword arguments. So you just have to feed it keyword arguments using **.
And if you're in a bad day,
dict(**locals())

That's exactly the same!True

Are there any benefits from using dict() instead of {}?

Other than being able to create a dict from a list of tuples, and copying a
dict using dict(anotherdict.items()), not that I know of.
 
S

Steven D'Aprano

That's exactly the same!
True


Of course it is. Isn't that what you wanted?

It's also a waste of time, because you create a dict literal using {},
then unpack it into keyword arguments, then call dict() to create a new
dict with the same content. Rather like doing this:

n = int(str(42))

only even more expensive.

Are there any benefits from using dict() instead of {}?

Of course there are. {} can be used for creating dict literals, which
means you are limited to key/values that you explicitly include. dict(),
on the other hand, has a rich set of constructor APIs:

py> help(dict)

Help on class dict in module builtins:

class dict(object)
| dict() -> new empty dictionary
| dict(mapping) -> new dictionary initialized from a mapping object's
| (key, value) pairs
| dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for k, v in iterable:
| d[k] = v
| dict(**kwargs) -> new dictionary initialized with the name=value pairs
| in the keyword argument list. For example: dict(one=1, two=2)


py> dict(zip('abcd', range(4)), x=23, y=42, z=999)
{'a': 0, 'c': 2, 'b': 1, 'd': 3, 'y': 42, 'x': 23, 'z': 999}


Try doing that with {} alone!
 
S

Steven D'Aprano

Started answering... now I'm asking! lol

I've tried to use dict() to create a dictionary to use like the switch
statement providing variable names instead of literals, such as:

{'A': 0, 'B': 1}

That's ok! But if I use dict() declaration:

{'a': 0, 'b': 1} #here variable names are taken as literals

What's going on? Is there a way to make dict() to resolve the variables?


This is by design. You're calling a function, dict(), and like all
functions, code like:

func(name=value)

provides a *keyword argument*, where the argument is called "name" and
the argument's value is as given. dict is no different from any other
function, it has no superpowers, keyword arguments are still keyword
arguments.

In this case, there is no simple way to use the dict() function[1] the
way you want. You could build up a string and then call eval():

s = "dict(%s=0, %s=1)" % (a, b)
d = eval(s)

but that's slow and inconvenient and dangerous if your data is untrusted.

So in this specific case, you should stick to the {} method.



[1] Technically it's a type, not a function, but the difference makes no
difference here.
 
C

Carlos Nepomuceno

From: (e-mail address removed)
Subject: Re: Beginner question
Date: Tue, 4 Jun 2013 12:25:27 +0000
To: (e-mail address removed)




Of course it is. Isn't that what you wanted?

Indeed! But that form isn't economically viable as you noted.
It's also a waste of time, because you create a dict literal using {},
then unpack it into keyword arguments, then call dict() to create a new
dict with the same content. Rather like doing this:

n = int(str(42))

only even more expensive.

Are there any benefits from using dict() instead of {}?

Of course there are. {} can be used for creating dict literals, which
means you are limited to key/values that you explicitly include. dict(),
on the other hand, has a rich set of constructor APIs:

py> help(dict)

Help on class dict in module builtins:

class dict(object)
| dict() -> new empty dictionary
| dict(mapping) -> new dictionary initialized from a mapping object's
| (key, value) pairs
| dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for k, v in iterable:
| d[k] = v
| dict(**kwargs) -> new dictionary initialized with the name=value pairs
| in the keyword argument list. For example: dict(one=1, two=2)


py> dict(zip('abcd', range(4)), x=23, y=42, z=999)
{'a': 0, 'c': 2, 'b': 1, 'd': 3, 'y': 42, 'x': 23, 'z': 999}

Awesome! Now I can do it just like that:
dict([(chr(ord('a')+x),x) for x in range(2)])
{'a': 0, 'b': 1}

Thanks a lot! ;)
 
C

Carlos Nepomuceno

From: (e-mail address removed)
Subject: Re: Beginner question
Date: Tue, 4 Jun 2013 12:35:59 +0000
To: (e-mail address removed)

Started answering... now I'm asking! lol

I've tried to use dict() to create a dictionary to use like the switch
statement providing variable names instead of literals, such as:

{'A': 0, 'B': 1}

That's ok! But if I use dict() declaration:

{'a': 0, 'b': 1} #here variable names are taken as literals

What's going on? Is there a way to make dict() to resolve the variables?


This is by design. You're calling a function, dict(), and like all
functions, code like:

func(name=value)

provides a *keyword argument*, where the argument is called "name" and
the argument's value is as given. dict is no different from any other
function, it has no superpowers, keyword arguments are still keyword
arguments.

In this case, there is no simple way to use the dict() function[1] the
way you want. You could build up a string and then call eval():

s = "dict(%s=0, %s=1)" % (a, b)
d = eval(s)

but that's slow and inconvenient and dangerous if your data is untrusted.

So in this specific case, you should stick to the {} method.



[1] Technically it's a type, not a function, but the difference makesno
difference here.

It's superclear now! You're an excelent teacher!

Can you explain me the difference of the type and function you've just mentioned?
 
R

Roy Smith

Larry Hudson said:
def partdeux():
print('A man lunges at you with a knife!')
option = input('Do you DUCK or PARRY? ').lower()
success = random.randint(0, 1)
if success:
if option == 'duck':
print('He tumbles over you')
return
if option == 'parry':
print('You trip him up')
return
print('He stabs you')

I'm going to suggest another possible way to organize this. I'm not
claiming it's necessarily better, but as this is a learning exercise,
it's worth exploring. Get rid of all the conditional logic and make
this purely table-driven:

responses = {(0, 'duck'): "He tumbles over you",
(0, 'parry'): "You trip him up",
(1, 'duck'): "He stabs you",
(1, 'parry'): "He stabs you",
}

and then....

def partdeux():
print('A man lunges at you with a knife!')
option = input('Do you DUCK or PARRY? ').lower()
success = random.randint(0, 1)
print responses[(success, option)]

Consider what happens when the game evolves to the point where you have
four options (DUCK, PARRY, RETREAT, FEINT), multiple levels of success,
and modifiers for which hand you and/or your opponent are holding your
weapons in? Trying to cover all that with nested logic will quickly
drive you insane.
 
M

Mitya Sirenef

That's exactly the same!
True

Are there any benefits from using dict() instead of {}?


Other than what Steven already mentioned, a big advantage is that it's
easier to make a dict if you have a lot of keys that are valid
identifiers, and it's more readable, to boot:

dict(red=1, blue=2, orange=3, violet=4, crimson=5, ...)

VS.

{'red':1, 'blue':2, 'orange':3, 'violet':4, 'crimson':5, ...}

-m


--
Lark's Tongue Guide to Python: http://lightbird.net/larks/

Although the most acute judges of the witches and even the witches
themselves, were convinced of the guilt of witchery, the guilt nevertheless
was non-existent. It is thus with all guilt. Friedrich Nietzsche
 
J

Joshua Landau

Is there a more efficient way of doing this? Any help is gratly appreciated.


import random
def partdeux():
print('''A man lunges at you with a knife!
Do you DUCK or PARRY?''')
option1=('duck')
option2=('parry')
optionsindex=[option1, option2]
randomizer=random.choice(optionsindex)
while randomizer==option1:
if input() in option1:
print('he tumbles over you')
break
else:
print('he stabs you')
break
while randomizer==option2:
if input() in option2:
print('you trip him up')
break
else:
print('he stabs you')
break
partdeux()

I'm going to look directly at the code for my comment, here, and
explain what's up. I imagine you were given this code to "fix up",
I'll lead you through the steps.


import random

You only use "random.choice", never anything else, so in this case I'd
be tempted to do:
from random import choice

This is *entirely optional*: I tend to do quite a lot of "from
<module> import <object>" but others prefer to be more explicit about
where things come from; your choice.


def partdeux():

Other than the atrocious name this is as simple as it gets to define a
function - you should like that it's a function, though.


print('''A man lunges at you with a knife!
Do you DUCK or PARRY?''')

This is iffy! Triple-quotes? Personally this is a really bad time to
use them - they break indentation and whatnot. I'd write:
print('A man lunges at you with a knife!')
print('Do you DUCK or PARRY?')

This, in my opinion, is much nicer. But we haven't "simplified" much yet.


option1=('duck')
option2=('parry')
optionsindex=[option1, option2]

There are two things off about this. Firstly, no-one should be so
addicted to brackets to use them here, and you should space variables.
option1 = 'duck'
option2 = 'parry'

BUT this is not needed anyway. The next line puts both of these in a
variable. You can add them straight in:
optionsindex = ['duck', 'parry']

There are some things wrong though:
1) *Never* lie. This is not an "index" of any sort, unless you're
referring to one of these:
[http://shop.pageprotectors.com/images/Index-Ring-Binder-2-Rings-Recipe-3x5-Card.jpg]
This should be named "options" or, better, "valid_responses".

2) You write "Do you DUCK or PARRY?". None of this suggests uppercase.
We shall deal with this later.

Thus:
valid_responses = ['duck', 'parry']

randomizer=random.choice(optionsindex)
while randomizer==option1: ....
while randomizer==option2:

This is odd! What do you think this does? This says that you choose an
option, "duck" or "parry". If it is "duck", then do A. If it is
"parry", then do B. But why would a computer choose options like that?
Surely it's better to do:

(I'm properly spacing it as I go along, by the way)
randomizer = random.choice([True, False])
while randomizer: ....
while not randomizer:

Oh, that's odd! As randomizer never changes after you set it, you can
be sure that the "while randomizer" is equivalent to "while True" or
"while False". This means it will loop forever without a break.
Looking at the breaks it is clear that *all paths lead to a break*. A
while *LOOP* should only ever be used to *LOOP*. This makes the
looping redundant.

Thus remove the breaks and use:
randomizer = random.choice([True, False])
if randomizer: ....
if not randomizer:

which can be better written:
if random.choice([True, False]): ....
else:
(BUT you may have to write "if choice([True, False]):" if you've
followed all of my advice)

if input() in option1:
"option1" no longer exists, so this is now written:
if input() in valid_responses[0]:
BUT why "in"? You want to check whether that *equals* the response,
not whether that is *in* the response:
if input() == valid_responses[0]:

else:
print('he stabs you')
Why "else"? This means that if you wrote "Seppuku" *he'd* stab you.
You want to check that you actually wrote the right thing!
elif input() == valid_responses[1]:
print('he stabs you')
This does not work, but we'll fix it later.

if input() in option2:
For the same reason, change this to:
if input() == valid_responses[1]:

else:
print('he stabs you') and this to:
elif input() == valid_responses[0]:
print('he stabs you')


The rest is better. That leaves you with a much nicer looking function:

### START CODE ###
from random import choice

def partdeux():
print("A man lunges at you with a knife!")
print("Do you DUCK or PARRY?")

valid_responses = ["duck", "parry"]

if choice([True, False]):
if input() == valid_responses[0]:
print('he tumbles over you')
elif input() == valid_responses[0]:
print('he stabs you')

else:
if input() == valid_responses[1]:
print('you trip him up')
elif input() == valid_responses[1]::
print('he stabs you')
partdeux()
### END CODE ###

There are some things to note. One is the multiple calls to "input()".
It actually makes sense to do this when you ask the question (you
don't ask someone if they want coffee, take a jog and then get your
answer, do you?).

Thus:
print("Do you DUCK or PARRY?") changes to
response = input("Do you DUCK or PARRY?\n")
The "\n" is because "input" doesn't automatically add a new line
whereas print does.

Secondly, you want to catch if it's not valid. You can do this
immediately after calling input (make sure you define valid_responses
first - move it to the top):
if response not in valid_responses:
# OH NO!

The way I deal with these is often with a while loop until there is a
correct response:
while "trying to get response":
response = input("Do you DUCK or PARRY?\n")
if response in valid_input:
break

What this does is constantly ask "Do you DUCK or PARRY" until the
response is valid.


Finally, you want to convert the response to lowercase in case they
thought they had to type in UPPERCASE because it looked like that.
while "trying to get response":
response = input("Do you DUCK or PARRY?\n").lower()
if response in valid_input:
break


Done. Here's the code:
(I added three bugs for fun; they're not hard to find but it should
stop you just taking the code without either understanding it or
reading what I wrote. It's also good practice)
### CODE ###
from random import choice

def partdeux():
valid_responses = ["duck", "parry"]

print("A man lunges at you with a knife!")

while "trying to get response":
response = input("Do you DUCK or PARRY?").lower()

if response in valid_responses:
break

if choice([True, False]):
if response == valid_responses[0]:
print("he tumbles over you'')
elif response == valid_responses[1]:
print('he stabs you')

else:
if response == valid_responses[1]:
print('you trip him up')
elif response == valid_responses[1]:
print('he stabs you')
partdeux()
### END CODE ###

NOTE THAT ROY SMITH'S VERSION IS BETTER, BUT COMPLETELY DIFFERENT CODE
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,137
Messages
2,570,795
Members
47,342
Latest member
eixataze

Latest Threads

Top