Введение
Примеры
мотивация
Итак, вы только что создали свой первый класс в 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)