Не уверен в __str__ в списке в Python

Исходя из опыта работы с Java, я понимаю, что __str__ - это что-то вроде версии toString для Python (хотя я понимаю, что Python - более старый язык).

Итак, я определил небольшой класс вместе с методом __str__ следующим образом:

class Node:

    def __init__(self, id):
        self.id = id
        self.neighbours = []
        self.distance = 0


    def __str__(self):
        return str(self.id)

Затем я создаю несколько его экземпляров:

uno = Node(1)    
due = Node(2)    
tri = Node(3)    
qua = Node(4)

Теперь ожидаемое поведение при попытке распечатать один из этих объектов состоит в том, что печатается связанное с ним значение. Такое тоже бывает.

print uno

дает

1

Но когда я делаю следующее:

uno.neighbours.append([[due, 4], [tri, 5]])

а потом

print uno.neighbours

я получил

[[[<__main__.Node instance at 0x00000000023A6C48>, 4], [<__main__.Node instance at 0x00000000023A6D08>, 5]]]

Где я ожидал

[[2, 4], [3, 5]]

Что мне не хватает? И что в противном случае достойное передергивания вещи я делаю? :)


person Christofer Ohlsson    schedule 16.09.2012    source источник
comment
Забавно, как это было закрыто через 7 лет после того, как об этом спросили.   -  person RW77    schedule 06.06.2021


Ответы (8)


У Python есть два разных способа преобразовать объект в строку: str() и repr(). Для печати объекта используется str(); при печати списка, содержащего объект, str() используется для самого списка, но реализация list.__str__() вызывает repr() для отдельных элементов.

Значит, вы также должны перезаписать __repr__(). Просто

__repr__ = __str__

в конце класса body будет делать свое дело.

person Sven Marnach    schedule 16.09.2012
comment
Поскольку __repr__() вызывается, когда нет __str__(), достаточно определить __repr__(). - person glglgl; 19.11.2015
comment
__repr__ не следует безоговорочно устанавливать на __str__. __repr__ должен создать представление, которое должно выглядеть как действительное Выражение Python, которое можно использовать для воссоздания объекта с тем же значением. В этом случае это будет скорее Node(2), чем 2. - person trapicki; 11.02.2018
comment
@trapicki Верно, хотя это скорее рекомендация, чем требование. OP хотел, чтобы вывод списка, упомянутый в вопросе, выглядел как [[2, 4], [3, 5]], и это то, что я ответил. Типы довольно часто отклоняются от указанной вами рекомендации, например Типы с плавающей запятой Numpy просто показывают числа как представления, а не numpy.float64(42.0), что может стать довольно подробным в списках. - person Sven Marnach; 11.02.2018
comment
Я не эксперт по питону, но в этом случае eval (repr (obj)) не вернет фактический узел. Ответ @tapicki немного ближе к истине, но в нем отсутствует существенная часть, запрошенная OP. Кто бы ни читал этот ответ, возможно, захочется проверить quora.com/What-does-repr -метод-среднее - person user3081519; 18.04.2018

Из-за безграничного превосходства Python над Java в Python есть не одна, а две операции toString.

Один __str__, другой __repr__

__str__ вернет удобочитаемую строку. __repr__ вернет внутреннее представление.

__repr__ можно вызывать для объекта, вызывая repr(obj) или используя обратные кавычки `obj`.

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

person Hans Then    schedule 16.09.2012
comment
Обратите внимание, что обратные кавычки как сокращение для repr() в Python 3. Незначительные придирки: ваше первое предложение может быть неправильно истолковано теми, кто воспринимает вещи слишком серьезно. - person Steven Rumbalski; 16.09.2012
comment
Я понимаю, что ищу возможность оскорбить юмористических людей. Я думал, что не перешел черту. Как вы думаете, мне следует отредактировать свой пост? - person Hans Then; 16.09.2012
comment
Это призыв к суждению. Возможно нет. Я просто подумал, что укажу на это. - person Steven Rumbalski; 16.09.2012
comment
Часто бывает трудно отличить иронию от сарказма, что может объяснить, почему ваш приемлемый в остальном ответ не получил большого количества голосов. Комментарий о юмористическом осуждении, вероятно, тоже никому не помог. - person martineau; 03.02.2014
comment
@martineau - Нет, их довольно легко различить. В любом случае, я думаю, что это отличный ответ. Я считаю, что юмор должен оставаться законным, и я поддержал его только из-за вашего комментария. Спасибо! - person RW77; 06.06.2021
comment
@ RW77: Их действительно бывает трудно различить, потому что сарказм - это форма иронии, и никто не увидит, как вы улыбаетесь в полном тексте ответа - плюс Stackoverlow - это не юмористический блог. ;¬) - person martineau; 06.06.2021

