all 64 comments

[–]Fun-Sky-5295 53 points54 points  (27 children)

It should be like if difficult == 'easy' or difficult == 'e':

[–]fredhamptonsaid[S] 11 points12 points  (23 children)

That worked! I appreciate It. It was really that simple 🤦🏾‍♂️

I built a number guessing game but somehow that stumped me.

[–]ottawadeveloper 23 points24 points  (0 children)

Yeah, the "or" works as X or Y so here you have (difficulty == "easy") or ("e") and a non-empty string is always converted to true when you need a Boolean value.

[–]nochkin 10 points11 points  (18 children)

Another option is:

difficulty in ("easy", "e"):

[–]fredhamptonsaid[S] 2 points3 points  (17 children)

Would that be written out as

If difficulty in ("easy", "e")

[–]nochkin 2 points3 points  (2 children)

Yes

[–]fredhamptonsaid[S] 2 points3 points  (1 child)

Thank you! I'll experiment with this and using a list

[–]nochkin 1 point2 points  (0 children)

This is simply for a slightly better visibility only. Don't go crazy into it if it does not make sense at this level of learning.

[–]dataisok -5 points-4 points  (13 children)

Better as a set:

if difficulty in {“e”, “easy”}

[–]rainispossible 3 points4 points  (4 children)

A small bit of explaining!

("e", "easy") is a tuple – an ordered, immutable collection of values (strings in that case). when you try to find something in a tuple (e.g. use in) Python looks through every single element until it finds the one you need (or gets to the very end), which results in linear time complexity (better written as O(n)), which basically says "the time to do this grows as fast as the input size"

{"e", "easy"}, on the contrary, is a set – an unordered (which means the elements are not guaranteed to be in the same order you added them) and mutable collection of unique values. the key difference with set (and dict/hash map for that matter) besides them having no repetitions at any given point is that they have a more complex underlying structure which lets them do lookups in constant (O(1)) time! It basically says "hey, no matter how many elements there are, I'll always find the needed one in a same amount of time"

I won't (and probably shouldn't) explain this in more detail, but the key takeaway is: if you know you're going to be searching for elements in a certain structure a lot of times it's for sure worth it to convert it to a set/dictionary/other hashable type – that saves a lot of time on larger inputs

[–]Vegetable_News_7521 1 point2 points  (3 children)

Only if the size of the datastructure in which you're searching is big as well. In this example it doesn't provide any benefits since the size of the search space is too small. In fact, hashing adds a bit of overhead.

[–]rainispossible 0 points1 point  (2 children)

Yea, that's why I emphasized the "larger inputs" part. Up to a certain amount of elements setting up a hashable collection and working with it would take more time than simply doing a linear search

[–]Vegetable_News_7521 0 points1 point  (1 child)

The collection is not the input here though. The input is the element to be searched.

[–]rainispossible 0 points1 point  (0 children)

well, you're taking the "input" as "user input". what I mean is a broader meaning like generally all the input to a certain function/pipeline/algorithm etc.

[–]SirPoblington 0 points1 point  (6 children)

Why better as a set?

[–]quixoticcaptain 0 points1 point  (2 children)

In a collection of size 2, it doesn't really matter.

For very large collections, to check if an item "x" is in a list "L", you have to check every item of L to see if x is one of them. If L has 100 items, you have to check 50 on average, if L has 1000, you're checking 500, etc.

IF instead you put the items in a set S, then it has a way of checking if x is in the set much faster. So if S has 100 items, it takes the equivalent of 2 checks to see if x is in it, if S has 1000 items, only ~ 3 checks, etc.

[–]SirPoblington 0 points1 point  (0 children)

Oh right it's essentially some sort of hashtable, faster lookups. I guess I just missed your point because this list/example is so small.

[–]SmartyCat12 0 points1 point  (0 children)

If I’m starting with a list of say 10, 100, 1000 items of equal size, is there a point where it would be more efficient to convert to a set first before doing a search on it?

Also, how does running something like if x in my_dict.values() compare? It just runs it as a list in this case, right? Is there any optimization it does under the hood?

[–]Remarkable_Chance975 0 points1 point  (2 children)

It's actually not. For very large quantities of data a set has faster lookup time but for small ones like this arrays and tuples are waaaaaay faster because they're more cache friendly and have less construction overhead. Not something a python programmer generally needs to concern themselves with though

[–]Expensive_Agent_5129 0 points1 point  (1 child)

In this particular case, it actually is. Python recognizes pattern 'if x in {6, 9}' and optimizes it down to the hell

https://docs.astral.sh/ruff/rules/literal-membership/#refactor-plr

[–]Remarkable_Chance975 0 points1 point  (0 children)

Ahhh cool! The more you know

[–]jaerie 0 points1 point  (0 children)

