Câu hỏi Sự khác biệt giữa __str__ và __repr__?


Sự khác biệt giữa __str__ và __repr__ trong Python?


2112
2017-09-17 04:27


gốc




Các câu trả lời:


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

Đầu tiên, hãy để tôi nhắc lại những điểm chính trong Bài đăng của Alex:

  • Việc triển khai mặc định là vô dụng (thật khó để nghĩ ra một thứ không thể, nhưng đúng vậy)
  • __repr__ mục tiêu là rõ ràng
  • __str__ mục tiêu là để có thể đọc được
  • Hộp đựng __str__ sử dụng các vật thể chứa ' __repr__

Triển khai mặc định là vô ích

Điều này phần lớn 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ặc định cho __repr__ sẽ hành động như:

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

sẽ là quá nguy hiểm (ví dụ, quá dễ dàng để có được 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 báo. Lưu ý rằng có một mặc định là đúng: nếu __repr__ được định nghĩa và __str__ không phải là, đối tượng sẽ hoạt động như thể __str__=__repr__.

Điều này có nghĩa là, trong thuật ngữ đơn giản: hầu hết mọi đối tượng bạn triển khai đều phải có chức năng __repr__ có thể sử dụng để hiểu đối tượng. Triển khai __str__ là tùy chọn: làm điều đó nếu bạn cần một chức năng “in đẹp” (ví dụ, được sử dụng bởi trình tạo báo cáo).

Mục đích của __repr__ là rõ ràng

Hãy để tôi đến ngay và nói điều đó - Tôi không tin vào những kẻ buôn lậu. Tôi thực sự không biết cách sử dụng bất kỳ trình gỡ rối nào và chưa bao giờ sử dụng một trình gỡ rối nghiêm túc. Hơn nữa, tôi tin rằng lỗi lớn trong debuggers là bản chất cơ bản của họ - hầu hết các lỗi mà tôi đã gỡ lỗi đã xảy ra từ rất lâu trước đây, trong một thiên hà ở rất xa. Điều này có nghĩa là tôi tin, với sự nhiệt tình tôn giáo, trong việc khai thác gỗ. Ghi nhật ký là mạch máu của bất kỳ hệ thống máy chủ lưu trữ đám cháy 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à

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 có một repr hữu ích, vì vậy mã như vậy có thể hoạt động. Đây là lý do tại sao điều "eval" xuất hiện: nếu bạn có đủ thông tin để 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ễ dàng, ít nhất là theo một cách mờ nhạt, hãy làm điều đó. Nếu không, hãy đảm bảo bạn có đủ thông tin về c dù sao. 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). Nó không có nghĩa là bạn thực sự có thể xây dựng MyClass, hoặc rằng đó là các đối số hàm tạo đúng - nhưng nó là một dạng hữu ích để diễn tả "đây là tất cả mọi thứ bạn cần biết về cá thể 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 là %r định dạng ký tự, tương đương] bên 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) và MyClass("3").

Mục đích của __str__ có thể đọc được

Cụ thể, nó không có ý định rõ ràng - thông báo rằng str(3)==str("3"). Tương tự như vậy, nếu bạn thực hiện một trừu tượng IP, có các đường của nó trông giống như 192.168.1.1 là tốt. Khi thực hiện một ngày / thời gian trừu tượng, các str có thể là "2010/4/12 15:35:22", vv Mục đích là để đại diện cho nó theo cách mà một người sử dụng, không phải là một 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, nó là một sự cải tiến.

Hộp đựng __str__ sử dụng các vật thể 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 làm thế nào có thể đọc được

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

được? Không hẳn. Cụ thể, các chuỗi trong một thùng chứa sẽ thấy nó quá dễ làm xáo trộn chuỗi đại diện 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 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

Triển khai thực hiện __repr__ cho bất kỳ lớp nào bạn triển khai. Đây phải là bản chất thứ hai. Triển khai thực hiện __str__ nếu bạn nghĩ rằng sẽ hữu ích khi có một phiên bản chuỗi có lỗi ở phía bên của khả năng đọc nhiều hơn có lợi cho sự mơ hồ hơn.


