Адаптация рабочего кода для Python 3 создает пустой PNG

Я использую следующий код (с очень небольшими изменениями) из этого поста. Этот скрипт просто берет массив шестнадцатеричного цвета и записывает из него изображение PNG. Я пытаюсь адаптировать его к Py3, но что-то идет не так.

import zlib, struct

def png_pack(png_tag, data):
    chunk_head = png_tag + data
    return (struct.pack("!I", len(data)) +
            chunk_head +
            struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))

def write_png(buf, width, height):

    # reverse the vertical line order and add null bytes at the start
    width_byte_4 = width * 4
    raw_data = b''.join(b'\x00' + buf[span:span + width_byte_4]
                        for span in range((height - 1) * width * 4, -1, - width_byte_4))

    return b''.join([
        b'\x89PNG\r\n\x1a\n',
        png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
        png_pack(b'IDAT', zlib.compress(raw_data, 9)),
        png_pack(b'IEND', b'')])


def saveAsPNG(array, filename):
    import struct
    if any([len(row) != len(array[0]) for row in array]):
        raise ValueError("Array should have elements of equal size")

    #First row becomes top row of image.
    flat = []
    map(flat.extend, reversed(array))
    #Big-endian, unsigned 32-byte integer.
    buf = b''.join([struct.pack('>I', ((0xffFFff & i32)<<8)|(i32>>24) )
                    for i32 in flat])   #Rotate from ARGB to RGBA.
    #print(type(buf))
    data = write_png(buf, len(array[0]), len(array))
    f = open(filename, 'wb')
    f.write(data)
    f.close()


saveAsPNG([[0xffFF0000, 0xffFFFF00],
           [0xff00aa77, 0xff333333]], 'test.png')

Он отлично работает с Python 2.7 и работает без каких-либо ошибок на Python 3. Однако полученное изображение пустое... Я не могу понять, в чем проблема. Я пытался заменить map с starmap но ничего не изменилось. Я проверил это buf является bytes вместо string, и это. Я действительно не знаю, почему он не пишет файл правильно. Любая подсказка?

1 ответ

Решение

В Python2 map возвращает список. В Python3 map возвращает объект карты:

print(type(map(flat.extend, reversed(array))))
# <class 'map'>

Объект карты является итератором. Не вызывает flat.extend пока это не повторяется. Поскольку никакая переменная не используется для сохранения объекта карты, он отбрасывается и flat остается пустым списком.

Так что вместо этого вам нужно:

flat = []
for item in reversed(array):
    flat.extend(item)

или понимание списка:

flat = [item for arr in reversed(array) for item in arr]

или itertools.chain.from_iterable:

import itertools as IT
flat = IT.chain.from_iterable(reversed(array))

mapникогда не должен использоваться для его побочных эффектов. Он должен использоваться только для генерации списка в Python2 или итератора в Python3.

В Python2 использую map поскольку его побочные эффекты были недовольной практикой. В Python3 эта политика в некоторой степени "обеспечивается" путем map итератор.

Другие вопросы по тегам