昨晚我厌倦了并开始将PyVISA移植到Python 3(在此处进展:https://github.com/thevorpalblade/pyvisa)。
我已经达到了一切正常的程度,只要我将设备地址(嗯,任何字符串确实)作为ASCII字符串而不是默认的unicode字符串传递(例如,
HP = vida.instrument(b“GPIB :: 16”)有效,而
HP = vida.instrument(“GPIB :: 16”)没有,引发了ValueError。
理想情况下,最终用户不必关心字符串编码。 关于我应该如何处理的任何建议? ctypes类型定义中的某些东西也许?
目前,相关的ctypes类型定义是:
ViString = _ctypes.c_char_p
答案 0 :(得分:5)
ctypes
,与Python 3中的大多数内容一样,故意不会在unicode和bytes之间自动转换。这是因为在大多数用例中,这只会要求人们切换到Python 3以避免相同类型的mojibake或UnicodeEncodeError
灾难。
然而,当你知道你只处理纯ASCII时,那是另一个故事。你必须明确 - 但你可以将这种显式性分解为包装器。
如Specifying the required argument types (function prototypes)中所述,除了标准的ctypes
类型之外,您还可以传递任何具有from_param
classmethod的类 - 通常会返回某种类型的实例(通常是相同类型)具有_as_parameter_
属性,但也可以只返回原生ctypes
- 类型值。
class Asciifier(object):
@classmethod
def from_param(cls, value):
if isinstance(value, bytes):
return value
else:
return value.encode('ascii')
这可能不是您想要的确切规则 - 例如,它会在bytearray
上失败(就像c_char_p
一样),即使可以安静地转换为bytes
...但是你不想隐含地将int
转换为bytes
。任何事情,无论你决定什么规则都应该很容易编码。
这是一个例子(在OS X上;你显然必须改变为Linux,Windows等加载libc
的方式,但你可能知道如何做到这一点):
>>> libc = CDLL('libSystem.dylib')
>>> libc.atoi.argtypes = [Asciifier]
>>> libc.atoi.restype = c_int
>>> libc.atoi(b'123')
123
>>> libc.atoi('123')
123
>>> libc.atoi('123') # Unicode fullwidth digits
ArgumentError: argument 1: <class 'UnicodeEncodeError'>: 'ascii' codec can't encode character '\uff10' in position 0: ordinal not in range(128)
>>> libc.atoi(123)
ArgumentError: argument 1: <class 'AttributeError'>: 'int' object has no attribute 'encode'
显然,如果对您的用例不够清楚,您可以捕获异常并提出另一个例外。
您可以类似地编写Utf8ifier
或Encodifier(encoding, errors=None)
类工厂,或者某些特定库所需的任何其他内容,并以相同的方式将其粘贴到argtypes
。
如果您还想自动解码返回类型,请参阅Return types和errcheck
。
最后一件事:当你确定数据假设是UTF-8时,你想要处理它们与Python 2.x不同的情况会(通过保存原样),你甚至可以在3.x中做到这一点。使用前面提到的Utf8ifier
作为您的argtype,并使用解码器errcheck,然后使用errors=surrogateescape
。有关完整示例,请参阅here。