Sự khác biệt giữa __str__ và __Vpr__?


Câu trả lời:


2723

Alex tóm tắt tốt, nhưng đáng ngạc nhiên, là quá ngắn gọn.

Trước tiên, hãy để tôi nhắc lại những điểm chính trong bài của Alex :

  • Việc triển khai mặc định là vô ích (thật khó để nghĩ về điều đó sẽ không xảy ra, nhưng vâng)
  • __repr__ mục tiêu là rõ ràng
  • __str__ mục tiêu là có thể đọc được
  • Container __str__sử dụng các đối tượng chứa '__repr__

Thực hiện mặc định là vô ích

Điều này chủ yếu là một bất ngờ vì mặc định của Python có xu hướng khá hữu ích. Tuy nhiên, trong trường hợp này, có một mặc định __repr__sẽ hoạt động như sau:

return "%s(%r)" % (self.__class__, self.__dict__)

sẽ là quá nguy hiểm (ví dụ, quá dễ dàng để đi vào đệ quy vô hạn nếu các đối tượng tham chiếu lẫn nhau). Vì vậy, Python cảnh sát ra. Lưu ý rằng có một mặc định là đúng: nếu __repr__được xác định và __str__không, đối tượng sẽ hành xử như vậy __str__=__repr__.

Điều này có nghĩa, về mặt đơn giản: hầu hết mọi đối tượng bạn triển khai nên có chức năng __repr__có thể sử dụng để hiểu đối tượng. Việc triển khai __str__là tùy chọn: thực hiện điều đó nếu bạn cần một chức năng in ấn khá hay (ví dụ, được sử dụng bởi trình tạo báo cáo).

Mục tiêu của __repr__là rõ ràng

Hãy để tôi ra ngoài và nói điều đó - tôi không tin vào trình gỡ lỗi. Tôi thực sự không biết cách sử dụng bất kỳ trình gỡ lỗi nào và chưa bao giờ sử dụng một trình nghiêm túc. Hơn nữa, tôi tin rằng lỗi lớn trong trình gỡ lỗi là bản chất cơ bản của chúng - hầu hết các lỗi tôi gỡ lỗi đã xảy ra cách đây rất lâu, trong một thiên hà ở rất xa. Điều này có nghĩa là tôi tin rằng, với sự nhiệt thành tôn giáo, trong việc đăng nhập. Ghi nhật ký là huyết mạch của bất kỳ hệ thống máy chủ dễ quên nào. Python giúp bạn dễ dàng đăng nhập: với một số trình bao bọc cụ thể của dự án, tất cả những gì bạn cần là một

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

Nhưng bạn phải thực hiện bước cuối cùng - đảm bảo mọi đối tượng bạn triển khai đều có repr hữu ích, để mã như thế có thể hoạt động. Đây là lý do tại sao điều này lại xuất hiện: nếu bạn có đủ thông tin như vậy eval(repr(c))==c, điều đó có nghĩa là bạn biết mọi thứ cần biết c. Nếu điều đó đủ dễ, ít nhất là theo cách mờ nhạt, hãy làm điều đó. Nếu không, hãy chắc chắn rằng bạn có đủ thông tin về cbất cứ điều gì. Tôi thường sử dụng một định dạng giống như eval : "MyClass(this=%r,that=%r)" % (self.this,self.that). Điều đó không có nghĩa là bạn thực sự có thể xây dựng MyClass hoặc đó là những đối số của hàm tạo đúng - nhưng nó là một hình thức hữu ích để diễn tả Đây là tất cả những gì bạn cần biết về ví dụ này.

Lưu ý: Tôi đã sử dụng %rở trên, không %s. Bạn luôn muốn sử dụng repr()[hoặc %rđịnh dạng ký tự, tương đương] trong __repr__triển khai hoặc bạn đang đánh bại mục tiêu repr. Bạn muốn có thể phân biệt MyClass(3)MyClass("3").

Mục tiêu của __str__là có thể đọc được