2174
2018-04-13 00:56



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à cách để đi. Để phát triển, hãy sử dụng trình gỡ rối (và / hoặc ghi nhật ký) để ghi nhật ký sử dụng sản xuất. Với một trình gỡ rối bạn có một cái nhìn của tất cả mọi thứ đã đi sai khi vấn đề xảy ra. Bạn có thể xem hình ảnh đầy đủ. Trừ khi bạn đang đăng nhập tất cả mọi thứ bạn không thể có được điều đó. Plus nếu bạn đang đăng nhập tất cả mọi thứ bạn sẽ phải lội qua tấn dữ liệu để có được những gì bạn muốn. - Samuel
Câu trả lời tuyệt vời (ngoại trừ một chút về việc không sử dụng các trình gỡ rối). Tôi chỉ muốn thêm liên kết vào trang này Giải Đáp khác về str so với unicode bằng 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
", ".join(l) - sử dụng thực hành không hợp lệ khi sử dụng chữ thường "L" làm tên biến. Chúng ta hãy không dạy lập trình những ví dụ mới như vậy trong các nguồn có thẩm quyền. - Nikolay Prokopyev
plus1 cho debuggers là vô ích, và không quy mô trị giá một xu. Lấy thông lượng đăng nhập của bạn để thay thế. Và vâng, đây là một bài viết được 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


Quy tắc của tôi: __repr__ dành cho nhà phát triển, __str__ dành cho khách hàng.


370
2017-09-17 11:35





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

>>> 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__, đó là CSONG được sử dụng cho __str__, nhưng không phải ngược lại.

Các mẩu tin quan trọng khác cần biết: __str__ trên vùng chứa được tích hợp sử dụng __repr__, KHÔNG phải __str__, cho các mục chứa trong đó. 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 làm phiền __repr__ đối tượng là chuỗi eval có thể sử dụng để xây dựng một đối tượng bằng nhau (nó chỉ là quá khó, VÀ không biết làm thế nào các mô-đun có liên quan đã được thực sự nhập khẩu làm cho nó thực sự phẳng ra không thể).

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


319
2017-09-17 04:49



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á đối tượng bằng foo. Bạn nói đúng là 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 làm thế nào module được nhập khẩu, nhưng điều này ít nhất đảm bảo rằng nó hoạt động trong một số ngữ cảnh dự đoán được. Tôi nghĩ đây là một cách đánh giá tốt nếu kết quả của __repr__ đủ 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 rằng __repr__ theo các thay đổi đối với lớp học. - Steven T. Snyder
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 bên phải), hoặc eval(repr(spam)) đặt ra một SyntaxError. Bằng cách đó bạn tránh nhầm lẫn. (Và đó là hầu hết đúng đối với nội trang dựng sẵn và phần lớn stdlib, ngoại trừ, ví dụ: danh sách đệ quy, trong đó a=[]; a.append(a); print(eval(repr(a))) mang đến cho bạn [[Ellipses]]…) Tất nhiên tôi không làm vậy sử dụng  eval(repr(spam)), ngoại trừ kiểm tra sanity trong các 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
plus1 để giải thích rằng __str__ được thực hiện dưới dạng __repr__. Dường như là một số bài đăng khác bị bỏ qua. - personal_cloud
Tại sao không sử dụng vùng chứa (danh sách, bộ dữ liệu) __str__ cho mỗi phần tử thay vì __repr__? Dường như tôi đã sai khi tôi thực hiện một __str__ trong đối tượng của tôi và khi nó là một phần của danh sách tôi thấy __repr__ thay thế. - SuperGeo


__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

143
2017-09-17 04:35





Tóm lại, mục tiêu của __repr__ là rõ ràng và __str__ là được   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 có 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ị được tạo ra bởi các chuyển đổi (ngược lại   dấu ngoặc kép). Đôi khi hữu ích khi có thể truy cập hoạt động này là   một hàm bình thường. Đối với nhiều loại, chức năng này thực hiện một nỗ lực   để trả về một chuỗi có thể mang lại một đối tượng có cùng giá trị khi   đã vượt qua eval(), nếu không, biểu diễn là một chuỗi được đính kèm trong   dấu ngoặc nhọn có 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ì mà hàm này trả về   cho các trường hợp của nó bằng cách xác định __repr__() phương pháp.

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

str(object='')

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


115
2017-10-25 18:38





Sự khác biệt giữa __str__ và __repr__ bằng Python?

__str__ (được gọi là "dunder (double-underscore) string") và __repr__ (đọc là "dunder-repper" (cho "đại diện")) đều là các phương thức đặc biệt trả về các 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__ bị thiếu.

