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.
Trước tiên, hãy để tôi nhắc lại những điểm chính trong bài của Alex :
__repr__
mục tiêu là rõ ràng__str__
mục tiêu là có thể đọc được__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ề c
bấ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)
và 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.
__repr__
là những gì tôi cần để gỡ lỗi. Cảm ơn sự giúp đỡ của bạn.
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.
__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.
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__
!
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.
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.
__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 đó.
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.
str
phần tử sử dụng container repr
vì [1, 2, 3]
! = ["1", "2, 3"]
.
__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
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)
là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 đượceval()
; 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 ,''
.
Sự khác biệt giữa
__str__
và__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 eval
hoặ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.format
hoặ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 repr
và 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.
__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ớ.
__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 datetime
mô-đun:
import datetime
Nếu chúng ta gọi datetime.now
trong 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
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=' ')
__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.
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ó.
type(obj).__qualname__
?
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
__repr__
mục tiêu tại một chuỗi đại diện đầy đủ của các đối tượng;__str__
là để trả về một chuỗi tốt đẹp cho in.Vì vậy, tôi thích hiểu họ là
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:
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'
repr
là sao chép. Nó là tốt hơn để nghĩ về nó như là đại diện.
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 ...
.
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 Fraction
lớ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)
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ì eval
nó 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 repr
về 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ế repr
có 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 repr
hà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ì eval
xảy ra ở đây.
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.
eval
không phải là nguy hiểm vốn có. Là không nguy hiểm hơn unlink
, open
hoặ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. eval
chỉ là một chức năng.
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. "
__repr__
không có nghĩa là bắt buộc phải trả về một biểu thức vaild Python.
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à:
__str__
: con người có thể đọc được__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 Python và IPython ). 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ế.
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__
và__str__
tồn tại trong mô hình dữ liệu.
Những câu trả lời xuất sắc đã bao hàm sự khác biệt giữa __str__
và __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))
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 tuple
cuộ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.
__repr__
khi nó ( __str__
) không được xác định! Vì vậy, bạn đã sai.
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'
>>> 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.
__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
Hiểu __str__
và __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.
__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.__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.__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
__repr__
được sử dụng ở mọi nơi, ngoại trừ bởi print
và str
phương thức (khi a __str__
được xác định!)