Cụ thể, nó không có ý định rõ ràng - lưu ý rằng str(3)==str("3"). Tương tự như vậy, nếu bạn thực hiện trừu tượng hóa IP, có str của nó trông giống như 192.168.1.1 là tốt. Khi thực hiện trừu tượng ngày / giờ, str có thể là "2010/4/12 15:35:22", v.v ... Mục tiêu là thể hiện nó theo cách mà người dùng, không phải lập trình viên, muốn đọc nó. Cắt bỏ các chữ số vô dụng, giả vờ là một số lớp khác - miễn là nó hỗ trợ khả năng đọc, đó là một cải tiến.

Container __str__sử dụng các đối tượng chứa '__repr__

Điều này có vẻ đáng ngạc nhiên, phải không? Đó là một chút, nhưng nó sẽ dễ đọc như thế nào nếu nó sử dụng chúng __str__?

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

Không hẳn. Cụ thể, các chuỗi trong một container sẽ thấy quá dễ dàng để làm xáo trộn biểu diễn chuỗi của nó. Khi đối mặt với sự mơ hồ, hãy nhớ, Python chống lại sự cám dỗ để đoán. Nếu bạn muốn thực hiện các hành vi trên khi bạn đang in một danh sách, chỉ cần

print "[" + ", ".join(l) + "]"

