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

  1. Написание кода на REPL.
  2. Использование объектов в строковом форматировании или интерполяции.

С Юлией работает тихо из коробки. Определим точечный объект на обоих языках. Это для Юли.

struct Point
  x::Float64
  y::Float64
end

А это для питона

class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

Если мы создадим экземпляр в Julia, он по умолчанию будет печатать объект, как вы написали бы для создания объекта с использованием синтаксиса Julia:

julia> p = Point(10, 20)
Point(10.0, 20.0)

В Python нам не так повезло. Вместо этого мы получаем:

python> p = Point(10, 20)                                                           

python> p                                                                           
<__main__.Point object at 0x108a1f940>

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

class Point:
   def __init__(self, x: float, y: float): 
       self.x = x
       self.y = y
   
   def __repr__(self):
       f"Point({self.x}, {self.y})"

Префикс f является частью современного подхода Python к интерполяции строк, называемого f-string, который я обсуждаю здесь. В любом случае точки теперь можно отображать в REPL:

python> p = Point(10, 3)                                                            

python> p                                                                           
Point(10, 3)

python> str(p)                                                                        
'Point(10, 3)'

Вы можете видеть, что при использовании с форматированием строк это выглядит так же. Если вам нужно другое представление для использования с форматированием строк, вы должны реализовать метод __str__. Измените класс Point, чтобы включить это определение:

def __str__(self):
       f"({self.x}, {self.y})"

Теперь, когда вы получите этот результат:

python> str(p)                                                                        
'(10, 3)'

Для любого читателя Python, интересующегося Джулией, логика совершенно иная. Вы реализуете одну функцию show.

show(io::IO, p::Point) = print(io, "($(p.x), $(p.y))")

Если вы хотите поддерживать разные представления для разных типов MIME, вы можете зарегистрировать функцию для каждого типа следующим образом:

function show(io::IO, ::MIME"text/json", p::Point)
    print(io, "{x = $(p.x), y = $(p.y)}")
end

function show(io::IO, ::MIME"text/plain", p::Point)
    print(io, "Point($(p.x), $(p.y))")
end

Затем мы можем отобразить точку в разных форматах:

julia> display("text/json", p)
{x = 3.0, y = 4.0}
julia> display("text/json", p)
{x = 3.0, y = 4.0}

Если вместо вывода на стандартный вывод вы хотите получить строковое представление, вы можете использовать функцию repr. Обе функции используют show.

julia> repr("text/plain", p)
"Point(3.0, 4.0)"

julia> repr("text/json", p)
"{x = 3.0, y = 4.0}"

Кроме того, можно предоставить определенный контекст для изменения способа отображения объекта.

function show(io::IO, ::MIME"text/plain", p::Point)
    if get(io, :compact, true)
        print(io, "($(p.x), $(p.y))")
    else
        print(io, "Point($(p.x), $(p.y))")        
    end
end

В этом случае мы изменили функцию show для text/plain, чтобы проверять пары ключ-значение, хранящиеся в контексте ввода-вывода.

julia> show(IOContext(stdout, :compact => false), MIME("text/plain"), p)
Point(3.0, 4.0)
julia> show(IOContext(stdout, :compact => true), MIME("text/plain"), p)
(3.0, 4.0)

julia> repr("text/plain", p, context=IOContext(stdout, :compact => true))
"(3.0, 4.0)"

julia> repr("text/plain", p, context=IOContext(stdout, :compact => false))
"Point(3.0, 4.0)"

В следующий раз мы сравним интерполяцию строк ​​в Julia и Python.