Документация по Python

Строковые представления экземпляров класса: методы __str__ и __repr__

В: Документация по Python

Введение

Примеры

мотивация

Итак, вы только что создали свой первый класс в Python, аккуратный маленький класс, который инкапсулирует игральную карту:

 class Card:
    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

 

В другом месте вашего кода вы создаете несколько экземпляров этого класса:

 ace_of_spades = Card('Spades', 1)
four_of_clubs = Card('Clubs',  4)
six_of_hearts = Card('Hearts', 6)

 

Вы даже создали список карт, чтобы представить «руку»:

 my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]

 

Теперь, во время отладки, вы хотите посмотреть, как выглядит ваша рука, поэтому вы делаете то, что естественно, и пишете:

 print(my_hand)

 

Но то, что вы получите, - это кучка бреда:

 [<__main__.Card instance at 0x0000000002533788>, 
 <__main__.Card instance at 0x00000000025B95C8>, 
 <__main__.Card instance at 0x00000000025FF508>]

 

В замешательстве вы пытаетесь просто напечатать одну карту:

 print(ace_of_spades)

 

И снова, вы получите этот странный вывод:

 <__main__.Card instance at 0x0000000002533788>

 

Не бойся Мы собираемся это исправить.

Однако сначала важно понять, что здесь происходит. Когда вы писали print(ace_of_spades) вы сказали Python вы хотели, чтобы напечатать информацию о Card , например ваш код вызывающего ace_of_spades . И, честно говоря, это так.

Этот вывод состоит из двух важных бита: type объекта и объекта id . Одна вторая часть (число шестнадцатеричного) достаточно , чтобы однозначно идентифицировать объект в момент print вызова. [1]

Что на самом деле произошло, так это то, что вы попросили Python «выразить словами» суть этого объекта и затем показать его вам. Более явная версия того же механизма может быть:

 string_of_card = str(ace_of_spades)
print(string_of_card)

 

В первой строке, вы пытаетесь превратить Card экземпляр в строку, а во втором вы его отображения.

Эта проблема

Проблема Вы сталкиваетесь возникает из - за того , что, в то время как вы сказали , Python все , что требовалось знать о Card классе для вас , чтобы создать карты, вы не сказали ему , как вы хотели Card экземпляров должны быть преобразованы в строки.

И так как он не знал, когда вы (неявно) писал str(ace_of_spades) , он дал вам то , что вы видели, родовое представление Card экземпляра.

Решение (часть 1)

Но мы можем сказать , Python , как мы хотим , чтобы экземпляры наших пользовательских классов , которые будут преобразованы в строки. И то , как мы делаем это с __str__ «Dunder» (для двойного подчеркивания) или метода «магического».

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

Рассмотрим следующий, обновленную версию нашего Card класса:

 class Card:
    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    def __str__(self):
        special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

        card_name = special_names.get(self.pips, str(self.pips))

        return "%s of %s" % (card_name, self.suit)

 

Вот, сейчас мы определили __str__ метод на наших Card класса , который, после того, как простой поиск в словаре для фигурных карт, возвращает строку , отформатированную однако мы решаем.

(Обратите внимание , что «возвращает» жирный шрифт здесь, чтобы подчеркнуть важность возвращения строки, а не просто напечатать его. Печать может показаться на работу, но тогда вам придется карта распечатывается , когда вы сделали что - то вроде str(ace_of_spades) , даже без вызова функции печати в главной программе. Таким образом , чтобы быть ясно, убедитесь , что __str__ возвращает строку.).

__str__ метод представляет собой метод, так что первый аргумент будет self , и это не должно ни принимать, ни быть переданы аргументы вывода дополнительных.

Возвращаясь к нашей проблеме отображения карты в более удобной для пользователя форме, если мы снова запустим:

 ace_of_spades = Card('Spades', 1)
print(ace_of_spades)

 

Мы увидим, что наш вывод намного лучше:

 Туз пик 

Так здорово, мы закончили, верно?

Ну просто , чтобы покрыть наши базы, давайте двойную проверку , что мы решили первый вопрос , который мы столкнулись, печать списка Card экземпляров, в hand .

Итак, мы перепроверили следующий код:

 my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]
print(my_hand)

 

И, к нашему удивлению, мы снова получаем эти забавные шестнадцатеричные коды:

 [<__main__.Card instance at 0x00000000026F95C8>, 
 <__main__.Card instance at 0x000000000273F4C8>, 
 <__main__.Card instance at 0x0000000002732E08>]

 

В чем дело? Мы сказали , Python , как мы хотели , чтобы наши Card экземпляров , которые будут отображаться, почему он , по- видимому , кажется, забыли?

Решение (часть 2)

Ну, механизм закулисности немного отличается, когда Python хочет получить строковое представление элементов в списке. Оказывается, Python не заботится о __str__ для этой цели.

Вместо этого, он ищет другой метод, __repr__ , и если это не найдено, он возвращается на «шестнадцатеричном вещь». [2]