(bạn có thể cũng có thể tìm ra những gì cần làm về từ điển.

Tóm lược

Thực hiện __repr__cho bất kỳ lớp học bạn thực hiện. Đây phải là bản chất thứ hai. Thực hiện __str__nếu bạn nghĩ rằng sẽ có ích khi có một phiên bản chuỗi có lỗi về mặt dễ đọc.


154
Chắc chắn không đồng ý với ý kiến ​​của bạn rằng gỡ lỗi không phải là hướng đi. Để phát triển, sử dụng trình gỡ lỗi (và / hoặc ghi nhật ký), để ghi nhật ký sử dụng sản xuất. Với trình gỡ lỗi, bạn có một cái nhìn về mọi thứ đã xảy ra khi sự cố xảy ra. Bạn có thể xem hình ảnh đầy đủ. Trừ khi bạn đang đăng nhập MỌI THỨ, bạn không thể có được điều đó. Ngoài ra, nếu bạn đang đăng nhập mọi thứ bạn sẽ phải lội qua hàng tấn dữ liệu để có được những gì bạn muốn.
Samuel

21
Câu trả lời tuyệt vời (ngoại trừ bit về việc không sử dụng trình gỡ lỗi). Tôi chỉ muốn thêm một liên kết đến câu hỏi và trả lời khác về str vs unicode trong Python 3 có thể liên quan đến cuộc thảo luận cho những người đã thực hiện chuyển đổi.
ThatAintWorking

11
plus1 cho trình gỡ lỗi là vô dụng và không quy mô đáng giá một xu. Thay vào đó hãy lấy thông lượng đăng nhập của bạn. Và vâng, đây là một bài viết tốt. Hóa ra __repr__là những gì tôi cần để gỡ lỗi. Cảm ơn sự giúp đỡ của bạn.
Personal_cloud

7
trình gỡ lỗi của bạn hoàn toàn sang một bên, tôi đã học được% r và dù sao cũng đáng để bình chọn
Mickey Perlstein

6
về trình gỡ lỗi và không có trình gỡ lỗi: không nhận được các ý kiến ​​cố thủ như vậy. Trong một số ứng dụng, việc gỡ lỗi là không thực tế, thông thường khi có liên quan đến thời gian thực hoặc khi mã của bạn chỉ thực thi từ xa trên nền tảng có ít quyền truy cập hoặc không có bàn điều khiển. Trong hầu hết các trường hợp khác, việc dừng lại ở một ngoại lệ để điều tra hoặc đặt điểm dừng sẽ nhanh hơn nhiều vì bạn không phải trải qua hàng ngàn dòng ghi nhật ký (sẽ làm lộn xộn đĩa của bạn và làm chậm ứng dụng). Cuối cùng, không phải lúc nào bạn cũng có thể đăng nhập, ví dụ như trên các thiết bị nhúng, trình gỡ lỗi cũng là bạn của bạn.
RedGlyph

523

Quy tắc ngón tay cái của tôi: __repr__dành cho nhà phát triển, __str__dành cho khách hàng.


2
Điều này đúng bởi vì đối với obj = uuid.uuid1 (), obj .__ str __ () là "2d7fc7f0-7706-11e9-94ae-0242ac110002" và obj .__ repr __ () là "UUID ('2d7 ') ". Nhà phát triển cần (giá trị + nguồn gốc) trong khi khách hàng cần một giá trị và họ không quan tâm làm thế nào họ có được nó!
Naren Yellavula

1
Ở đây khách hàng có thể không nhất thiết có nghĩa là người dùng cuối. Đó là khách hàng hoặc người dùng của đối tượng. Vì vậy, nếu là SDK thì các nhà phát triển SDK sẽ sử dụng __str__để các nhà phát triển bình thường có đối tượng dễ đọc. Mặt khác, __repr__là dành cho chính các nhà phát triển SDK.
Shiplu Mokaddim

398

Trừ khi bạn đặc biệt hành động để đảm bảo khác, hầu hết các lớp không có kết quả hữu ích cho một trong hai:

>>> class Sic(object): pass
... 
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>> 

Như bạn thấy - không có sự khác biệt và không có thông tin nào ngoài lớp và đối tượng id. Nếu bạn chỉ ghi đè một trong hai ...:

>>> class Sic(object): 
...   def __repr__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
...   def __str__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
<__main__.Sic object at 0x2617f0>
>>> 

như bạn thấy, nếu bạn ghi đè __repr__, thì CSONG được sử dụng cho__str__ , nhưng không phải ngược lại.

Các thông tin quan trọng khác cần biết: __str__trên một thùng chứa tích hợp sử dụng __repr__, KHÔNG phải __str__, cho các mục mà nó chứa. Và, mặc dù các từ về chủ đề được tìm thấy trong các tài liệu điển hình, hầu như không ai bận tâm làm cho các __repr__đối tượng là một chuỗieval có thể sử dụng để xây dựng một đối tượng bằng nhau (nó quá khó, VÀ không biết mô-đun có liên quan thực sự được nhập vào như thế nào căn hộ không thể).

Vì vậy, lời khuyên của tôi: tập trung vào việc làm cho __str__con người có thể đọc được một cách hợp lý và __repr__rõ ràng nhất có thể, ngay cả khi điều đó cản trở mục tiêu mờ nhạt không thể đạt được là làm cho __repr__giá trị trả về được chấp nhận làm đầu vào __eval__!


34
Trong các bài kiểm tra đơn vị của tôi, tôi luôn kiểm tra eval(repr(foo))đánh giá một đối tượng bằng foo. Bạn đúng rằng nó sẽ không hoạt động bên ngoài các trường hợp thử nghiệm của tôi vì tôi không biết mô-đun được nhập như thế nào, nhưng điều này ít nhất đảm bảo rằng nó hoạt động trong một số bối cảnh có thể dự đoán được. Tôi nghĩ rằng đây là một cách tốt để đánh giá nếu kết quả của __repr__nó là đủ rõ ràng. Làm điều này trong một bài kiểm tra đơn vị cũng giúp đảm bảo __repr__theo dõi các thay đổi đối với lớp.
Steven T. Snyder

4
Tôi luôn cố gắng đảm bảo rằng eval(repr(spam)) == spam(ít nhất là trong bối cảnh phù hợp), hoặc eval(repr(spam))tăng a SyntaxError. Bằng cách đó bạn tránh nhầm lẫn. (Và điều đó gần như đúng với các nội dung và hầu hết các stdlib, ngoại trừ, ví dụ, danh sách đệ quy, nơi a=[]; a.append(a); print(eval(repr(a)))cung cấp cho bạn [[Ellipses]]) Tất nhiên tôi không làm điều đó để thực sự sử dụng eval(repr(spam)) , ngoại trừ kiểm tra độ tỉnh táo trong bài kiểm tra đơn vị nhưng tôi làm đôi khi sao chép và dán repr(spam)vào một phiên tương tác.
abarnert

Tại sao các container (danh sách, bộ dữ liệu) sẽ không sử dụng __str__cho từng thành phần thay vì __repr__? Có vẻ sai đối với tôi, khi tôi triển khai một mục có thể đọc được __str__trong đối tượng của mình và khi nó là một phần của danh sách, tôi thấy điều đó xấu hơn __repr__thay vào đó.
SuperGeo

Chỉ gặp phải một lỗi khó chịu liên quan đến thực tế là eval(repr(x))thất bại ngay cả đối với các loại dựng sẵn: class A(str, Enum): X = 'x'sẽ nâng SyntaxError lên eval(repr(A.X)). Thật buồn, nhưng có thể hiểu được. BTW, eval(str(A.X))thực sự hoạt động, nhưng tất nhiên chỉ khi class Aở trong phạm vi - vì vậy nó có thể không hữu ích lắm.
tối đa

@SuperGeo Các câu trả lời khác bao gồm điều này: strphần tử sử dụng container repr[1, 2, 3]! = ["1", "2, 3"].
mtraceur

163

__repr__: đại diện của đối tượng python thường eval sẽ chuyển đổi nó trở lại đối tượng đó

__str__: là bất cứ điều gì bạn nghĩ là đối tượng đó ở dạng văn bản

ví dụ

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

151

Nói tóm lại, mục tiêu của __repr__là rõ ràng và __str__có thể đọc được.

Đây là một ví dụ tốt:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

Đọc tài liệu này cho repr:

repr(object)

Trả về một chuỗi chứa một đại diện có thể in của một đối tượng. Đây là cùng một giá trị mang lại bởi chuyển đổi (báo giá ngược). Đôi khi rất hữu ích khi có thể truy cập hoạt động này như một chức năng thông thường. Đối với nhiều loại, hàm này thực hiện một nỗ lực trả về một chuỗi sẽ mang lại một đối tượng có cùng giá trị khi được truyền vào eval(), nếu không, biểu diễn là một chuỗi được đặt trong dấu ngoặc nhọn chứa tên của loại đối tượng cùng với thông tin bổ sung thường bao gồm tên và địa chỉ của đối tượng. Một lớp có thể kiểm soát những gì hàm này trả về cho các thể hiện của nó bằng cách định nghĩa một __repr__()phương thức.

Đây là tài liệu cho str:

str(object='')

Trả về một chuỗi chứa một đại diện có thể in độc đáo của một đối tượng. Đối với chuỗi, điều này trả về chính chuỗi. Sự khác biệt với repr(object)str(object)không phải lúc nào cũng cố gắng trả về một chuỗi có thể chấp nhận được eval(); Mục tiêu của nó là trả về một chuỗi có thể in được. Nếu không có đối số nào được đưa ra, trả về chuỗi rỗng , ''.


1
Ý nghĩa của chuỗi có thể in ở đây là gì? Bạn có thể giải thích nó được không?
Vicrobot

115

Sự khác biệt giữa __str____repr__trong Python là gì?

__str__(đọc là "chuỗi dunder (double-underscore)") và __repr__(đọc là "dunder-repper" (cho "đại diện")) đều là các phương thức đặc biệt trả về chuỗi dựa trên trạng thái của đối tượng.

__repr__cung cấp hành vi sao lưu nếu __str__thiếu.

Vì vậy, trước tiên người ta nên viết một __repr__cái cho phép bạn xác định lại một đối tượng tương đương từ chuỗi mà nó trả về, ví dụ như sử dụng evalhoặc bằng cách nhập nó vào ký tự cho ký tự trong vỏ Python.

Bất cứ lúc nào sau đó, người ta có thể viết một __str__biểu diễn chuỗi cho người dùng có thể đọc được, khi người ta tin rằng nó là cần thiết.

__str__

Nếu bạn in một đối tượng, hoặc chuyển nó tới format, str.formathoặc str, sau đó nếu một __str__phương thức được xác định, phương thức đó sẽ được gọi, nếu không, __repr__sẽ được sử dụng.

__repr__

Các __repr__phương pháp được gọi bởi các chức năng được xây dựng trong reprvà là những gì đang lặp lại trên vỏ python của bạn khi đánh giá một biểu thức trả về một đối tượng.

Vì nó cung cấp một bản sao lưu cho __str__, nếu bạn chỉ có thể viết một, hãy bắt đầu với__repr__

Đây là phần trợ giúp tích hợp trên repr:

repr(...)
    repr(object) -> string

    Return the canonical string representation of the object.
    For most object types, eval(repr(object)) == object.

Đó là, đối với hầu hết các đối tượng, nếu bạn nhập vào những gì được in bởi repr, bạn sẽ có thể tạo một đối tượng tương đương. Nhưng đây không phải là thực hiện mặc định.

Thực hiện mặc định của __repr__

Đối tượng mặc định __repr__là ( nguồn C Python ) giống như:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

Điều đó có nghĩa là theo mặc định, bạn sẽ in mô-đun mà đối tượng đến từ, tên lớp và biểu diễn thập lục phân của vị trí của nó trong bộ nhớ - ví dụ:

<__main__.Foo object at 0x7f80665abdd0>

Thông tin này không hữu ích lắm, nhưng không có cách nào để rút ra cách người ta có thể tạo chính xác một biểu diễn chính tắc của bất kỳ trường hợp cụ thể nào, và tốt hơn là không có gì, ít nhất là cho chúng ta biết cách chúng ta có thể xác định duy nhất nó trong bộ nhớ.

Làm thế nào có __repr__thể hữu ích?

Chúng ta hãy xem mức độ hữu dụng của nó, sử dụng trình bao và datetimeđối tượng Python . Đầu tiên chúng ta cần nhập datetimemô-đun:

import datetime

Nếu chúng ta gọi datetime.nowtrong shell, chúng ta sẽ thấy mọi thứ chúng ta cần để tạo lại một đối tượng datetime tương đương. Điều này được tạo bởi datetime __repr__:

>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

Nếu chúng ta in một đối tượng datetime, chúng ta sẽ thấy một định dạng đẹp (có thể đọc được) của con người. Điều này được thực hiện bởi datetime __str__:

>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951

Việc tạo lại đối tượng chúng ta đã mất là một vấn đề đơn giản vì chúng ta đã không gán nó cho một biến bằng cách sao chép và dán từ __repr__đầu ra, sau đó in nó và chúng ta có được nó trong cùng một đầu ra có thể đọc được của con người như đối tượng khác:

>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180

Làm thế nào để tôi thực hiện chúng?

Khi bạn đang phát triển, bạn sẽ muốn có thể tái tạo các đối tượng ở cùng trạng thái, nếu có thể. Ví dụ, đây là cách đối tượng datetime định nghĩa __repr__( nguồn Python ). Nó khá phức tạp, vì tất cả các thuộc tính cần thiết để tái tạo một đối tượng như vậy:

def __repr__(self):
    """Convert to formal string, for repr()."""
    L = [self._year, self._month, self._day,  # These are never zero
         self._hour, self._minute, self._second, self._microsecond]
    if L[-1] == 0:
        del L[-1]
    if L[-1] == 0:
        del L[-1]
    s = "%s.%s(%s)" % (self.__class__.__module__,
                       self.__class__.__qualname__,
                       ", ".join(map(str, L)))
    if self._tzinfo is not None:
        assert s[-1:] == ")"
        s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
    if self._fold:
        assert s[-1:] == ")"
        s = s[:-1] + ", fold=1)"
    return s

Nếu bạn muốn đối tượng của mình có một đại diện dễ đọc hơn, bạn có thể thực hiện __str__tiếp theo. Đây là cách đối tượng datetime ( nguồn Python ) thực hiện __str__, điều này dễ dàng thực hiện vì nó đã có chức năng hiển thị nó ở định dạng ISO:

def __str__(self):
    "Convert to string, for str()."
    return self.isoformat(sep=' ')

Đặt __repr__ = __str__?

Đây là một bài phê bình về một câu trả lời khác ở đây cho thấy thiết lập __repr__ = __str__.

Cài đặt __repr__ = __str__là ngớ ngẩn - __repr__là một dự phòng cho __str__và a __repr__, được viết cho việc sử dụng của nhà phát triển trong gỡ lỗi, nên được viết trước khi bạn viết __str__.

Bạn chỉ cần một __str__khi bạn cần một đại diện bằng văn bản của đối tượng.

Phần kết luận

Xác định __repr__cho các đối tượng bạn viết để bạn và các nhà phát triển khác có một ví dụ có thể lặp lại khi sử dụng nó khi bạn phát triển. Xác định __str__khi bạn cần một đại diện chuỗi có thể đọc được của con người về nó.


Nó không phải là một cái gì đó dọc theo dòng type(obj).__qualname__?
Solomon Ucko

@SolomonUcko có trong Python 3, đó có vẻ là trường hợp - Tôi đã tìm kiếm mã nguồn nơi điều này được triển khai và tôi sẽ cập nhật câu trả lời của mình với thông tin đó khi tôi kết hợp nó.
Aaron Hall

33

Trên trang 358 của cuốn sách Python scripting cho khoa học tính toán của Hans Petter Langtangen, nó nói rõ rằng

  • Các __repr__mục tiêu tại một chuỗi đại diện đầy đủ của các đối tượng;
  • Các __str__là để trả về một chuỗi tốt đẹp cho in.

Vì vậy, tôi thích hiểu họ là

  • repr = sao chép
  • str = chuỗi (đại diện)

theo quan điểm của người dùng mặc dù đây là một sự hiểu lầm mà tôi đã mắc phải khi học trăn.

Một ví dụ nhỏ nhưng tốt cũng được đưa ra trên cùng một trang như sau:

Thí dụ

In [38]: str('s')
Out[38]: 's'

In [39]: repr('s')
Out[39]: "'s'"

In [40]: eval(str('s'))
Traceback (most recent call last):

  File "<ipython-input-40-abd46c0c43e7>", line 1, in <module>
    eval(str('s'))

  File "<string>", line 1, in <module>

NameError: name 's' is not defined


In [41]: eval(repr('s'))
Out[41]: 's'

Đó là tại pg. # 351.
jiten

4
Đó là loại gây hiểu lầm để gọi reprlà sao chép. Nó là tốt hơn để nghĩ về nó như là đại diện.
NelsonGon

31

Ngoài tất cả các câu trả lời được đưa ra, tôi muốn thêm vài điểm: -

1) __repr__()được gọi khi bạn chỉ cần viết tên đối tượng trên bảng điều khiển python tương tác và nhấn enter.