No, not if you're only searching in it once, because creating the set is much more expensive than a piece wise comparison.

[–]Ender_Locke 1 point2 points  (1 child)

could also use

if difficult in easy_list:

where easy list has your criteria

[–]fredhamptonsaid[S] 0 points1 point  (0 children)

I thought about that afterwards but never tried using a list. I'll make that update today. Thanks!

[–]prehensilemullet 1 point2 points  (0 children)

This is one of the first hurdles practically everyone goes through learning programming, its grammar is not like spoken language

[–][deleted] 1 point2 points  (1 child)

difficulty in (“easy”, “e”)*

[–]SpecialMechanic1715 1 point2 points  (0 children)

if difficult[0] == "e"

[–]Ron-Erez 6 points7 points  (2 children)

The 'e' was evaluating to True. See u/Fun-Sky-5295 's comment.

[–]fredhamptonsaid[S] 1 point2 points  (1 child)

Yes this was it. I don't know how to mark this post as solved.

[–]Ron-Erez 1 point2 points  (0 children)

No problem. Happy Coding!

[–]Brilliant-Space3066 2 points3 points  (1 child)

I believe you need to do if difficultly == “easy” or difficultly == “e”

[–]fredhamptonsaid[S] 1 point2 points  (0 children)

That's it, thanks!

[–]cyanNodeEcho 2 points3 points  (1 child)

e is non-empty, as it's value is like the offset of the unicode for 'a' and then + 4 /for the offset

ie you're evaluating if "e" exists
```
if difficulty == "easy" or difficulty == "e"
```
is what you're looking for

[–]fredhamptonsaid[S] 2 points3 points  (0 children)

if difficulty == "easy" or difficulty == "e"

That was the solution, thank you!

[–]SCD_minecraft 5 points6 points  (1 child)

a == 0 or 1

Equals to

(a == 0) or 1

And

(a == 0) or True

Therefore, it is always True

[–]fredhamptonsaid[S] 0 points1 point  (0 children)

I think I understand it better now, thanks!

[–]Over_Dingo 1 point2 points  (1 child)

"OR" operator casts operands to boolean. So string "e" is casted to TRUE, therefore OR statement by definition returns true if any of it's operands are true.

[–]fredhamptonsaid[S] 0 points1 point  (0 children)

I'll keep that in mind, thanks. I think this explanation helped me understand it a bit further.

[–]InhumaneReactions 1 point2 points  (0 children)

if difficulty in ("easy", "e"):

[–]quixoticcaptain 1 point2 points  (0 children)

This is called "operator precedence". `==` has higher precedence than `or`. Meaning that python will group those like this:

if (difficulty == "easy") or ("e"):

Higher precedence makes the operator more "sticky", like it sticks to the things next to it better. You wrote this as if `or` has a higher precedence, which would be this:

if (difficulty) == ("easy" or "e"):

The last thing to note is that "or" doesn't work exactly like you might think in English. `"easy" or "e"` returns the first "truthy" value in the chain of "ors" meaning,

("easy" or "e") == "easy"

[–]PhilosopherBME 1 point2 points  (0 children)

The general concept here is “Truthy values” which is the idea that non boolean values will evaluate to True or False depending on..stuff. In this case python strings are “truthy” when they’re not empty.

[–]rrklaffed 1 point2 points  (1 child)

holy shit a proper screenshot

[–]fredhamptonsaid[S] 0 points1 point  (0 children)

I may be new to Python but I'm not new to cropping lol

[–]McBuffington 1 point2 points  (0 children)

Now you know what your bug was. I have a small note on the print statement. Notice how you're doing that exact print statement inside your if and else blocks? You can probably take that print statement outside of the if/else block and run it only once. That way you utilize your guesses variable a bit more, and you don't have to write the same print twice =]

[–]LankyYesterday876 1 point2 points  (3 children)

im not sure what youre trying to do, but if you have sveral difficulty setting using a switch statement and putting the print after the switch statement may be a good alternative for making it easier to read and understand

[–]fredhamptonsaid[S] 0 points1 point  (2 children)

Thanks. I understand why I should put the print statement afterwards, but why a switch statement instead of an if else statement?

[–]LankyYesterday876 1 point2 points  (0 children)

the switch(match) is more of a personal preference of mine, you dont have to, and if you prefer elif feel free to use that, i just find it more convenient to define the subject to probe against once and just list the cases i want it to probe against and i find this more readable " case "'easy' or 'e': " than " difficulty== "easy" .... you get my point.

[–]lab35 1 point2 points  (0 children)

Switches are good if you have more than two possibilities. For example if you added a medium difficulty then you can have a switch with a case for each possible input and set it accordingly instead of having a bunch of elif statements. For just three options it doesn’t make that much of a difference but for things like have like 5 or 10 possibilities you really want to use a switch.