То есть ты говоришь, что мне нужно сделать два метода, чтобы сделать то же самое? Один, когда я хочу , чтобы print свою карточку сама по себе , а другой , когда он в какой - то контейнер?

Нет, но сначала давайте посмотрим на то , что наш класс был бы, если бы мы должны были реализовать как __str__ и __repr__ методы:

 class Card:
    special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    def __str__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s (S)" % (card_name, self.suit)

    def __repr__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s (R)" % (card_name, self.suit)

 

Здесь, реализация этих двух методов __str__ и __repr__ точно так же, за исключением того, что различие между этими двумя методами, (S) , добавляется в строки , возвращаемых __str__ и (R) добавляется к строкам возвращаемых __repr__ .

Обратите внимание , что так же , как наш __str__ метод, __repr__ не принимает никаких аргументов и возвращает строку.

Теперь мы можем видеть, какой метод отвечает за каждый случай:

 ace_of_spades = Card('Spades', 1)
four_of_clubs = Card('Clubs',  4)
six_of_hearts = Card('Hearts', 6)

my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]

print(my_hand)           # [Ace of Spades (R), 4 of Clubs (R), 6 of Hearts (R)]

print(ace_of_spades)     # Ace of Spades (S)

 

Как было покрыто, то __str__ метод вызывается , когда мы проходили мимо нашей Card экземпляра print и __repr__ метод вызывается , когда мы проходили мимо список наших экземпляров для print .

На этом этапе стоит отметить, что, как мы можем явно создать строку из экземпляра пользовательского класса с использованием str() , как мы делали раньше, мы можем также явно создать строковое представление нашего класса с встроенной функцией называется repr() .

Например:

 str_card = str(four_of_clubs)
print(str_card)                     # 4 of Clubs (S)

repr_card = repr(four_of_clubs)
print(repr_card)                    # 4 of Clubs (R)

 

И кроме того, если определено, мы могли бы назвать методы напрямую (хотя это кажется немного неясным и ненужным):

 print(four_of_clubs.__str__())     # 4 of Clubs (S)

print(four_of_clubs.__repr__())    # 4 of Clubs (R)

 

О тех дублированных функциях ...

Разработчики Python поняли, в случае , если вы хотели идентичные строки должны быть возвращены из str() и repr() вы могли бы функционально дублирующие методы - то , что никто не любит.

Таким образом, вместо этого существует механизм для устранения необходимости в этом. Один я пробрался к тебе до этого момента. Оказывается, что если класс реализует __repr__ метод , но не __str__ метод, и вы передаете экземпляр этого класса str() (независимо от того , явно или неявно), Python будет Откат на вашем __repr__ реализации и использовать.

Так, чтобы было ясно, рассмотрим следующую версию Card класса:

 class Card:
    special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    def __repr__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s" % (card_name, self.suit)

 

Обратите внимание , эта версия только реализует __repr__ метод. Тем не менее, вызовы str() результат в удобной версии:

 print(six_of_hearts)            # 6 of Hearts  (implicit conversion)
print(str(six_of_hearts))       # 6 of Hearts  (explicit conversion)

 

как это делают вызовы repr() :

 print([six_of_hearts])          #[6 of Hearts] (implicit conversion)
print(repr(six_of_hearts))      # 6 of Hearts  (explicit conversion)

 

Резюме

Для того , чтобы вам расширить возможности ваших экземпляров классов , чтобы «показать себя» в удобных способах, вы хотите , чтобы рассмотреть вопрос об осуществлении по крайней мере вашего класс __repr__ метода. Если память служит, во время разговора Raymond Hettinger сказал , что обеспечение реализации классов __repr__ является одной из первых вещей , которые он смотрит на Python, делая анализ кода, и теперь должно быть ясно , почему. Количество информации , которую вы могли бы добавить к отладке заявления, отчеты об ошибках, или лог - файлы с помощью простого метода является подавляющим , когда по сравнению с ничтожными, и часто менее чем полезно (тип, идентификатор) информации , которая задается по умолчанию.

Если вы хотите , различные представления для того, когда, например, внутри контейнера, вы хотите реализовать как __repr__ и __str__ методы. (Подробнее о том, как вы можете использовать эти два метода по-разному ниже).

Реализованы оба метода, стиль eval-round-trip __repr __ ()

 class Card:
    special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    # Called when instance is converted to a string via str()
    # Examples:
    #   print(card1)
    #   print(str(card1)
    def __str__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s" % (card_name, self.suit)

    # Called when instance is converted to a string via repr()
    # Examples:
    #   print([card1, card2, card3])
    #   print(repr(card1))
    def __repr__(self):
        return "Card(%s, %d)" % (self.suit, self.pips) 

Синтаксис

Параметры

Примечания

Еще от кодкамп
Замечательно! Вы успешно подписались.
Добро пожаловать обратно! Вы успешно вошли
Вы успешно подписались на кодкамп.
Срок действия вашей ссылки истек.
Ура! Проверьте свою электронную почту на наличие волшебной ссылки для входа.
Успех! Ваша платежная информация обновлена.
Ваша платежная информация не была обновлена.