Он предоставляет удобочитаемую версию вывода, а не «объект»: Пример:

class Pet(object):

    def __init__(self, name, species):
        self.name = name
        self.species = species

    def getName(self):
        return self.name

    def getSpecies(self):
        return self.species

    def Norm(self):
        return "%s is a %s" % (self.name, self.species)

if __name__=='__main__':
    a = Pet("jax", "human")
    print a 

возвращается

<__main__.Pet object at 0x029E2F90>

в то время как код с "str" возвращает что-то другое

class Pet(object):

    def __init__(self, name, species):
        self.name = name
        self.species = species

    def getName(self):
        return self.name

    def getSpecies(self):
        return self.species

    def __str__(self):
        return "%s is a %s" % (self.name, self.species)

if __name__=='__main__':
    a = Pet("jax", "human")
    print a 

возвращает:

jax is a human
person jax    schedule 12.01.2016
comment
Красиво и понятно! - person AsheKetchum; 06.10.2017

Ответ на вопрос

Как указано в другом ответе и как вы можете прочитать в PEP 3140, str на list вызовах для каждого элемента __repr__. С этой частью ничего не поделаешь.

Если вы реализуете __repr__, вы получите что-то более информативное, но при правильной реализации не совсем то, что вы ожидали.

Правильная реализация

Быстрое, но неправильное решение - присвоить псевдониму __repr__ __str__.

__repr__ не следует устанавливать в __str__ безусловно. __repr__ должен создать представление, которое должно выглядеть как допустимое выражение Python, которое можно использовать для воссоздания объекта с тем же значением. В данном случае это будет скорее Node(2), чем 2.

Правильная реализация __repr__ позволяет воссоздать объект. В этом примере он также должен содержать другие важные элементы, такие как neighours и distance.

неполный пример:

class Node:

    def __init__(self, id, neighbours=[], distance=0):
        self.id = id
        self.neighbours = neighbours
        self.distance = distance


    def __str__(self):
        return str(self.id)


    def __repr__(self):
        return "Node(id={0.id}, neighbours={0.neighbours!r}, distance={0.distance})".format(self)
        # in an elaborate implementation, members that have the default
        # value could be left out, but this would hide some information


uno = Node(1)    
due = Node(2)    
tri = Node(3)    
qua = Node(4)

print uno
print str(uno)
print repr(uno)

uno.neighbours.append([[due, 4], [tri, 5]])

print uno
print uno.neighbours
print repr(uno)

Примечание: print repr(uno) вместе с правильной реализацией __eq__ и __ne__ или __cmp__ позволит воссоздать объект и проверить равенство.

person trapicki    schedule 11.02.2018

Что ж, __str__ методы контейнерных объектов будут использовать repr для своего содержимого, а не str. Таким образом, вы можете использовать __repr__ вместо __str__, поскольку в результате вы используете идентификатор.

person Community    schedule 16.09.2012

__str__ вызывается только тогда, когда от объекта требуется строковое представление.

Например str(uno), print "%s" % uno или print uno

Однако есть еще один волшебный метод, называемый __repr__, это представление объекта. Если вы явно не преобразуете объект в строку, используется представление.

Если вы сделаете это uno.neighbors.append([[str(due),4],[str(tri),5]]), он будет делать то, что вы ожидаете.

person Burhan Khalid    schedule 16.09.2012

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

Об этом свидетельствует то, что вы видите в своем выводе.

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

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

person Tommy W.    schedule 16.02.2017

print self.id.__str__() подойдет вам, хотя и не так полезен для вас.

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

print self._grid.__str__()

def __str__(self):
    """
    Return a string representation of the grid for debugging.
    """
    grid_str = ""
    for row in range(self._rows):
        grid_str += str( self._grid[row] )
        grid_str += '\n'
    return grid_str
person user3295570    schedule 12.09.2014