PEP 368 – 标准图像协议和类
- 作者:
- Lino Mastrodomenico <l.mastrodomenico at gmail.com>
- 状态:
- 已延期
- 类型:
- 标准跟踪
- 创建:
- 2007年6月28日
- Python版本:
- 2.6, 3.0
- 历史记录:
摘要
目前Python世界中图像存储和处理的情况极其分散:几乎每个使用图像对象的库都实现了自己的图像类,彼此之间不兼容,而且通常不够Pythonic。标准库中存在一个基本的RGB图像类(Tkinter.PhotoImage
),但除了Tkinter编程之外,几乎无法使用,也未被使用。
这种分散不仅占用开发人员宝贵的脑力资源,而且使不同库之间交换图像(在相对常见的用例中需要)变得比必要时更慢、更复杂。
本PEP建议通过定义一个简单且Pythonic的图像协议/接口来改善这种情况,希望现有的图像类(标准库内部和外部)都能接受并实现该协议,*并且不会破坏*其现有用户群的向后兼容性。实际上,这是关于最小“类图像”对象的外观和行为的定义(类似于类文件对象中的read()
和write()
方法)。
还建议在标准库中包含一个提供基本图像处理功能并实现新协议的类,以及一个混合类,有助于为现有的图像类添加对该协议的支持。
PEP延期
由于缺乏目前有兴趣推动PEP目标、收集和整合反馈,以及有足够可用时间有效地执行此操作的负责人,因此对本PEP中涉及的概念的进一步探索已被推迟。
基本原理
让高质量模块准备好纳入Python标准库的一个好方法是,简单地等待竞争的外部库之间进行自然选择,以提供一个具有有用功能和庞大用户群的明确赢家。然后,可以通过将其纳入标准库来正式认可事实上的标准。
不幸的是,这种方法在创建Python世界中的主要图像类方面效果不佳:几乎每个需要图像对象的第三方库都创建了自己的类,与其他库中的类不兼容。这是一个真正的问题,因为程序创建和处理图像(例如使用PIL(Python Imaging Library))然后使用wxPython或pygame显示它是完全合理的。但是这些库具有不同且不兼容的图像类,通常的解决方案是手动将图像从源导出到(宽度、高度、字节字符串)元组,然后“导入”它,在目标格式中创建一个新实例。这种方法*有效*,但既不如必要时优雅,也比必要时慢。
另一种有时使用过的“解决方案”是创建从一个类到另一个类的特定适配器和/或转换器(例如,PIL提供ImageTk
模块用于将PIL图像转换为与Tkinter兼容的类)。但是这种方法在涉及的库数量方面扩展性不佳,并且对于用户来说仍然很烦人:如果我有一个完美的图像对象,为什么我需要在将其传递给下一个方法之前进行转换,为什么它不能简单地接受我的图像?
这个问题绝不限于上面提到的三个库,并且可能有多个原因,其中两个在我看来在解决问题之前非常重要需要理解
- 在当今的计算世界中,图像是一种基本类型,并不严格地与特定领域相关联。这就是为什么上面提到的三个库(PIL、wxPython和pygame)的图像类之间永远不会出现明确的赢家:它们涵盖不同的领域,并且并没有真正互相竞争;
- Python标准库从未提供过一个好的图像类,可以被第三方模块采用或模仿。
Tkinter.PhotoImage
提供基本的RGB功能,但它是其中速度最慢、最难看的,并且只能在创建Tkinter根窗口后才能实例化。
本PEP试图通过四种方式改善这种情况
- 它定义了一个简单且Pythonic的图像协议/接口(在Python和C端),希望现有的图像类(标准库内部和外部)都能接受并实现该协议,*并且不会破坏*其现有用户群的向后兼容性。
- 它建议在标准库中包含三个新类
ImageMixin
提供了实现新协议所需的大部分内容;其主要目的是使现有库支持此接口尽可能简单,在某些情况下,只需将其添加到基类列表中并在构造函数中进行少量添加即可。Image
是ImageMixin
的子类,并将添加一个构造函数,该构造函数可以在不同像素格式之间调整图像大小和/或转换图像。这旨在提供新协议的快速有效默认实现。ImageSize
是一个小的辅助类。有关详细信息,请参见下文。
Tkinter.PhotoImage
将实现新协议(主要通过ImageMixin
类),并且所有可以接收图像的Tkinter方法都将被修改为接受任何实现该接口的对象。顺便说一句,本PEP的作者将与最常见的外部库的开发人员合作以实现相同目标(在其类中支持该协议并接受任何实现该协议的类)。- 新的
PyImage_*
函数将添加到CPython C API中:它们实现了协议的C端,并将第一个参数作为**任何**支持该协议的对象,即使它不是Image
/ImageMixin
类的实例。
对最终用户的主要影响将是简化不同库之间图像的交换(如果一切顺利,任何Python库都将接受来自任何其他库的图像)以及新Image
类的开箱即用可用性。新类旨在涵盖简单但常见的用例,例如将照片裁剪和/或调整为所需大小,并将其传递给适当的小部件以在窗口上显示,或使纹理变暗并将其传递给3D库。
Image
类并非旨在替代或与PIL、Pythonmagick或NumPy竞争,即使它提供了这三个库功能的一个(非常小)子集。特别是PIL提供了非常丰富的图像处理功能,包含*数十*个类、过滤器、转换和文件格式。将PIL(或类似的东西)包含在标准库中可能是值得的目标,也可能不是,但这完全超出了本PEP的范围。
规范
imageop
模块用作新类和对象的*默认*位置,因为它长期以来一直托管提供某种类似功能的函数,但如果需要,可以创建一个新模块(例如,一个新的“image
”或“media
”模块;后者最终可能包含其他多媒体类)。
MODES
是一个新的模块级常量:它是Image
类支持的像素格式的集合。任何实现新协议的图像对象都保证以其中一种模式进行格式化,但接受图像的库只允许支持其中的一部分。
这些模式反过来也可用作模块级常量(例如imageop.RGB
)。
下表总结了当前支持的模式及其属性
名称 | 组件名称 | 每个组件的位数 | 子采样 | 有效区间 |
---|---|---|---|---|
L | l(小写L) | 8 | 否 | 全范围 |
L16 | l | 16 | 否 | 全范围 |
L32 | l | 32 | 否 | 全范围 |
LA | l, a | 8 | 否 | 全范围 |
LA32 | l, a | 16 | 否 | 全范围 |
RGB | r, g, b | 8 | 否 | 全范围 |
RGB48 | r, g, b | 16 | 否 | 全范围 |
RGBA | r, g, b, a | 8 | 否 | 全范围 |
RGBA64 | r, g, b, a | 16 | 否 | 全范围 |
YV12 | y, cr, cb | 8 | 1, 2, 2 | 16-235, 16-240, 16-240 |
JPEG_YV12 | y, cr, cb | 8 | 1, 2, 2 | 全范围 |
CMYK | c, m, y, k | 8 | 否 | 全范围 |
CMYK64 | c, m, y, k | 16 | 否 | 全范围 |
当模式的名称以数字结尾时,它表示每个像素的平均位数。所有其他模式都简单地使用每个像素每个组件一个字节。
不支持调色板模式或每个组件少于8位的模式。欢迎来到21世纪。
以下是模式的简要说明及其包含的基本原理;模式分为四组
- **灰度**(
L*
模式):它们在科学计算中大量使用(这些人可能还需要非常高的动态范围和精度,因此L32
是唯一每个组件有32位的模式),有时将彩色图像的单个组件视为灰度图像可能很有用(这由平面图像的各个平面使用,请参见下面的YV12
);组件的名称('l'
,小写字母L)代表亮度,第二个可选组件('a'
)是alpha值,表示像素的不透明度:alpha = 0表示完全透明,alpha = 255/65535表示完全不透明的像素; - **RGB*模式**:普通的彩色图像。可选的alpha组件与灰度模式中的含义相同;
- YCbCr,也称为 YUV(
*YV12
模式)。这些模式是平面模式(即每个分量的所有像素值都存储在连续的内存区域中,而不是通常的排列方式,其中一个像素的所有分量都位于连续的字节中),并使用 1、2、2(也称为 4:2:0)子采样(即每个像素都有自己的 Y 值,但 Cb 和 Cr 分量在 2x2 的相邻像素组之间共享),因为这是 YCbCr 图像最常见的格式。请注意,V(Cr)平面存储在 U(Cb)平面之前。YV12
通常用于 MPEG2(包括 DVD)、MPEG4(ASP/DivX 和 AVC/H.264)和 Theora 视频帧。Y 的有效值在范围(16, 236)内(不包括 236),Cb 和 Cr 的有效值在范围(16, 241)内。JPEG_YV12
类似于YV12
,但三个分量可以具有 256 个值的完整范围。它是几乎所有 JPEG/JFIF 文件和 MJPEG 视频帧使用的原生格式。这两个模式与所有其他支持模式的“奇怪之处”源于它们被许多现有库和应用程序广泛使用的事实;这也是它们被包含在内的原因(以及它们无法无损转换为 RGB 的事实,因为 YCbCr 是更大的颜色空间);像素值的有趣 4:2:0 平面排列相对容易支持,因为在大多数情况下,这三个平面可以被视为三个独立的灰度图像; - CMYK* 模式(青色、品红色、黄色和黑色)是减色颜色模式,用于在纸上打印彩色图像。专业设计师喜欢假装他们离不开它们,所以它们就在这里。
Python API
请参阅下面的示例。
在 Python 2.x 中,这里定义的所有新类都是新式类。
模式对象
模式对象提供了一些属性和方法,可用于实现适用于不同类型图像的通用算法。
分量
每个像素的分量数(例如,RGBA 图像为 4)。
component_names
一个字符串元组;请参阅上表中的“分量名称”列。
bits_per_component
8、16 或 32;请参阅上表中的“每个分量的位数”。
bytes_per_pixel
components * bits_per_component // 8
,仅适用于非平面模式(见下文)。
planar
布尔值;如果图像分量分别位于单独的平面中,则为True
。目前,当且仅当模式使用子采样时才会发生这种情况。
subsampling
一个元组,对于模式中的每个分量,它包含一个包含两个整数的元组,分别表示水平和垂直方向的下采样量。实际上,对于YV12
和JPEG_YV12
,它是((1, 1), (2, 2), (2, 2))
,对于其他所有内容,它是((1, 1),) * components
。
x_divisor
max(x for x, y in subsampling)
;使用此模式的图像的宽度必须能够被此值整除。
y_divisor
max(y for x, y in subsampling)
;使用此模式的图像的高度必须能够被此值整除。
intervals
一个元组,对于模式中的每个分量,它包含一个包含两个整数的元组:该分量的最小和最大有效值。对于YV12
,它的值为((16, 235), (16, 240), (16, 240))
,对于其他所有内容,它的值为((0, 2 ** bits_per_component - 1),) * components
。
get_length(iterable[integer]) -> int
参数必须是一个可迭代对象,其中包含两个整数:图像的宽度和高度;它返回使用此模式存储这些尺寸的图像所需的字节数。
实现细节:模式是str
的子类的实例,并且其值等于其名称(例如,imageop.RGB == 'RGB'
),但L32
的值为'I'
。这仅用于与现有 PIL 用户向后兼容;使用此处提出的图像协议的新代码不应依赖此细节。
图像协议
任何支持图像协议的对象都必须提供以下方法和属性。
mode
此图像中像素的格式和排列;它是MODES
集中常量之一。
size
buffer
一个介于 0 和 255 之间的整数序列;它们是用于存储图像数据的实际字节(即修改它们的值会影响图像像素,反之亦然);数据具有行主序/C 连续顺序,没有填充,也没有任何特殊的内存对齐,即使每个分量有超过 8 位。唯一支持的方法是__len__
、__getitem__
/__setitem__
(使用整数和切片索引)和__iter__
;在 C 端,它实现了缓冲区协议。这是一个非常底层的图像接口,用户负责对每个分量超过 8 位的模式使用正确的(原生)字节顺序,以及对
YV12
图像使用正确的数值范围。缓冲区可能会也可能不会保留对其图像的引用,但即使在相应的图像被垃圾回收器销毁后,使用缓冲区仍然是安全的(尽管没有用)(这需要更改 wxPython 以及可能的其他库的图像类)。实现细节:这可以是array('B')
、bytes()
对象或专门的固定长度类型。
info
一个dict
对象,可以包含与图像关联的任意元数据(例如,DPI、伽马、ICC 配置文件、曝光时间…);这些数据的解释超出了本 PEP 的范围,并且可能取决于用于创建和/或保存图像的库;如果图像的方法返回一个新图像,它可以复制或调整其自己的info
属性中的元数据(ImageMixin
实现始终使用空的info
字典创建一个新图像)。
bits_per_component
bytes_per_pixel
component_names
分量
intervals
planar
subsampling
对应mode.*
属性的快捷方式。
map(function[, function...]) -> None
对于图像中的每个像素,将每个分量通过对应的函数映射。如果只传递一个函数,则会重复使用它来处理每个分量。此方法就地修改图像,并且通常非常快(大多数情况下,函数仅被调用少量次数,对于没有分支的简单函数,可能仅调用一次),但它对传递的函数施加了一些限制。
- 它必须接受一个整数参数并返回一个数字(
map
会将结果四舍五入到最接近的整数,并在必要时将其剪辑到range(0, 2 ** bits_per_component)
);- 它不能尝试拦截任何由参数上的任何操作引发的
BaseException
、Exception
或Exception
的任何未知子类(实现可能会尝试通过传递有趣的对象来优化速度,因此即使是简单的"if n == 10:"
也可能引发异常:只需忽略它,map
将负责处理它);捕获任何其他异常都可以;- 它应该是无副作用的,并且其结果不应依赖于在
map
的单个调用期间可能发生变化的值(参数除外)。
rotate90() -> image
rotate180() -> image
rotate270() -> image
返回图像的副本,该副本围绕其中心逆时针旋转 90、180 或 270 度。
clip() -> None
将YV12
图像中无效的分量值饱和到允许的最小值或最大值(请参阅mode.intervals
),对于其他图像模式,此方法不执行任何操作,速度很快;鼓励保存/导出YV12
图像的库始终调用此方法,因为中间操作(例如map
方法)可能会将值分配给像素,这些值超出有效间隔。
split() -> tuple[image]
返回对应于图像中各个分量的L
、L16
或L32
图像的元组。
平面图像还支持在component_names
中定义的相同名称的属性:它们包含提供对应分量像素值视图的灰度(模式L
)图像;对子图像的任何更改都会立即反映在父图像上,反之亦然(它们的缓冲区引用相同的内存位置)。
非平面图像提供以下其他方法。
pixels() -> iterator[pixel]
返回一个迭代器,迭代图像中的所有像素,从顶行开始,从左到右扫描每一行。有关像素对象的描述,请参见下文。
__iter__() -> iterator[line]
返回一个迭代器,迭代图像中的所有行,从上到下。有关行对象的描述,请参见下文。
__len__() -> int
返回图像中的行数(size.height
)。
__getitem__(integer) -> line
返回指定(y)位置的行。
__getitem__(tuple[integer]) -> pixel
参数必须是两个整数的元组;它们分别被解释为图像中的 x 和 y 坐标(0, 0 是左上角),并返回一个像素对象。
__getitem__(slice | tuple[integer | slice]) -> image
参数必须是切片或包含两个切片或一个整数和一个切片的元组;图像的选定区域被复制,并返回一个新的图像;image[x:y:z]
等效于image[:, x:y:z]
。
__setitem__(tuple[integer], integer | iterable[integer]) -> None
修改指定位置的像素;image[x, y] = integer
是image[x, y] = (integer,)
的快捷方式,用于具有单个分量的图像。
__setitem__(slice | tuple[integer | slice], image) -> None
以与__getitem__
方法的相应形式相同的方式选择一个区域,并将第二个参数中的图像的像素副本分配给它,该图像必须与此图像具有完全相同的模式以及与指定区域相同的大小;如果存在,alpha 分量将被简单地复制,并且不会影响图像的其他分量(即,不执行 alpha 合成)。
创建图像后,mode
、size
和 buffer
(包括buffer
在内存中的地址)永远不会改变。
如果接受PEP 3118,则预期所有图像对象都将支持新的缓冲区协议,但这超出了本 PEP 的范围。
Image
和 ImageMixin
类
ImageMixin
类实现了上面描述的所有方法和属性,除了mode
、size
、buffer
和 info
。Image
是 ImageMixin
的子类,它为这四个属性添加了支持,并提供了以下构造函数(请注意,构造函数不是图像协议的一部分)
__init__(mode, size, color, source)
mode
必须是MODES
集合中的常量之一,size
是两个整数的序列(新图像的宽度和高度);color
是整数序列,每个图像分量一个,用于将所有像素初始化为相同的值;source
可以是适当大小和格式的整数序列,在新图像的缓冲区中按原样复制,或者是一个现有的图像;在 Python 2.x 中,source
也可以是str
的实例,并被解释为字节序列。color
和source
是互斥的,如果两者都省略,则图像将初始化为透明黑色(在YV12
模式下缓冲区中的所有字节的值为 16,在CMYK*
模式下为 255,其他所有模式下为 0)。如果存在source
并且是图像,则可以省略mode
和/或size
;如果指定了它们并且与源模式和/或大小不同,则会转换源图像。用于调整大小和执行颜色空间转换的确切算法可能因 Python 版本和实现而异,但它们始终提供高质量的结果(例如:三次样条插值可用于上采样,抗锯齿滤波器可用于下采样图像);支持任何模式转换组合,但用于转换到和从
CMYK*
模式使用的算法非常简单:如果您拥有设备的确切颜色配置文件,则可能需要使用良好的颜色管理工具,例如 LittleCMS。新图像具有一个空的info
dict
。
线条对象
行对象(例如,在迭代图像时返回)支持以下属性和方法
mode
此行来自的图像的模式。
__iter__() -> iterator[pixel]
返回一个迭代器,迭代行中的所有像素,从左到右。有关像素对象的描述,请参见下文。
__len__() -> int
返回行中的像素数(图像宽度)。
__getitem__(integer) -> pixel
返回指定(x)位置的像素。
__getitem__(slice) -> image
复制行的选定部分并返回一个新图像;新图像的高度始终为 1。
__setitem__(integer, integer | iterable[integer]) -> None
修改指定位置的像素;line[x] = integer
是line[x] = (integer,)
的快捷方式,用于具有单个分量的图像。
__setitem__(slice, image) -> None
选择行的一部分,并将第二个参数中的图像的像素副本分配给它,该图像必须高度为 1,宽度等于指定的切片,并且与此行具有相同的模式;如果存在,alpha 分量将被简单地复制,并且不会影响图像的其他分量(即,不执行 alpha 合成)。
像素对象
像素对象(例如,在迭代行时返回)支持以下属性和方法
mode
此像素来自的图像的模式。
value
整数元组,每个分量一个。任何正确长度的可迭代对象都可以分配给value
(它将自动转换为元组),但不能将整数分配给它,即使模式只有一个分量:请使用例如pixel.l = 123
代替。
r, g, b, a, l, c, m, y, k
每个分量的整数值;只有适用于当前模式(在mode.component_names
中)的分量才可用。
__iter__() -> iterator[int]
__len__() -> int
__getitem__(integer | slice) -> int | tuple[int]
__setitem__(integer | slice, integer | iterable[integer]) -> None
这四种方法模拟了一个固定长度的整数列表,每个像素分量一个。
ImageSize
类
ImageSize
是一个命名元组,一个与 tuple
相同的类,除了
- 它的构造函数只接受两个整数,宽度和高度;它们在构造函数中使用它们的
__index__()
方法进行转换,因此所有ImageSize
对象都保证只包含int
(或可能在 Python 2.x 中为long
)实例; - 它具有一个
width
和一个height
属性,它们分别等效于元组中的第一个和第二个数字; - 其
__repr__
方法返回的字符串为'imageop.ImageSize(width=%d, height=%d)' % (width, height)
。
ImageSize
通常不是由最终用户实例化的,但在创建实现图像协议的新类时可以使用,因为 size
属性必须是 ImageSize
实例。
C API
可用的图像模式在 C 级别上可见为 PyImage_*
常量,类型为 PyObject *
(例如:PyImage_RGB
是 imageop.RGB
)。
以下函数为模式和图像对象提供了一个 C 友好的接口(所有函数在失败时都返回 NULL
或 -1)
int PyImageMode_Check(PyObject *obj)
如果对象obj
是有效的图像模式,则返回 true。
int PyImageMode_GetComponents(PyObject *mode)
PyObject* PyImageMode_GetComponentNames(PyObject *mode)
int PyImageMode_GetBitsPerComponent(PyObject *mode)
int PyImageMode_GetBytesPerPixel(PyObject *mode)
int PyImageMode_GetPlanar(PyObject *mode)
PyObject* PyImageMode_GetSubsampling(PyObject *mode)
int PyImageMode_GetXDivisor(PyObject *mode)
int PyImageMode_GetYDivisor(PyObject *mode)
Py_ssize_t PyImageMode_GetLength(PyObject *mode, Py_ssize_t width, Py_ssize_t height)
这些函数等效于它们对应的 Python 属性或方法。
int PyImage_Check(PyObject *obj)
如果对象obj
是Image
对象或Image
类型的子类型的实例,则返回真;另请参见下面的PyObject_CheckImage
。
int PyImage_CheckExact(PyObject *obj)
如果对象obj
是Image
对象,但不是Image
类型的子类型的实例,则返回真。
PyObject* PyImage_New(PyObject *mode, Py_ssize_t width, Py_ssize_t height)
返回一个新的Image
实例,初始化为透明黑色(有关详细信息,请参见上面的Image.__init__
)。
PyObject* PyImage_FromImage(PyObject *image, PyObject *mode, Py_ssize_t width, Py_ssize_t height)
返回一个新的Image
实例,使用image
对象的內容进行初始化,必要时重新缩放并转换为指定的mode
。
PyObject* PyImage_FromBuffer(PyObject *buffer, PyObject *mode, Py_ssize_t width, Py_ssize_t height)
返回一个新的Image
实例,使用buffer
对象的內容进行初始化。
int PyObject_CheckImage(PyObject *obj)
如果对象obj
实现了图像协议的足够子集,以便被下面定义的函数接受,即使它的类不是ImageMixin
和/或Image
的子类,则返回真。目前,它只是检查属性mode
、size
和buffer
的存在性和正确性。
PyObject* PyImage_GetMode(PyObject *image)
Py_ssize_t PyImage_GetWidth(PyObject *image)
Py_ssize_t PyImage_GetHeight(PyObject *image)
int PyImage_Clip(PyObject *image)
PyObject* PyImage_Split(PyObject *image)
PyObject* PyImage_GetBuffer(PyObject *image)
int PyImage_AsBuffer(PyObject *image, const void **buffer, Py_ssize_t *buffer_len)
这些函数等效于它们对应的 Python 属性或方法;图像内存只能通过 GIL 和对图像或其缓冲区的引用进行访问,并且对于每个组件超过 8 位的模式应格外小心:数据以本地字节顺序存储,并且可能**不**与 2 或 4 字节边界对齐。
示例
使用新的Image
类和协议的一些常见操作示例
# create a new black RGB image of 6x9 pixels
rgb_image = imageop.Image(imageop.RGB, (6, 9))
# same as above, but initialize the image to bright red
rgb_image = imageop.Image(imageop.RGB, (6, 9), color=(255, 0, 0))
# convert the image to YCbCr
yuv_image = imageop.Image(imageop.JPEG_YV12, source=rgb_image)
# read the value of a pixel and split it into three ints
r, g, b = rgb_image[x, y]
# modify the magenta component of a pixel in a CMYK image
cmyk_image[x, y].m = 13
# modify the Y (luma) component of a pixel in a *YV12 image and
# its corresponding subsampled Cr (red chroma)
yuv_image.y[x, y] = 42
yuv_image.cr[x // 2, y // 2] = 54
# iterate over an image
for line in rgb_image:
for pixel in line:
# swap red and blue, and set green to 0
pixel.value = pixel.b, 0, pixel.r
# find the maximum value of the red component in the image
max_red = max(pixel.r for pixel in rgb_image.pixels())
# count the number of colors in the image
num_of_colors = len(set(tuple(pixel) for pixel in image.pixels()))
# copy a block of 4x2 pixels near the upper right corner of an
# image and paste it into the lower left corner of the same image
image[:4, -2:] = image[-6:-2, 1:3]
# create a copy of the image, except that the new image can have a
# different (usually empty) info dict
new_image = image[:]
# create a mirrored copy of the image, with the left and right
# sides flipped
flipped_image = image[::-1, :]
# downsample an image to half its original size using a fast, low
# quality operation and a slower, high quality one:
low_quality_image = image[::2, ::2]
new_size = image.size.width // 2, image.size.height // 2
high_quality_image = imageop.Image(size=new_size, source=image)
# direct buffer access
rgb_image[0, 0] = r, g, b
assert tuple(rgb_image.buffer[:3]) == (r, g, b)
向后兼容性
此 PEP 涉及三个方面,需要考虑向后兼容性
- **Python 2.6**:新类和对象被添加到
imageop
模块中,而不会触及现有模块的内容;将向Tkinter.PhotoImage
添加新的方法和属性,并且其__getitem__
和__setitem__
方法将被修改为接受整数、元组和切片(目前它们仅接受字符串)。所有更改都提供了现有功能的超集,因此预计不会出现任何重大兼容性问题。 - **Python 3.0**:根据PEP 3108,将删除
imageop
模块的旧内容;本提案中定义的所有内容都将像在 Python 2.x 中一样工作,除了通常的 2.x/3.0 差异(例如,将删除对long
整数的支持以及将str
实例解释为字节序列的支持)。 - **外部库**:标准图像方法和属性的名称和语义经过仔细选择,以允许一些操作图像的外部库(至少包括 PIL、wxPython 和 pygame)在其图像类中实现新的协议,而不会破坏与现有代码的兼容性。图像协议和 NumPy 数组之间唯一明显的冲突是
size
属性的值以及image[x, y]
表达式中的坐标顺序。
参考实现
如果此 PEP 被接受,作者将提供用纯 Python 编写的这些新类的参考实现(可以在 CPython、PyPy、Jython 和 IronPython 中运行),以及一个针对 Python 和 C 速度优化的第二个实现,适合包含在 CPython 标准库中。作者还将提交所需的 Tkinter 修补程序。对于所有代码,都将提供 Python 2.x 和 Python 3.0 的版本(预计这两个版本将非常相似,并且 Python 3.0 版本可能会几乎完全自动生成)。
致谢
如果被接受,此 PEP 的实现由 Google 通过 Google Summer of Code 计划赞助。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0368.rst