在IPython中,使用如何配置ipython以十六进制格式显示整数?中的方法,可以自定义对象的打印方式:
In [1]: import ast
...: formatter=get_ipython().display_formatter.formatters["text/plain"]
...: formatter.for_type(ast.AST, lambda o, p, cycle: p.text("??"))
Out[1]: <function __main__.<lambda>(o, p, cycle)>
In [2]: x=ast.parse('1+2')
In [3]: x
Out[3]: ??
在SageMath中,同样的方法不起作用:
sage: import ast
....: formatter=get_ipython().display_formatter.formatters["text/plain"]
....: formatter.for_type(ast.AST, lambda o, p, cycle: p.text("??"))
....: x=ast.parse('1+2')
sage: x
<ast.Module object at 0x7f5750ccfaf0>
对于自定义类,似乎可以使用
_repr_
或 __repr__
(前者仅当对象继承自 SageObject
时):
sage: class A(SageObject):
....: ^Idef _repr_(self)->str:
....: ^I^Ireturn "abc"
....: A()
abc
sage: class A:
....: ^Idef __repr__(self)->str:
....: ^I^Ireturn "abc"
....: A()
abc
sage: ast.AST.__repr__=lambda x: "??" # hack, patch Python method
sage: x=ast.parse('1+2')
sage: x
??
如果我想自定义现有 SageMath 类的方式,例如,这不起作用
sage: from sage.rings.complex_interval import ComplexIntervalFieldElement
sage: ComplexIntervalFieldElement.__repr__=lambda x: "??"
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[30], line 1
----> 1 ComplexIntervalFieldElement.__repr__=lambda x: "??"
TypeError: cannot set '__repr__' attribute of immutable type 'sage.rings.complex_interval.ComplexIntervalFieldElement'
因此,总而言之,是否可以自定义如何在输出中打印
CIF
或其他 SageMath 类型?
这个答案在某些情况下有效,但有局限性。
翻看SageMath源代码,漂亮打印的工作方式是:
SageDisplayFormatter
类 .format()
方法 →DisplayManager
类 .displayhook()
方法 →DisplayManager
类 ._rich_output_formatter()
方法 →BackendIPythonCommandline
类.plain_text_formatter()
方法(仅当对象上不存在_rich_repr_
方法时)(此硬编码SagePrettyPrinter
用作下面的pretty_printer_class
)→BackendBase
类 ._apply_pretty_printer()
方法 →pretty_printer_class
对象的新实例,并对其调用 .pretty()
→SagePrettyPrinter
类 .pretty()
方法 →SagePrettyPrinter.pretty_repr
列表中的对象 →SomeIPythonRepr
类 .__call__()
方法 →self._type_repr
中查找类型。请注意,
SomeIPythonRepr
可能会被覆盖,请通过设置检查使用哪一个:
from sage.repl.display.pretty_print import SagePrettyPrinter
SagePrettyPrinter.DEBUG=True
因此,我们修改列表(请注意,这会访问内部属性,这可能会破坏)
sage: from sage.repl.display.pretty_print import SagePrettyPrinter
....: from sage.repl.display.fancy_repr import SomeIPythonRepr
....: import ast
....: someIPythonReprInstance = next(x for x in SagePrettyPrinter.pretty_repr
....: if isinstance(x, SomeIPythonRepr))
....: someIPythonReprInstance._type_repr[ast.Module] = lambda o, p, cycle: p.text("??")
注意,这里我们不能使用
ast.AST
,因为由于某种原因,对象的MRO没有被遍历,这与IPython不同。
然后,该类型的漂亮打印将大部分工作:
sage: ast.parse('1+2')
??
另一个例子:
sage: def printAlgebraicNumber(o: AlgebraicNumber, p: SagePrettyPrinter, cycle: bool)->None:
....: o.exactify()
....: p.text(repr(o))
....: if o not in QQ:
....: p.text(' (minpoly = ')
....: p.pretty(o.minpoly())
....: p.text(')')
sage: QQbar(sqrt(2))
1.414213562373095? (minpoly = x^2 - 2)
请注意,这有一些性能影响——每次打印
QQbar
的元素时,它都是精确的(即使未显式调用 exactify()
,也需要检查该元素是否属于有理数)。
如果不起作用,请尝试设置
DEBUG
选项(见上文)以查看使用的是哪款漂亮的打印机 - 如果有的话。
潜在有用:
get_ipython().display_formatter
返回 SageDisplayFormatter
实例sage.repl.rich_output.get_display_manager()
返回 DisplayManager
单例实例,相当于 get_ipython().display_formatter.dm