在将Python 2代码移植到Python 3时处理ctypes和ASCII字符串

昨晚我厌倦了并开始将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

解决方法:

与Python 3中的大多数内容一样,ctypes故意不会在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一样),即使它可以安静地转换为字节……但是你不希望隐式地将int转换为字节.任何事情,无论你决定什么规则都应该很容易编码.

这是一个例子(在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 typeserrcheck.

最后一件事:当您确定数据应该是UTF-8时,但是您想要处理它们与Python 2.x不同的情况(通过保留它们原样),你甚至可以在3.x中做到这一点.使用前面提到的Utf8ifier作为你的argtype和一个解码器errcheck,并使用errors=surrogateescape.有关完整的例子,请参阅here.

上一篇:使用 Mac 的 Safari 收看 Netflix 时出现错误码「S7361-1253」如何解决?


下一篇:小草手把手教你 LabVIEW 串口仪器控制——VISA 串口配置