python print vs __str__?

9

有人能为我解释一下 print sthprint str(sth) 之间的区别吗?

例如在sqlite3官方文档中的示例中,可以看到以下代码来创建一个数据库并使用工厂类包装从中提取的数据:

(1) 创建数据库:

# I am using CPython 2.7, but I suppose 2.6 will be Ok as well
import sqlite3
conn = sqlite3.connect(":memory:")
c = conn.cursor()

c.execute('''create table stocks
(date text, trans text, symbol text, qty real, price real)''')
c.execute("""insert into stocks values ('2006-01-05','BUY','RHAT',100,35.14)""")
conn.commit()

c.close()

(2) 现在使用Row工厂来生成一些对象:

>>> conn.row_factory = sqlite3.Row
>>> c = conn.cursor()

>>> c.execute('select * from stocks')
<sqlite3.Cursor object at 0x7f4e7dd8fa80>
>>> r = c.fetchone()
>>> type(r)
<type 'sqlite3.Row'>
>>> r
(u'2006-01-05', u'BUY', u'RHAT', 100.0, 35.14)

正如你所见,我们可以输入rprint r来获取这个行对象的漂亮表示。

但上面未显示的是,print str(r)会给你不同的东西——更像是:

<sqlite3.Row object at 0x7f4e7dd8abcd> 

我想知道熟悉CPython实现的人能否解释一下,print如何从不支持__str__的对象中获取这种表示形式?

或者我猜另一个问题是,在上述表达式给出不同结果的情况下,我如何获得与简单的print obj打印出的字符串等效的字符串?


可能是重复的问题:如何在打印对象时产生与str()和repr()不同的输出? - Ciro Santilli OurBigBook.com
@CiroSantilli:不错的观点。虽然——我肯定有偏见——但我更喜欢我的答案。PS: "how I learned to stop worrying and love the dupes" - ジョージ
复制您的答案,修改一行以避免重复答案,然后让我们关闭它 :-) - Ciro Santilli OurBigBook.com
2个回答

7
当我写这篇文章并寻找一些参考资料时,我实际上已经找到了大部分答案:
  1. Python对象的C实现可以实现PyObject_Print()函数,它定义了对象将被打印到文件(包括stdout)的方式;
  2. 因此,要获取该表示,可能应该使用cStringIO模块(尚未尝试,但据说应该可以工作)。
尽管如此,我仍然希望在这里留下这个问题,以期有人会发现它有用 - 或提供更好的答案。 cStringIO示例:
import cStringIO as C; s = C.StringIO(); print >>s, r, ; s.getvalue()  

--最后一个逗号有助于去掉换行符(这取决于平台)

PS. 这里有几个与之相关的问题:
-- "Python print isn't using __repr__, __unicode__ or __str__ for unicode subclass?"
-- "Difference between __str__ and __repr__ in Python?"

(例如,第一个问题的answer答案中有这个很好的link链接到PyFile_WriteObject()代码。)

附注:在py3k中,这种差异似乎已经完全消失了。seems to be gone completely


3
这是__str____repr__之间的区别:
两种方法都会返回表示对象的字符串,但是:
- __repr__应该返回一个有效的Python表达式或类似<....>的东西,如果无法产生,则应该返回它。 - __str__可以返回更加用户友好的字符串。
形式上,print sth等同于print repr(sth)
>>> class C:
...   def __str__(self):
...     return "__str__"
...   def __repr__(self):
...     return "__repr__"
... 
>>> c = C()
>>> c
__repr__
>>> print c
__str__
>>> `c`
'__repr__'
>>> repr(c)
'__repr__'
>>> str(c)
'__str__'

