Chris
Chris

Reputation: 13

What is the order of operations for 'and' and 'or'?

In Python is this:

def blackjack_check(hand): # hand is a tuple
    winning_cards = [10,'Jack','Queen','King']
    if hand[0] in winning_cards and hand[1] == 'Ace':
        return True
    elif hand[0] == 'Ace' and hand[1] in winning_cards:
        return True
    else:
        return False

the same as this...?

def blackjack_check(hand): # hand is a tuple
    winning_cards = [10,'Jack','Queen','King']
    if (hand[0] in winning_cards and hand[1]=='Ace' or 
        hand[0] == 'Ace' and hand[1] in winning_cards):
        return True
    else:
        return False

Can I use the second code block instead of the first? It would eliminate an extra elif statement and it just seems cleaner. My concern is how the 'and' and 'or' operators work. Are the two 'and' comparisons separate and the 'or' compares them? Is there an order of operations for 'and' and 'or'? I ran the code and it works both ways but I want to make sure I understand exactly how the operators work.

Upvotes: 1

Views: 364

Answers (3)

jedwards
jedwards

Reputation: 30210

vaultah's answer addresses your actual question perfectly -- they deserve the upvote and checkmark.

But I think programming blackjack in every language you're learning is an excellent way to better understand it, so I threw together code to show a different way to implement the blackjack test. More for educational purposes than to actually answer your question:

# Note: sometimes an Ace should be 1, but when checking for blackjack you can always 
#   consider it 11
def card_value(c):
    if isinstance(c, int):
        return c
    elif c in ['Jack', 'Queen', 'King']:
        return 10
    elif c == 'Ace':
        return 11

def blackjack_check(hand):
    hand_value = sum(card_value(c) for c in hand)
    return hand_value == 21

print(blackjack_check((2, 10)))             # False
print(blackjack_check((10, 10)))            # False
print(blackjack_check((2, 'Ace')))          # False
print(blackjack_check(('King', 'Jack')))    # False
print(blackjack_check(('King', 'Ace')))     # True
print(blackjack_check(('Ace', 'Queen')))    # True

If I were to implement it today, Cards and Hands would be classes, and there'd be a Hand.is_blackjack() method, like:

import random

class Card:
    NAMES = {1: 'Ace', 11:'Jack', 12:'Queen', 13:'King'}
    def __init__(self, pips, suit):
        self.pips = pips
        self.suit = suit

    def __repr__(self):
        name = Card.NAMES.get(self.pips, "%d" % self.pips)
        return "%s of %s" % (name, self.suit)

    def value(self, ace_hi=True):
        # Handle Ace
        if self.pips == 1:
            return 11 if ace_hi else 1
        return min(self.pips, 10)

class Hand(list):
    def is_blackjack(self):
        hand_value = sum(c.value() for c in self)
        return hand_value == 21

CARDS = [Card(p,s) for p in range(1,14) for s in ['Spades', 'Hearts', 'Clubs', 'Diamonds']]

h = Hand(random.sample(CARDS, 2))

print("Hand:")
for c in h:
    print("  %s" % c)

print("Blackjack? %s" % h.is_blackjack())

Examples:

Hand:
  Jack of Spades
  Ace of Spades
Blackjack? True

Hand:
  Queen of Spades
  9 of Diamonds
Blackjack? False

Sorry for the silly non-answer, but these are just different ideas to think about. Don't worry if they're over your head, you'll get there.

Upvotes: 2

vaultah
vaultah

Reputation: 46533

Yes, the second code block is equivalent to the first one. According to the documentation, or has lower precedence than and. It means that the if statement is evaluated as

if ((hand[0] in winning_cards and hand[1] == 'Ace') or 
    (hand[0] == 'Ace' and hand[1] in winning_cards)):

which is what you want.

You could return the result of that boolean expression to shorten the code:

def blackjack_check(hand):
    winning_cards = [10, 'Jack', 'Queen', 'King']
    return (hand[0] in winning_cards and hand[1] == 'Ace' or 
            hand[0] == 'Ace' and hand[1] in winning_cards)

Upvotes: 4

user1084944
user1084944

Reputation:

The usual advice is if you don't know, then your readers might not know either, and it would be better for everybody if you use parentheses.

if ((hand[0] in winning_cards and hand[1]=='Ace') or 
    (hand[0] == 'Ace' and hand[1] in winning_cards)):

although you might try other formulations, such as

if (any(card == 'Ace' for card in hand) and
    any(card in winning_cards for card in hand)):

or writing a helper function and using

if hascard(hand, ('Ace',)) and hascard(hand, winning_cards):

Upvotes: 0

Related Questions