Рубрики
Без рубрики

Придавая смысл магическим числам с перечислением Python

Быстрый маленький вкус модуля перечисления, чтобы показать, насколько он крут. Tagged с Python, стандартная библиотека.

Эта статья не супер в глубине. Я только что обнаружил крутой модуль в библиотеке Standard Python и хотел поделиться им, чтобы вызвать ваш интерес. Если вы столкнетесь с какими -либо проблемами и у вас есть какие -либо вопросы, я рад изо всех сил, чтобы помочь вам пройти через это!

Использование модуля Enum’s Python

Я работал над упражнением на Упражнения Это было связано с поиском лучшей покерной руки, и я оказался с множеством «волшебных ценностей», плавающих вокруг. Когда я говорю «магическую ценность», я имею в виду, что твердое кодируемая постоянная, которая должна иметь какое-то семантическое значение. В этом конкретном случае у меня были игровые карты, которые могли бы принять одно из 13 различных конкретных значений, и у некоторых из этих значений были имена. И Тогда У меня были покерные руки, которые получали оценку, и эти различные типы рук также имели внутренние ценности, где некоторые руки были лучше, чем другие.

Моя первая итерация была дополнительной волшебной и выглядела примерно так:

if self.is_straight() and self.is_flush():
    return 8
elif self.biggest_group() == 4:     # 4 of a Kind
    return 7
elif self.is_full_house():
    return 6
elif self.is_flush():
    return 5
elif self.is_straight():
    return 4
elif self.biggest_group() == 3:     # 3 of a kind
    return 3
elif self.is_two_pair():
    return 2
elif self.biggest_group() == 2:     # One pair
    return 1
else:                               # High Card
    return 0

Это было странно и заставило меня показывать код с кодовой точки зрения. Но я позволил этому пройти. Затем я написал этот код:

if value == "A":
    self.value = 14
elif value == "K":
    self.value = 13
elif value == "Q":
    self.value = 12
elif value == "J":
    self.value = 11
else:
    self.value = int(value)

Больше волшебных чисел! Я смутно помнил, что у Питона был enum Модуль в его стандартной библиотеке, и я также смутно помнил, что enums должны были быть полезны для ситуаций, когда у вас есть конкретные категории/типы, которые может принять значение. Итак, я провел некоторое исследование. Оказывается, они на самом деле даже круче, чем я думал.

enum Модуль имеет кучу разных типов enums Но, поскольку я много делал сортировки, сравнивая и относительный рейтинг, а также переходил между этими и нормальными числами, я выбрал Intenum Анкет Это просто означает, что enum Значения будут целыми. Но как это выглядит?

Вы можете определить перечисление с помощью функционального синтаксиса (так же, как вы создаете экземпляр класса или создать тип ждемца ), или вы можете создать его с помощью синтаксиса класса. Вы увидите оба в этой статье, по разным причинам.

Сначала я покажу функциональную оценку для покерных рук. Я иду по этому пути, потому что мне все равно, какие цифры. Я просто хочу прояснить относительный рейтинг.

from enum import IntEnum

Score = IntEnum('Score', [
    'HIGH_CARD',
    'PAIR',
    'TWO_PAIR',
    'THREE_OF_A_KIND',
    'STRAIGHT',
    'FLUSH',
    'FULL_HOUSE',
    'FOUR_OF_A_KIND',
    'STRAIGHT_FLUSH',
])

И это все, что нам нужно. Теперь мы можем увидеть их относительные ценности и сравнить их!

>>> Score.STRAIGHT.value
5
>>> Score.FOUR_OF_A_KIND > Score.THREE_OF_A_KIND
True
>>> Score.FLUSH < Score.HIGH_CARD
False

Это позволяет нам семантизировать (мое новое слово, спасибо!) Наш код выше:

if self.is_straight() and self.is_flush():
    return Score.STRAIGHT_FLUSH
elif self.biggest_group() == 4:
    return Score.FOUR_OF_A_KIND