2) __str__()được gọi khi bạn sử dụng đối tượng với câu lệnh in.

3) Trong trường hợp, nếu __str__bị thiếu, sau đó in và bất kỳ chức năng nào sử dụng lệnh str()gọi __repr__()đối tượng.

4) __str__()của các container, khi được gọi sẽ thực thi __repr__()phương thức của các phần tử chứa nó.

5) str()được gọi bên trong __str__()có thể có khả năng tái diễn mà không có trường hợp cơ sở và lỗi về độ sâu đệ quy tối đa.

6) __repr__()có thể gọi repr()mà sẽ cố gắng tự động tránh đệ quy vô hạn, thay thế một đối tượng đã được đại diện bằng ....


14

Nói một cách đơn giản:

__str__được sử dụng để hiển thị chuỗi đại diện cho đối tượng của bạn để người khác dễ đọc .

__repr__được sử dụng để hiển thị một chuỗi đại diện của các đối tượng.

Giả sử tôi muốn tạo một Fractionlớp trong đó biểu diễn chuỗi của một phân số là '(1/2)' và đối tượng (lớp Phân số) sẽ được biểu diễn dưới dạng 'Phân số (1,2)'

Vì vậy, chúng ta có thể tạo một lớp Phân số đơn giản:

class Fraction:
    def __init__(self, num, den):
        self.__num = num
        self.__den = den

    def __str__(self):
        return '(' + str(self.__num) + '/' + str(self.__den) + ')'

    def __repr__(self):
        return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'



