2

I was looking into python infix operators and I found this: Python: defining my own operators?

It was asked a very long time ago but I can't find anything more recent. It seems like more of a hack than I'd normally be comfortable doing. Using that hack, I've transformed my code:

if __name__ == '__main__':
    of = Infix(lambda x, y: Card(Value[x], Suit[y]))

    card1 = 'EIGHT' | of | 'DIAMONDS'
    card2 = 'THREE' | of | 'SPADES'
    card3 = 'QUEEN' | of | 'SPADES'

    assert card1.suit != card2.suit
    assert card3.suit == card2.suit

    assert ('NINE' | of | 'HEARTS').beats('EIGHT' | of | 'HEARTS')
    assert ('NINE' | of | 'HEARTS').beats('EIGHT' | of | 'HEARTS', trump=Suit.HEARTS)
    assert ('NINE' | of | 'HEARTS').beats('EIGHT' | of | 'HEARTS', trump=Suit.DIAMONDS)

    assert not Card(Value.NINE, Suit.HEARTS).beats(Card(Value.TEN, Suit.HEARTS))
    assert not Card(Value.NINE, Suit.HEARTS).beats(Card(Value.TEN, Suit.HEARTS), trump=Suit.HEARTS)
    assert not Card(Value.NINE, Suit.HEARTS).beats(Card(Value.TEN, Suit.HEARTS), trump=Suit.DIAMONDS)

    assert not Card(Value.NINE, Suit.DIAMONDS).beats(Card(Value.EIGHT, Suit.HEARTS))
    assert not Card(Value.NINE, Suit.DIAMONDS).beats(Card(Value.EIGHT, Suit.HEARTS), Suit.HEARTS)
    assert not Card(Value.NINE, Suit.DIAMONDS).beats(Card(Value.EIGHT, Suit.HEARTS), Suit.CLUBS)

You can see what the code looks like with and without, and I think it's WAY more readable like this. Still, it's a hack, and I also am not a fan of accessing the enums by string. I am still considering:

EIGHT = Value.EIGHT
SPADES = Suit.SPADES

card = Card(EIGHT, SPADES)

I could even combine the two approaches and change the Infix to:

   of = Infix(lambda x,y: Card(x, y))

   card = EIGHT | of | SPADES

Is there a better way of doing the infix thing in Python 3? Is it worth doing at all?

EDIT:

    assert not (NINE | of | HEARTS).beats(TEN | of | HEARTS)
    assert not (NINE | of | HEARTS).beats(TEN | of | HEARTS, trump=HEARTS)
    assert not (NINE | of | HEARTS).beats(TEN | of | HEARTS, trump=DIAMONDS)

    assert not Card(NINE, DIAMONDS).beats(Card(EIGHT, HEARTS))
    assert not Card(NINE, DIAMONDS).beats(Card(EIGHT, HEARTS), trump=HEARTS)
    assert not Card(NINE, DIAMONDS).beats(Card(EIGHT, HEARTS), trump=CLUBS)

One comment may be suggesting that the first block is no better than the second. I can see the logic there. This includes the whole EIGHT = Value.EIGHT thing which I think is good. (Can you tell I come from compiled-languages-land?)

  • 2
    There is no better way of defining custom Infix operators for known types, but you could override existing for your own custom objects. However, I do not see this as a good fit for infix operator, this should just be a `tuple` or some close relative. It seems `of` in `8 | of | 'spades'` is not doing much more than `(8, 'spades')` would not handle well. – norok2 Dec 25 '19 at 20:22
  • I assume code blocks can't go in comments, so I edited the main post. Thanks for the response. Is that along the lines of what you meant? @norok2 – Andrew Pearce Dec 25 '19 at 20:30
  • essentially, yes, but see @KarlKnechtel answer to avoid the inelegant `EIGHT = Value.EIGHT` – norok2 Dec 25 '19 at 20:39

1 Answers1

0

Just let the class do the parsing, via a factory method:

class Card:
    # other stuff
    @staticmethod
    def fromstring(s):
        value, of, suit = s.split()
        return Card(Value[value], Suit[suit])
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153