关于print语句,如果一个对象不是字符串,它会首先使用字符串转换规则转换为字符串,关于字符串转换规则内置函数repr()在其参数中执行与将其括在括号和反引号中完全相同的转换。 内置函数str()执行类似但更用户友好的转换。
编辑: 关于所引述的特定情况,似乎在 C 级别sqlite3.rowPyTypeObject.tp_print定义为指向自定义打印函数的指针,该函数将转发到PyTuple_Type.tp_print。与此同时,tp_strtp_repr没有被定义 - 因此将会回退到观察到的默认对象打印行为。
作为结论,在 python 2.x 中,print(obj)print(str(obj))print(repr(obj)) 有可能产生三种不同的结果。
这种不一致在3.x中已经被解决,因为打印语句成为了普通函数。
# In Python 3.3:
>>> print(r)
<sqlite3.Row object at 0x7f4cedfbffd0>
>>> print(repr(r))
<sqlite3.Row object at 0x7f4cedfbffd0>
>>> print(str(r))
<sqlite3.Row object at 0x7f4cedfbffd0>

编辑2: 仍然涉及sqlite3.Row的特定情况,似乎可以将行转换为元组。 我已经在Python 2.6和3.3中进行了测试。

Python 2.6:

>>> sys.version
'2.6.6 (r266:84292, Dec 26 2010, 22:31:48) \n[GCC 4.4.5]'
>>> type(r)
<type 'sqlite3.Row'>
>>> r
(u'2006-01-05', u'BUY', u'RHAT', 100.0, 35.140000000000001)
>>> tuple(r)
(u'2006-01-05', u'BUY', u'RHAT', 100.0, 35.140000000000001)
>>> repr(tuple(r))
"(u'2006-01-05', u'BUY', u'RHAT', 100.0, 35.140000000000001)"

Python 3.3:

>>> sys.version
'3.3.1 (default, May 28 2013, 18:34:21) \n[GCC 4.4.5]'
>>> type(r)
<type 'sqlite3.Row'>
>>> r
<sqlite3.Row object at 0x7f4cedfbffd0>
>>> tuple(r)
('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14)
>>> repr(tuple(r))
"('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14)"

1
正式来说,print sthprint str(sth)相同(你的文本中有一个明显的拼写错误,'str' <-> 'repr')在我注意到例如对于问题中的代码,无论是str()还是repr()函数都不会产生与c = cStringIO.StringIO(); print >>c ... ; c.getvalue()相同的结果之前,这正是我对该主题的理解。 - ジョージ
1
不确定这是否是您的问题,但是在使用print时关于sqlite3.row的_漂亮格式化_的所有魔法似乎都发生在这里:http://hg.python.org/cpython/file/e9d0fb934b46/Modules/_sqlite/row.c#l162 - Sylvain Leroux
1
@SylvainLeroux:是的,然后它会作为一个条目填充到PyTypeObject结构中:http://hg.python.org/cpython/file/e9d0fb934b46/Modules/_sqlite/row.c#l215;至于3.*中对象打印的代码--现在似乎是[根据标志使用`str()`或`repr()`](http://hg.python.org/cpython/file/tip/Objects/fileobject.c#l140)(我没有轻易找到任何Python文档指定如何从Python控制这些标志,因此我假设默认情况下始终是`str()`或`repr()`)。 - ジョージ
1
@ジョージ 根据文档,在2.7版本中,调用PyTypeObject.tp_print时,Py_PRINT_RAW是唯一可接受的标志。有趣的是,在3.x版本中,PyFile_WriteObject也有同样的限制。我不明白相应的测试怎么会失败?显然,如果它失败了(它失败了吗?),那么就有其他允许的标志... - Sylvain Leroux
1
至少,在Include/object.h中定义了Py_PRINT_RAW作为唯一的_Py_PRINT_标志: http://hg.python.org/cpython/file/c8212fca8747/Include/object.h#l579 无论如何,我认为你没有机会从Python中控制此标志--除非编写自己的C包装器来实现。这似乎很麻烦,而在2.X中,print语句的扩展形式print ... >>应该足够好用... - Sylvain Leroux
显示剩余15条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接