Bagaimana cara membuat representasi string khusus untuk objek kelas?

202

Pertimbangkan kelas ini:

class foo(object):
    pass

Representasi string default terlihat seperti ini:

>>> str(foo)
"<class '__main__.foo'>"

Bagaimana saya bisa menjadikan tampilan ini sebagai string khusus?

Björn Pollex
sumber

Jawaban:

272

Menerapkan __str__()atau __repr__()dalam metaclass kelas.

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object):
  __metaclass__ = MC

print C

Gunakan __str__jika Anda bermaksud pengerasan yang dapat dibaca, gunakan __repr__untuk representasi yang tidak ambigu.

Ignacio Vazquez-Abrams
sumber
2
Saya ingin membuat semacam dekorator kelas, jadi saya dapat dengan mudah mengatur representasi string khusus untuk kelas saya tanpa harus menulis metaclass untuk masing-masing. Saya tidak terlalu mengenal metaclasses Python, jadi bisakah Anda memberi saya petunjuk di sana?
Björn Pollex
Sayangnya ini tidak dapat dilakukan dengan dekorator kelas; itu harus diatur ketika kelas didefinisikan.
Ignacio Vazquez-Abrams
10
@ Space_C0wb0y: Anda bisa menambahkan string seperti _representationke tubuh kelas dan return self._representationdalam __repr__()metode metaclass.
Sven Marnach
@ BjörnPollex Anda mungkin bisa melakukan ini dengan dekorator, tapi saya berharap Anda harus berjuang dengan banyak seluk-beluk bahasa. Dan bahkan jika Anda melakukannya Anda masih terikat untuk menggunakan metaclass dalam satu atau lain cara karena Anda tidak ingin menggunakan default __repr__untuk mewakili C. Alternatif untuk memiliki _representationanggota adalah dengan membuat pabrik metaclass yang menghasilkan metaclass dengan tepat __repr__(ini bisa menyenangkan jika Anda sering menggunakan ini).
Menjulang
2
@ThomasLeonard: stackoverflow.com/questions/39013249/metaclass-in-python3-5
Ignacio Vazquez-Abrams
22
class foo(object):
    def __str__(self):
        return "representation"
    def __unicode__(self):
        return u"representation"
Andrey Gubarev
sumber
10
Ini mengubah representasi string untuk instanceskelas, bukan untuk kelas itu sendiri.
tauran
maaf, tidak melihat bagian kedua dari posting Anda. Gunakan metode di atas.
Andrey Gubarev
6
@RobertSiemer Mengapa? Meskipun jawabannya tidak secara khusus menargetkan pertanyaan OP, itu tetap membantu. Itu membantu saya. Dan sekilas, saya tidak melihat ada pertanyaan yang meminta implementasi contoh. Jadi mungkin orang mendarat di halaman ini terlebih dahulu.
akinuri
14

Jika Anda harus memilih di antara __repr__atau __str__pergi untuk yang pertama, sebagai __str__panggilan implementasi standar __repr__ketika itu tidak ditentukan.

Contoh Vector3 Kustom:

class Vector3(object):
    def __init__(self, args):
        self.x = args[0]
        self.y = args[1]
        self.z = args[2]

    def __repr__(self):
        return "Vector3([{0},{1},{2}])".format(self.x, self.y, self.z)

    def __str__(self):
        return "x: {0}, y: {1}, z: {2}".format(self.x, self.y, self.z)

Dalam contoh ini, reprmengembalikan lagi string yang dapat langsung dikonsumsi / dieksekusi, sedangkan stryang lebih berguna sebagai hasil debug.

v = Vector3([1,2,3])
print repr(v)    #Vector3([1,2,3])
print str(v)     #x:1, y:2, z:3
pengguna1767754
sumber
1
Meskipun poin Anda tentang __repr__vs __str__benar, ini tidak menjawab pertanyaan aktual, yaitu tentang objek kelas, bukan instance.
Björn Pollex
Terima kasih atas umpan baliknya, sepenuhnya mengawasi itu. Biarkan saya meninjau jawaban saya.
user1767754
Saya pikir Anda implementasi untuk repr dan str bertukar.
jspencer
4

Jawaban yang disetujui Ignacio Vazquez-Abrams cukup tepat. Namun, dari generasi Python 2. Pembaruan untuk Python 3 saat ini adalah:

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object, metaclass=MC):
    pass

print(C)

Jika Anda menginginkan kode yang berjalan melintasi Python 2 dan Python 3, keenam modul tersebut telah Anda bahas:

from __future__ import print_function
from six import with_metaclass

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(with_metaclass(MC)):
    pass

print(C)

Akhirnya, jika Anda memiliki satu kelas yang Anda ingin memiliki repr statis kustom, pendekatan berbasis kelas di atas berfungsi dengan baik. Tetapi jika Anda memiliki beberapa, Anda harus membuat metaclass yang mirip dengan MCmasing-masing, dan itu bisa melelahkan. Dalam hal itu, mengambil metaprogramming Anda satu langkah lebih jauh dan membuat pabrik metaclass membuat segalanya sedikit lebih bersih:

from __future__ import print_function
from six import with_metaclass

def custom_class_repr(name):
    """
    Factory that returns custom metaclass with a class ``__repr__`` that
    returns ``name``.
    """
    return type('whatever', (type,), {'__repr__': lambda self: name})

class C(with_metaclass(custom_class_repr('Wahaha!'))): pass

class D(with_metaclass(custom_class_repr('Booyah!'))): pass

class E(with_metaclass(custom_class_repr('Gotcha!'))): pass

print(C, D, E)

cetakan:

Wahaha! Booyah! Gotcha!

Metaprogramming bukanlah sesuatu yang biasanya Anda butuhkan setiap hari — tetapi ketika Anda membutuhkannya, itu benar-benar tepat sasaran!

Jonathan Eunice
sumber
0

Hanya menambahkan ke semua jawaban yang bagus, versi saya dengan dekorasi:

from __future__ import print_function
import six

def classrep(rep):
    def decorate(cls):
        class RepMetaclass(type):
            def __repr__(self):
                return rep

        class Decorated(six.with_metaclass(RepMetaclass, cls)):
            pass

        return Decorated
    return decorate


@classrep("Wahaha!")
class C(object):
    pass

print(C)

stdout:

Wahaha!

Sisi bawah:

  1. Anda tidak dapat mendeklarasikan Ctanpa kelas super (tidak class C:)
  2. Ccontoh akan menjadi contoh dari beberapa derivasi aneh, jadi mungkin ide yang bagus untuk menambahkan __repr__contoh juga.
Aviv Goll
sumber