f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)

13

Trong tất cả sự trung thực, eval(repr(obj))không bao giờ được sử dụng. Nếu bạn thấy mình đang sử dụng nó, bạn nên dừng lại, vì evalnó nguy hiểm và chuỗi là một cách rất không hiệu quả để tuần tự hóa các đối tượng của bạn (sử dụngpickle thay thế).

Do đó, tôi khuyên bạn nên thiết lập __repr__ = __str__. Lý do là str(list)các yêu cầu reprvề các yếu tố (tôi coi đây là một trong những lỗi thiết kế lớn nhất của Python không được Python 3 xử lý). Một thực tế reprcó thể sẽ không hữu ích như đầu ra của print [your, objects].

Để đủ điều kiện này, theo kinh nghiệm của tôi, trường hợp sử dụng hữu ích nhất của reprhàm là đặt một chuỗi bên trong một chuỗi khác (sử dụng định dạng chuỗi). Bằng cách này, bạn không phải lo lắng về việc thoát dấu ngoặc kép hay bất cứ điều gì. Nhưng lưu ý rằng không có gì evalxảy ra ở đây.


19
Tôi nghĩ rằng điều này bỏ lỡ điểm. Việc sử dụng eval(repr(obj))là một bài kiểm tra độ tỉnh táo và quy tắc ngón tay cái - nếu điều này tái tạo lại đối tượng ban đầu một cách chính xác thì bạn đã __repr__thực hiện tốt . Nó không có ý định rằng bạn thực sự tuần tự hóa các đối tượng theo cách này.
JWG