elif self.is_full_house():
    return Score.FULL_HOUSE
elif self.is_flush():
    return Score.FLUSH
elif self.is_straight():
    return Score.STRAIGHT
elif self.biggest_group() == 3:
    return Score.THREE_OF_A_KIND
elif self.is_two_pair():
    return Score.TWO_PAIR
elif self.biggest_group() == 2:
    return Score.PAIR
else:
    return Score.HIGH_CARD

Намного более читаемо! Один из способов, которым вы знаете, что мы делаем что -то правильно, это то, что я смог удалить объяснения комментариев для некоторых из менее четких условий, не потеряв какую -либо читабельность вообще. Круто, верно?

Теперь давайте посмотрим на другой случай со значениями Face Card. Для этого я использовал синтаксис класса, потому что я хотел добавить немного больше функциональности. Подробнее об этом через минуту. Вот стартовый код.

class FaceCard(IntEnum):
    """Numeric values of face cards"""
    JACK = 11
    QUEEN = 12
    KING = 13
    ACE = 14

Теперь мы можем использовать его как обычный номер, но это Имен!

Входные данные были предоставлены как строки символов: "Ах" для Ace of Hearts, "8c" Для 8 клубов и т. Д. Я начал с определения такой карты:

class Card:
    def __init__(self, label):
        self.suit = label[-1]
        value = label[:-1]
        if value == "J":
            self.value = FaceCard.JACK
        elif value == "Q":
            self.value = FaceCard.QUEEN
        elif value == "K":
            self.value = FaceCard.KING
        elif value == "A":
            self.value = FaceCard.ACE
        else:
            self.value = int(value)

    def __lt__(self, other):
        return self.value < other.value

__init__ Метод инициализация экземпляра, а __lt__ Метод рассказывает, как эти объекты ведут себя вокруг < оператор. Теперь мы можем сравнить их так:

>>> king = Card("KS")  # King of Spades
>>> three = Card("3S")  # Three of Spades
>>> three < king
True

Несмотря на то, что ценность для короля на самом деле является Facecard. Король , он сравнивает использование его целочисленного значения, поэтому я могу относиться к нему как к нормальному числу для большинства вещей! Перечисления крутые!

Для получения дополнительной информации, Стандартные библиотечные документы действительно хороши.

Бонус: обратно в струны!

Я думал enum было круто. А потом я узнал, что мне нужно преобразовать значения моей карты обратно в их закодированные строки для вывода. Как выясняется, перечисления имеют два основных свойства: имя , что похоже на «ключ», и значение Анкет Проверьте это удивительность:

# The same FaceCard class as before:
class FaceCard(IntEnum):
    """Numeric values of face cards"""
    JACK = 11
    QUEEN = 12
    KING = 13
    ACE = 14

    # This is new:
    # Define a custom method to call when a case gets "stringified"
    def __str__(self):
        return self.name[0]

Теперь в нашем классе карт:

class Card:
    # ...
    def __str__(self):
        return str(self.value) + self.suit

И потому что мы определили пользовательский __str__ Метод для Facecard То, как мы это сделали, этот код будет работать независимо от того, является ли карта лицевой картой или нет!

>>> eight = Card("8H")
>>> ace = Card("AD")
>>> eight.value
8
>>> eight.suit
"H"
>>> ace.value
FaceCard.ACE
>>> ace.suit
"D"
>>> str(eight)
"8H"            # str(eight) == str(8) + "H"
>>> str(ace)
"AD"            # str(ace) == str(FaceCard.ACE) + "D"
                # str(FaceCard.ACE) == "ACE"[0] == "A"

Верно?? ПРАВИЛЬНО?!?! Я имею в виду, насколько элегантно вы можете получить?

Я хочу знать, как ты Используется enum до. Вы нашли аккуратный вариант использования? Есть ли другие драгоценные камни в enum модуль, которого я еще не обнаружил?

Оригинал: “https://dev.to/rpalo/giving-meaning-to-magic-numbers-with-python-enums-21od”