为什么可以在python 3中使用ctypes修改不可变字节对象?

时间:2017-12-02 15:56:46

标签: python c python-3.x ctypes

bytes对象是immutable。它不支持项目分配:

>>> bar = b"bar"
>>> bar[0] = b"#"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment

str对象也是不可变的:

>>> bar = "bar"
>>> bar[0] = "#"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

可以使用ctypes修改字节对象,而不能对str对象执行相同操作。你能解释一下原因吗?请看下面的例子。

c code

char* foo(char *bar) {
    bar[0] = '#';
    return bar;
}

c代码编译

gcc -shared -o clib.so -fPIC clib.c

个字节尝试

python代码

import ctypes

clib = ctypes.CDLL('./clib.so')

bar = b"bar"
print("Before:", bar, id(bar))

clib.foo(bar)
print("After: ", bar, id(bar))

python代码输出

Before: b'bar' 140451244811328
After:  b'#ar' 140451244811328

str尝试

str对象在Python 3中也是不可变的,但与字节对象不同,它不可能用ctypes修改它。

python代码

import ctypes

clib = ctypes.CDLL('./clib.so')

bar = "bar"
print("Before:", bar, id(bar))

clib.foo(bar)
print("After: ", bar, id(bar))

python代码输出

Before: bar 140385853714080
After:  bar 140385853714080

1 个答案:

答案 0 :(得分:4)

Python 3中的

str被抽象为Unicode,并且每个字符串可以存储为1个,2个或4个字节,具体取决于字符串中使用的最高Unicode字符。要将字符串传递给C函数,必须将其转换为特定的表示形式。在这种情况下,ctypes将转换后的临时缓冲区传递给C而不是原始缓冲区。如果您错误地设计函数或将不可变对象发送到改变内容的函数,ctypes可能会崩溃并破坏Python,并且在这些情况下由用户注意。

bytes情况下ctypes传递指向其内部缓冲区的指针,但不希望它被修改。考虑:

a = b'123'
b = b'123'

由于bytes是不可变的,因此Python可以在ab中自由存储相同的引用。如果您将b传递给ctypes - 包裹的函数并对其进行修改,则可能会损坏a

直接来自ctypes documentation

  

但是,您应该小心,不要将[immutable objects]传递给期望指向可变内存的函数。如果你需要可变的内存块,ctypes有一个create_string_buffer()函数可以用各种方式创建它们....