7
evalkhông phải là nguy hiểm vốn có. Là không nguy hiểm hơn unlink, openhoặc bằng văn bản cho tập tin. Chúng ta có nên dừng ghi vào tệp vì có lẽ một cuộc tấn công độc hại có thể sử dụng đường dẫn tệp tùy ý để đưa nội dung vào bên trong? Mọi thứ đều nguy hiểm nếu người câm sử dụng một cách ngu ngốc. Thành ngữ là nguy hiểm. Hiệu ứng Dunning-Kruger rất nguy hiểm. evalchỉ là một chức năng.
Luis Masuelli

12

Từ một Wiki Tham khảo Python (không chính thức) (bản sao lưu trữ) của effbot:

__str__" tính toán biểu diễn chuỗi" không chính thức "của một đối tượng. Điều này khác __repr__ở chỗ nó không phải là biểu thức Python hợp lệ: thay vào đó có thể sử dụng biểu diễn thuận tiện hoặc ngắn gọn hơn. "


3
__repr__không có nghĩa là bắt buộc phải trả về một biểu thức vaild Python.
Nhà vật lý điên

10

str - Tạo một đối tượng chuỗi mới từ đối tượng đã cho.

repr - Trả về biểu diễn chuỗi chính tắc của đối tượng.

Sự khác biệt:

