为什么可以在 python 3 中使用 ctypes 修改不可变字节对象?
Why it is possible to modify immutable bytes object using ctypes in python 3?
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 修改 bytes 对象,但不能用 str 对象做同样的事情。你能解释一下为什么吗?请看下面的例子。
c码
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 中也是不可变的,但与 bytes 对象不同,它不能用 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
Python 3 中的 str
被抽象为 Unicode,并且可以存储为每个字符串 1、2 或 4 字节,具体取决于字符串中使用的最高 Unicode 字符。要将字符串传递给 C 函数,必须将其转换为特定的表示形式。 ctypes
在这种情况下将转换后的临时缓冲区传递给 C 而不是原始缓冲区。 ctypes
可能会崩溃并损坏 Python 如果您的函数原型不正确或将不可变对象发送到会改变内容的函数,在这些情况下用户要小心。
在 bytes
的情况下 ctypes
传递一个指向其内部字节缓冲区的指针,但不希望它被修改。考虑:
a = b'123'
b = b'123'
由于 bytes
是不可变的,Python 可以自由地在 a
和 b
中存储相同的引用。如果你将 b
传递给一个 ctypes
-wrapped 函数并且它修改了它,它也可能会破坏 a
。
直接来自 ctypes documentation:
You should be careful, however, not to pass [immutable objects] to functions expecting pointers to mutable memory. If you need mutable memory blocks, ctypes has a create_string_buffer()
function which creates these in various ways....
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 修改 bytes 对象,但不能用 str 对象做同样的事情。你能解释一下为什么吗?请看下面的例子。
c码
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 中也是不可变的,但与 bytes 对象不同,它不能用 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
str
被抽象为 Unicode,并且可以存储为每个字符串 1、2 或 4 字节,具体取决于字符串中使用的最高 Unicode 字符。要将字符串传递给 C 函数,必须将其转换为特定的表示形式。 ctypes
在这种情况下将转换后的临时缓冲区传递给 C 而不是原始缓冲区。 ctypes
可能会崩溃并损坏 Python 如果您的函数原型不正确或将不可变对象发送到会改变内容的函数,在这些情况下用户要小心。
在 bytes
的情况下 ctypes
传递一个指向其内部字节缓冲区的指针,但不希望它被修改。考虑:
a = b'123'
b = b'123'
由于 bytes
是不可变的,Python 可以自由地在 a
和 b
中存储相同的引用。如果你将 b
传递给一个 ctypes
-wrapped 函数并且它修改了它,它也可能会破坏 a
。
直接来自 ctypes documentation:
You should be careful, however, not to pass [immutable objects] to functions expecting pointers to mutable memory. If you need mutable memory blocks, ctypes has a
create_string_buffer()
function which creates these in various ways....