Vì vậy, đầu tiên bạn nên viết __repr__ cho phép bạn khôi phục một đối tượng tương đương từ chuỗi nó trả về, ví dụ: sử dụng eval hoặc bằng cách nhập nó vào ký tự-cho-ký tự trong một trình bao Python.

Bất cứ lúc nào sau đó, người ta có thể viết __str__ cho một biểu diễn chuỗi có thể đọc được của cá thể, khi một người 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ó đến format, str.format, hoặc là str, sau đó nếu một __str__ phương thức được định nghĩa, phương thức đó sẽ được gọi, nếu không, __repr__ sẽ được sử dụng.

__repr__

Các __repr__ phương thức được gọi bởi hàm dựng sẵn repr và là những gì được lặp lại trên vỏ python của bạn khi nó đá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 bằng __repr__

Đây là sự trợ giúp được xây dựng 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 gõ 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à cài đặt mặc định.

Triển khai mặc định của __repr__

Đối tượng mặc định __repr__ Là (C Nguồn Python) cái gì đó 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 đối tượng là 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 phải là rất hữu ích, nhưng không có cách nào để tìm ra cách người ta có thể tạo chính xác một biểu diễn kinh điển của bất kỳ cá thể cụ thể nào, và nó 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ớ.

Có thể như thế nào __repr__ hữu ích?

Hãy xem nó hữu ích như thế nào, bằng cách sử dụng trình bao Python và datetime các đối tượng. Trước tiên, chúng ta cần nhập datetime module:

import datetime

Nếu chúng ta gọi datetime.now trong trình bao, 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 ngày giờ __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 dễ đọc của con người (trên thực tế là ISO). Điều này được thực hiện bởi datetime's __str__:

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

Nó là một vấn đề đơn giản để tạo lại đối tượng chúng ta đã mất vì chúng tôi đã không gán nó cho một biến bằng cách sao chép và dán từ __repr__ đầu ra, và sau đó in nó, và chúng tôi nhận được nó trong cùng một đầu ra có thể đọc được 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 cách nào để triển khai 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 trong cùng một 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 = ", ".join(map(str, L))
    s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
    if self._tzinfo is not None:
        assert s[-1:] == ")"
        s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
    return s

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

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

Bộ __repr__ = __str__?

Đây là một phê phán của 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à ngu ngốc - __repr__ là một dự phòng cho __str__ và một __repr__, được viết cho các nhà phát triển sử dụng trong gỡ lỗi, phải được viết trước khi bạn viết __str__.

Bạn cần một __str__ chỉ khi bạn cần một biểu diễn văn bản của đối tượng.

Phần kết luận

Định nghĩa __repr__ đối với 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ụ tái sản xuất khi sử dụng nó khi bạn phát triển. Định nghĩa __str__ khi bạn cần một biểu diễn chuỗi có thể đọc được của con người về nó.


85
2018-01-25 02:01





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 giao diện đ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 lệnh in.

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

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

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

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


12
2017-09-08 03:27





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

Do đó, tôi khuyên bạn nên thiết lập __repr__ = __str__. Lý do là str(list) cuộc gọi repr trên các phần tử (tôi coi đây là một trong những lỗ hổng thiết kế lớn nhất của Python không được Python giải quyết 3). Thực tế repr có lẽ sẽ không hữu ích như đầu ra của print [your, objects].

Để hội đủ đ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 repr chức năng 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 khỏi dấu ngoặc kép hay bất cứ thứ gì. Nhưng lưu ý rằng không có eval xảy ra ở đây.


11
2017-11-15 10:39



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 sự tỉnh táo và một quy tắc chung - nếu điều này tái tạo lại đối tượng gốc một cách chính xác thì bạn có một __repr__ thực hiện. 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
eval vốn không phải là nguy hiểm. Không nguy hiểm hơn unlink, openhoặc ghi vào tệp. Chúng ta có nên ngừng ghi vào các tập tin bởi vì có lẽ một cuộc tấn công nguy hiểm có thể sử dụng một đường dẫn tập tin tùy ý để đưa nội dung vào bên trong? Mọi thứ đều nguy hiểm nếu ngu ngốc được sử dụng bởi những người ngu ngốc. Idiocy là nguy hiểm. Hiệu ứng Dunning-Kruger rất nguy hiểm. eval chỉ là một hàm. - Luis Masuelli