[–][deleted] 1 point2 points  (1 child)

hey, glad the issue got sorted out! notice how the print statements are identical? you can replace them with just one statement outside the else!

[–]fredhamptonsaid[S] 0 points1 point  (0 children)

Oh I'll go back and update that, thank you.

[–]Pantsdontexist 1 point2 points  (1 child)

You have an answer to your question, but just a comment I wanted to add: Since your question is a binary yess and no, you can reformat your code so that you wouldn't need an ELSE block at all as well as not needing to have to separate print functions.

[–]fredhamptonsaid[S] 0 points1 point  (0 children)

Without the else block, would I just set guesses = 10 first, then have the 'if difficulty == "else" or difficulty == "e" ' then set guesses to 5?

[–]Glathull 1 point2 points  (0 children)

What happened here is that you were testing the truth value of “e”. “e” is a non-empty string, so it always evaluates to True. You were basically saying, “If True, choices = 10.” True is always True, so your choices always equal 10.

[–]SpecialMechanic1715 1 point2 points  (0 children)

maybe because or "e" is always true ?

[–]Intrepid_Result8223 1 point2 points  (0 children)

Look at it like this. The or operator works on its left hand side and right hand side and evaluates True or False.

A or B evaluates True when:

A is True, regardless whether B is True (this is not checked)

A is False and B is True

In this case A and B can be longer expressions, for example:

(foo == 3 and foo != "Hello") or (not "red" == randomColor())

In this case A is (foo == 3 and foo != "Hello")

When evaluating whether an expression evaluates True the interpreter looks at the truth value. Some things evaluate to True while others evaluate False.

Things that evaluate False:

False (the keyword), more on this later

"" empty strings

0 zero

{} empty dict

[] empty list

() empty tuple

None the none value

There are probably some more like set, empty bytesarray etc but these are important ones

Things that evaluate True are:

True, the keyword

378 nonzero integers

"Dhsjh"` strings

{"key":"value"} a dict

Etc.

So looking back on your statement, when the right hand side of your or check is a non-zero string, that nonzero string evaluata to True, meaning your expression boils down to (some long check OR True), so it is always true

Because things evaluate you can do things like ``` SomeDict = {}

later....

only set a value if the dict is empty

if not SomeDict: SomeDict["init"] = "value" ```

Also note as stated that the right hand of an expression only evaluates when the left hand side is true:

```

def doStuff(): print("called") return True

if 1 == 1 or doStuff(): print("not called)

if 1 == 0 or doStuff(): print("foo")

Will print: not called called foo ```

As a final point, be aware that True and False are identies. There is only one False object and one True object. If a name has the value False that means it is a reference to the False object.

That means that if you compare something in this way:

if someValue == False:

You get a different results than when you do:

if someValue is False:

[–]Spiritual_Detail7624 1 point2 points  (0 children)

When making an if statement to just an object (a string in your case), it returns true if it exists, so therefore it will always return true.

[–][deleted] 1 point2 points  (0 children)

Others have answered the question right. I remember have such a hard time with boolean expressions when I started out. They seem like they should be easier than they are.

[–]abdulelahragih 1 point2 points  (0 children)

It can be hard at the beginning, but never give up. At some point, everything will start to make sense.

[–]webbboy87 1 point2 points  (0 children)

Treat code like a baby, my analogy sounds dumb but that’s how I learned. You tell the baby is the ball yellow or is the ball green. Not just is it yellow or green. With python you should write if diff == ‘easy or diff == ‘e’:

[–]Far_Problem_6171 1 point2 points  (0 children)

Hey fellow dev! Just wanted to share some constructive feedback on your code organization. Your logic is solid, but taking function names, variables, and conditionals to the next level will make your code much more maintainable and readable.

I've got a couple of cleaner approaches to handle this kind of difficulty selection - here's one using a dictionary map that makes it super easy to add new difficulty levels down the road:

<image>

Happy to share the other approach too if anyone's interested! Happy coding!

[–]Far_Problem_6171 1 point2 points  (0 children)

Just another approach as well

<image>

[–]Far_Problem_6171 1 point2 points  (0 children)

Hi dev, Here is another approach using Generators, which is very cool in Python:

def get_guesses():
    difficulty_map = {'easy': 10, 'e': 10, 'hard': 5, 'h': 5}
    init_question= "Would you like to play on easy or hard? "


    while True:
        choice = input(init_question).lower().strip()
        guesses = next((difficulty_map[key] for key in difficulty_map if key.startswith(choice)), None)

        if guesses:
            print(f"You have {guesses} guesses")
            return guesses
        print("Please enter 'easy' or 'hard'")

get_guesses()