通过 TKinter 使用 [clock format] 改变 proc returns ""

Using [clock format] through TKinter changes how proc returns ""

我正在使用 TKinter 从 Python 访问现有的 Tcl 库。 Tcl 过程之一在列表中查找值,如果未找到该值,则 returns ""。 Python 代码将 return 值视为 unicode 并检查它是否等于“”。在 Tcl 代码中调用 [clock format] 之前,这可以正常工作。之后,Python 代码将 return 值视为一个元组。我可以在 Python 代码中添加一些额外的逻辑来处理这个问题,但似乎还有一些更大的问题可能会产生其他影响。

示例 Python 程序:

import Tkinter

_tclsh = Tkinter.Tcl()

_tclsh.eval('proc returnBlank { } { return "" }')
_tclsh.eval('proc returnNotBlank { } { return "not blank" }')

print "Before calling clock"
_tclsh.eval('set shouldBeBlank [returnBlank]')
shouldBeBlank = _tclsh.getvar('shouldBeBlank')
print "shouldBeBlank is ", shouldBeBlank, " with type ", type(shouldBeBlank)

_tclsh.eval('set shouldNotBeBlank [returnNotBlank]')
shouldNotBeBlank = _tclsh.getvar('shouldNotBeBlank')
print "shouldNotBeBlank is ", shouldNotBeBlank, " with type ", type(shouldNotBeBlank)

print "\nCalling [clock seconds]"
_tclsh.eval('puts [clock seconds]')
_tclsh.eval('set shouldBeBlank [returnBlank]')
shouldBeBlank = _tclsh.getvar('shouldBeBlank')
print "shouldBeBlank is ", shouldBeBlank, " with type ", type(shouldBeBlank)

_tclsh.eval('set shouldNotBeBlank [returnNotBlank]')
shouldNotBeBlank = _tclsh.getvar('shouldNotBeBlank')
print "shouldNotBeBlank is ", shouldNotBeBlank, " with type ", type(shouldNotBeBlank)

print "\nCalling [clock format [clock seconds]]"
_tclsh.eval('puts [clock format [clock seconds]]')
_tclsh.eval('set shouldBeBlank [returnBlank]')
shouldBeBlank = _tclsh.getvar('shouldBeBlank')
print "shouldBeBlank is ", shouldBeBlank, " with type ", type(shouldBeBlank)

_tclsh.eval('set shouldNotBeBlank [returnNotBlank]')
shouldNotBeBlank = _tclsh.getvar('shouldNotBeBlank')
print "shouldNotBeBlank is ", shouldNotBeBlank, " with type ", type(shouldNotBeBlank)

结果输出为:

Before calling clock
shouldBeBlank is    with type  <type 'unicode'>
shouldNotBeBlank is  not blank  with type  <type 'str'>

Calling [clock seconds]
1431623835
shouldBeBlank is    with type  <type 'unicode'>
shouldNotBeBlank is  not blank  with type  <type 'str'>

Calling [clock format [clock seconds]]
Thu May 14 13:17:15 EDT 2015
shouldBeBlank is  ()  with type  <type 'tuple'>
shouldNotBeBlank is  not blank  with type  <type 'str'>

如您所见,只有空字符串受到影响,调用 [clock] 不会导致问题;只有[时钟格式].

如有任何帮助,我们将不胜感激。

注意:我尝试了不同的方法来 return 空字符串,但它们并没有影响输出。我试过了:

_tclsh.eval('proc returnBlank { } { return }')
_tclsh.eval('proc returnBlank { } { return {} }')

这是 Tkinter 猜测字符串类型的方式的问题。

在 Tcl 中,空列表或空字符串之间根本没有有意义的区别,因此字节码编译器可以随意更改内部类型,这正是 Tcl 在这种情况下最方便的。

Tcl 在 C 实现中使用了一个 Tcl_Obj 结构,它一次最多可以保存两个类型信息(一个总是字符串,另一个是 Tcl 解释器的当前视图,取决于最后一次使用对象)。

因此,如果某些代码试图 'guess' Tcl 对象的类型,它会执行以下(不可靠的)技巧,它查看 Tcl 对象的内部值并假设类型指针是否是已设置,该对象属于该类型。

所以看这个:

import Tkinter
tclsh = Tkinter.Tcl()
tclsh.eval('set x ""')
v = tclsh.getvar('x')
print type(v)
tclsh.eval('lindex $x 0')
v = tclsh.getvar('x')
print type(v)

首先是 'str',然后是 'tuple'。因此,如果您将变量用作列表,就会发生这种情况,但为什么它会修改您的 procs return 值?

这是由于字节编译时积极的文字共享造成的。空字符串是一种常见的文字,通常只有一个对象(如 Python None 或小整数)。

你可以使用一个小技巧来解决这个问题,如果你真的需要,只需明确地强制类型转换(称为 'shimmering' 参见 http://wiki.tcl.tk/3033)。

例如如果你想强制一个整数内部表示,在 Tcl 中:

set x "2"
incr x 0
# X now has internal type Integer

lindex $x 0
# X now has internal type tuple

append x ""
# X now has the internal type unicode

等等。当您尝试映射两种语言类型系统(如果它们有很大不同)时,这是一种怪癖,您会在 DBAPI 层中看到与 sqlite 类似的效果,或者当您尝试通过 xmlrpc 或 json 调用函数时。