str ():

  • làm cho đối tượng có thể đọc được
  • tạo đầu ra cho người dùng cuối

repr ():

  • cần mã tái tạo đối tượng
  • tạo đầu ra cho nhà phát triển

8

Một khía cạnh còn thiếu trong các câu trả lời khác. Đúng là nói chung mô hình là:

  • Mục tiêu của __str__ : con người có thể đọc được
  • Mục tiêu của __repr__: rõ ràng, có thể đọc bằng máy thông quaeval

Thật không may, sự khác biệt này là thiếu sót, bởi vì REPL của Python và IPython cũng sử dụng __repr__để in các đối tượng trong bảng điều khiển REPL (xem các câu hỏi liên quan cho PythonIPython ). Do đó, các dự án được nhắm mục tiêu cho công việc bảng điều khiển tương tác (ví dụ: Numpy hoặc Pandas) đã bắt đầu bỏ qua các quy tắc trên và cung cấp một __repr__triển khai có thể đọc được của con người thay thế.


7

Từ cuốn sách Thông thạo Python :

Một yêu cầu cơ bản cho một đối tượng Python là cung cấp các biểu diễn chuỗi có thể sử dụng của chính nó, một biểu tượng được sử dụng để gỡ lỗi và ghi nhật ký, một biểu tượng khác để trình bày cho người dùng cuối. Đó là lý do tại sao các
phương thức đặc biệt __repr____str__tồn tại trong mô hình dữ liệu.


5

Những câu trả lời xuất sắc đã bao hàm sự khác biệt giữa __str____repr__, đối với tôi, người ta có thể hiểu được cái trước có thể đọc được ngay cả bởi người dùng cuối và cái sau hữu ích nhất có thể cho các nhà phát triển. Do đó, tôi thấy rằng việc triển khai mặc định __repr__thường không đạt được mục tiêu này vì nó bỏ qua thông tin hữu ích cho các nhà phát triển.

Vì lý do này, nếu tôi có đủ đơn giản __str__, tôi thường chỉ cố gắng để có được những điều tốt nhất của cả hai thế giới với một cái gì đó như:

def __repr__(self):
    return '{0} ({1})'.format(object.__repr__(self), str(self))

4

Một điều quan trọng cần ghi nhớ là __str__sử dụng các đối tượng có chứa ' __repr__.

>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"

Python ủng hộ sự không rõ ràng về khả năng đọc , __str__cuộc gọi của một tuplecuộc gọi các đối tượng được chứa ' __repr__, đại diện "chính thức" của một đối tượng. Mặc dù bản trình bày chính thức khó đọc hơn bản đại diện không chính thức, nhưng nó rõ ràng và mạnh mẽ hơn để chống lại lỗi.


Nó sử dụng __repr__ khi nó ( __str__) không được xác định! Vì vậy, bạn đã sai.
jiten

4

Tóm lại:

class Demo:
  def __repr__(self):
    return 'repr'
  def __str__(self):
    return 'str'

demo = Demo()
print(demo) # use __str__, output 'str' to stdout

s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'

import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout

from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'

4
>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

Khi print()được gọi trên kết quả của decimal.Decimal(23) / decimal.Decimal("1.05")số nguyên được in; đầu ra này ở dạng chuỗi có thể đạt được __str__(). Nếu chúng ta chỉ cần nhập biểu thức, chúng ta sẽ nhận được một decimal.Decimalđầu ra - đầu ra này ở dạng biểu diễn có thể đạt được __repr__(). Tất cả các đối tượng Python có hai dạng đầu ra. Dạng chuỗi được thiết kế để có thể đọc được. Biểu mẫu đại diện được thiết kế để tạo đầu ra mà nếu được cung cấp cho trình thông dịch Python sẽ (khi có thể) sẽ tái tạo đối tượng được đại diện.


4

__str__có thể được gọi trên một đối tượng bằng cách gọi str(obj)và sẽ trả về một chuỗi có thể đọc được của con người.

__repr__có thể được gọi trên một đối tượng bằng cách gọi repr(obj)và sẽ trả về đối tượng bên trong (trường đối tượng / thuộc tính)

Ví dụ này có thể giúp:

class C1:pass

class C2:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")

class C3:        
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")

class C4:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")


ci1 = C1()    
ci2 = C2()  
ci3 = C3()  
ci4 = C4()

print(ci1)       #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1))  #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2)       #C2 class str
print(str(ci2))  #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3)       #C3 class repr
print(str(ci3))  #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4)       #C4 class str 
print(str(ci4))  #C4 class str 
print(repr(ci4)) #C4 class repr

3

Hiểu __str____repr__trực giác và vĩnh viễn phân biệt chúng cả.

__str__trả lại chuỗi cơ thể được ngụy trang của một đối tượng nhất định để mắt có thể đọc được
__repr__trả lại cơ thể thực sự của một đối tượng nhất định (trả lại chính nó) cho sự không rõ ràng để xác định.

Xem nó trong một ví dụ

In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form

Giống như là __repr__

In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.

Chúng tôi có thể thực hiện thao tác số học trên __repr__kết quả một cách thuận tiện.

In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
    ...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)

nếu áp dụng các hoạt động trên __str__

In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Trả về không có gì ngoài lỗi.

Một vi dụ khac.

In [36]: str('string_body')
Out[36]: 'string_body' # in string form

In [37]: repr('real_body')
Out[37]: "'real_body'" #its real body hide inside

Hy vọng điều này sẽ giúp bạn xây dựng căn cứ cụ thể để khám phá thêm câu trả lời.


3
  1. __str__phải trả về đối tượng chuỗi trong khi __repr__có thể trả về bất kỳ biểu thức python nào.
  2. Nếu __str__thực hiện bị thiếu thì __repr__chức năng được sử dụng như dự phòng. Không có dự phòng nếu __repr__thực hiện chức năng bị thiếu.
  3. Nếu __repr__hàm trả về chuỗi đại diện của đối tượng, chúng ta có thể bỏ qua việc thực hiện __str__hàm.

Nguồn: https://www.journaldev.com/22460/python-str-inter-fifts


2

__repr__được sử dụng ở mọi nơi, ngoại trừ bởi printstrphương thức (khi a __str__được xác định!)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.