Разное

Байт код python: Что такое байт-код Python и как его использовать

Модифицикация байт-кода функции в Python / Хабр

Некоторое время назад мне потребовалось решить достаточно необычную задачу, а именно, добавить нестандартный оператор в языке python. Эта задача заключалась в генерации питоновского кода по псевдокоду, похожему на ассемблер, который содержит в себе оператор goto. Сложный лексический анализатор мне писать не хотелось, оператор goto в псевдокоде использовался для организации циклов и переходов по условиям, и хотелось иметь некоторый его аналог в питоне, которого нет.

Существует какой-то модуль, выложенный в честь первого апреля в качестве шутки, однако у меня он не заработал. Сразу хочу оговориться, что о недостатках использования данного оператора я осведомлен, однако в ряде случаев при автоматической генерации кода его использование сильно упрощает жизнь программисту. Плюс ко всему, описанный подход позволяет добавить любую необходимую модификацию кода, если такая требуется, об этом на примере добавления оператора goto и будет рассказано далее.

Итак, есть проблема, как добавить в питон пару новых команд и как заставить его их верно интерпретировать (переходить по нужным адресам). Для этого напишем декоратор, который будет подцепляться к функции, в пределах которой мы хотим использовать оператор goto и добавлять метки (label), и воспользуемся модулями dis, который позволяет работать с байт-кодом питона, и new, который позволяет создавать внутренние объекты питона динамически.

Для начала, определимся с форматом команд. Так как питон имеет ряд ограничений по синтаксису, то команды вида

a:
goto a

сделать не удастся. Однако, питон позволяет добавить конструкции вида

label .a
goto .a

Здесь следует заметить, что точка играет важную роль, т.к. питон пропускает пробелы и сводит это к обращениям к атрибутам класса. Запись без точки приведет к сообщению о синтаксической ошибке. Итак, рассмотрим байт-код данных команд. Для этого выполним следующий код:

>>> def f():
>>>     label .a
>>>     goto .a
>>> import dis
>>> dis.dis( f )
  2           0 LOAD_GLOBAL              0 (label)
              3 LOAD_ATTR                1 (a)
              6 POP_TOP

  3           7 LOAD_GLOBAL              2 (goto)
             10 LOAD_ATTR                1 (a)
             13 POP_TOP
             14 LOAD_CONST               0 (None)
             17 RETURN_VALUE

Следовательно, команда объявления метки и перехода по метке сводится к трем операциям LOAD_GLOBAL, LOAD_ATTR, POP_TOP, основные из которых — первые две. Модуль dis позволяет определить байт-код этих команд с помощью словаря opmap и получить по байт-коду их символьное представление с помощью словаря opname.

>>> dis.opmap[ 'LOAD_GLOBAL' ]
116
>>> dis.opmap[ 'LOAD_ATTR' ]
105

Байтовое представление функции f хранится в f.func_code.co_code, а символьные представления ее переменных хранятся в f.func_code.co_names.

>>> f.func_code.co_names
('label', 'a', 'goto')

Теперь немного о байтовых представлениях интересующих нас команд. По куску дизассемблера видно, что команды LOAD_GLOBAL и LOAD_ATTR представляются тремя байтами (слева указано смещение), первый из которых — байт-код операции (из opmap), второй и третий — данные (младший и старший байт соответственно), представляющие собой индекс в списке f.func_code.co_names, соответствующий тому, какую переменную или какой атрибут мы хотим объявить.

Определить, есть ли аргументы у команды (и таким образом, длину команды в байтах), можно с помощью сравнения с dis.HAVE_ARGUMENT. Если она больше или равна данной константе, то она имеет аргументы, иначе — нет. Таким образом, получаем функцию для разбора байт-кода функции. Далее, заменяем код меток на операцию NOP, а код операторов goto на JUMP_ABSOLUTE, которая в качестве параметра принимает смещение внутри функции. Вот, практически и все. Код декоратора и пример использования приведен ниже.

import dis, new

class MissingLabelError( Exception ):
    pass

class ExistingLabelError( Exception ):
    pass
    
def goto( function ):
    labels_dict = {}
    gotos_list = []
    command_name = ''
    previous_operation = ''
    i = 0

    while i < len( function.func_code.co_code ):
        operation_code = ord( function.func_code.co_code[ i ] )
        operation_name = dis.opname[ operation_code ]

        if operation_code >= dis.HAVE_ARGUMENT:
            lo_byte = ord( function.func_code.co_code[ i + 1 ] )
            hi_byte = ord( function.func_code.co_code[ i + 2 ] )
            argument_position = ( hi_byte << 8 ) ^ lo_byte
            
            if operation_name == 'LOAD_GLOBAL':
                command_name = function.func_code.co_names[ argument_position ]
                
            if operation_name == 'LOAD_ATTR' and previous_operation == 'LOAD_GLOBAL':
                if command_name == 'label':
                    label = function.func_code.co_names[ argument_position ]
                    if labels_dict.has_key( label ):
                        raise ExistingLabelError( 'Label redifinition: %s' % label )
                    labels_dict.update( { label : i - 3 } )
                elif command_name == 'goto':
                    gotos_list += [ ( function.func_code.co_names[ argument_position ], i - 3 ) ]
            
            i += 3
            
        else:
            i += 1

        previous_operation = operation_name
        
    codebytes_list = list( function.func_code.co_code )
    for label, index in labels_dict.items():
        codebytes_list[ index : index + 7 ] = [ chr( dis.opmap[ 'NOP' ] ) ] * 7
    # заменяем 7 последовательно идущих байт команд LOAD_GLOBAL, LOAD_ATTR и POP_TOP на NOP

    for label, index in gotos_list:
        if label not in labels_dict:
            raise MissingLabelError( 'Missing label: %s' % label )
        
        target_index = labels_dict[ label ] + 7
        codebytes_list[ index ] = chr( dis.opmap[ 'JUMP_ABSOLUTE' ] )
        codebytes_list[ index + 1 ] = chr( target_index & 0xFF )
        codebytes_list[ index + 2 ] = chr( ( target_index >> 8 ) & 0xFF )

    # создаем байт-код для новой функции
    code = function.func_code
    new_code = new.code( code.co_argcount, code.co_nlocals, code.co_stacksize, code.co_flags,
        str().join( codebytes_list ), code.co_consts, code.co_names, code.co_varnames,
        code.co_filename, code.co_name, code.co_firstlineno, code.co_lnotab )
    
    # создаем новую функцию
    new_function = new.function( new_code, function.func_globals )
    return new_function

Пример использования:

@goto
def test_function( n ):
    
    goto .label1
     
    label .label2
    print n
    goto .label3
        
    label .label1
    print n
    n -= 1
    if n != 0:
        goto .label1
    else:
        goto .label2
        
    label .label3
    print 'the end'

test_function( 10 )

Результат выполнения примера:

10
9
8
7
6
5
4
3
2
1
0
the end

В заключение хочу добавить, что данное решение не вполне соответствует общему стилю питона: оно не слишком надежно из-за сильной зависимости от версии интерпретатора (в данном случае использовался интерпретатор 2.7, но должно работать для всех версий 2-ки), однако решение данной задачи еще раз доказывает большую гибкость языка и возможность добавления новой необходимой функциональности.

Погружение в пучину интерпретатора Python. Ч1 / Хабр

От переводчика: Наверно всем интересно, что внутри у инструмента, который используешь, этот интерес овладел и мной, но главное не утопать в нём и не закопаться так что не вылезти. Найдя для себя интересный материал, я решил заботливо перевести его и представить хабросообществу (моя первая публикация, прошу ногами сильно не пинать). Тем, кому интересен как Python работает на самом деле, прошу проследовать под кат.


Последние три месяца я потратил много времени на byterun, интерпретатор питоновского байткода, написанного на питоне. Работа над этим проектом была для меня захватывающе весёлой и познавательной. Я был бы рад, если бы вы тоже его потыкали. Но прежде нам надо немного остепенится, понять как работает python, так, чтобы мы знали, что такое интерпретатор на самом деле и с чем его едят.

Я подразумеваю, что вы сейчас в том же положении, что и я три месяца назад. Вы понимаете python, но понятия не имеете как он работает.

Небольшая заметка: Я работаю с версией 2.7 в этом посте. Третья версия почти схожа со второй, есть небольшие различия в синтаксисе и наименованиях, но в целом всё тоже самое.

Как работает python?

Мы начнём с очень (очень очень) высокого уровня внутренней работы. Что происходит когда вы выполняете код в вашем интерпретаторе?

~ $ python
Python 2.7.2 (default, Jun 20 2012, 16:23:33)
[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = "hello"

Годы идут, ледянки тают, Линус Торвальдс пилит очередное ядро, а 64 битый процессор без устали трудится, тем временем происходит четыре шага: лексической анализ, парсинг, компиляция и наконец таки интерпретация. Парсер забирает скормленные ему инструкции и генерирует структуру которая объясняет их связь формируя AST( Абстрактное Синтаксическое Дерево). Компилятор затем преобразует AST в одни (или несколько) объектов кода (байткод + обвязка). Потом интерпретатор выполняет каждый объект.

Я не собираюсь говорить об лексическом анализе, парсинге или компиляции сегодня, наверно потому что я сам про эти вещи ни чего не знаю, но не унывайте: вы всегда сможете изучить это, потратив часов этак пятьдесят. Мы предположим, что эти шаги прошли хорошо и успешно, и у нас есть на руках объекты python кода.

Перед тем как приступить к делу, я хочу сделать небольшую ремарку: в данном топике мы будем говорить об объектах функциях, объектах кода, и байткоде. Это всё разные вещи. Давайте начнём с функций. Нам не обязательно вникать глубоко в них, чтобы добраться до интерпретатора, но я просто хочу прояснить, что объекты функции и объекты кода — это две большие разницы, а объекты функции — самые интересные.

Объекты функции

Вы наверно могли слышать про «объекты функции». Это вещи которые люди подразумевают когда говорят: «Функции — это объекты первого класса». Давайте изучим их подробнее:

>>> def foo(a):
...     x = 3
...     return x + a
...
>>> foo
<function foo at 0x107ef7aa0>

«Функции это объекты первого класса» означает что функции — это объекты также как список это объект или экземпляры MyObject это объекты. Раз foo это объект, мы можем исследователь его не выполняя его (в этом и есть разница между foo() и foo). Мы можем предать foo как параметр в другую функцию или можем присвоить его переменной.

Давайте немного посмотрим на foo подробней:

>>> def foo(a):
...     x = 3
...     return x + a
...
>>> foo
<function foo at 0x107ef7aa0>
>>> foo.func_code
<code object foo at 0x107eeccb0, file "<stdin>", line 1>

Как вы можете видеть в выше приведённом коде, объект кода это атрибут объекта функции. Объект кода генерируется питоновским компилятором и интерпретатором, он содержит информацию необходимую для работы интерпретатора. Давайте посмотрим на атрибуты объекта кода:

>>> dir(foo.func_code)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename',
'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals',
'co_stacksize', 'co_varnames']

Здесь целая куча ништяков, большинство из которых нм сейчас не нужно. Давайте подробнее рассмотрим три атрибута объекта foo.

>>> foo.func_code.co_varnames
('a', 'x')
>>> foo.func_code.co_consts
(None, 3)
>>> foo.func_code.co_argcount
1

Вот что здесь есть: имена переменных и констант которые используются в нашей функции и количество принимаемых аргументов. Но мы всё ещё не видим ни чего что было бы похоже на инструкции. Инструкции называют байткодом ссылка, кстати это атрибут объекта кода:

>>> foo.func_code.co_code
'd\x01\x00}\x01\x00|\x01\x00|\x00\x00\x17S'

Напоминаю что байткод и объекты кода это не одно и тоже. Байткод это атрибут объекта кода помимо многих других атрибутов. Так что же такое байткод? Ну это просто набор байт. Они выглядят странно когда мы их печатаем потому что некоторым байтом сопоставимы символы а другим нет, давайте выведем их как числа.

>>> [ord(b) for b in foo.func_code.co_code]
[100, 1, 0, 125, 1, 0, 124, 1, 0, 124, 0, 0, 23, 83]

Вот байты которые творят всю магию. Интерпретатор будет последовательно и безустанно выбирать байты, смотреть какие они операции выполняют и с какими аргументами и исполнять команды. Для того чтобы пойти ещё дальше можно просмотреть исходный код Cpython а конкретно ceval.c что мы сделаем позднее.

Дизассемблирование байткода

Дизассемблирование означает взять все эти байты и преобразовать их во что-нибудь, что мы человеки способны понять. Это не выполняется в стандартном цикле питона. Сегодня для этой задачи есть отличный инструмент — модуль dis. Мы воспользуемся функцией dis.dis чтобы проанализировать что делает наша foo.

>>> def foo(a):
...     x = 3
...     return x + a
...
>>> import dis
>>> dis.dis(foo.func_code)
  2           0 LOAD_CONST               1 (3)
              3 STORE_FAST               1 (x)

  3           6 LOAD_FAST                1 (x)
              9 LOAD_FAST                0 (a)
             12 BINARY_ADD
             13 RETURN_VALUE

Первый номер это строка исходного python кода, второй номер это смещение внутри байткода: LOAD_CONST находится на позиции 0, а STORE_FAST на позиции 3 и так далее. Средняя колонка это название самой инструкции, последние две колонки дают понятие об аргументах инструкции (ели они есть), четвертая колонка показывает сам аргумент, который представляет собой индекс в других атрибутов объекта кода. В этом примере аргумент для LOAD_CONST это индекс в списке co_consts, а аргумент для STORE_FAST это индекс в co_varnames, в пятой колонке выводятся имена переменных или значение констант. Мы можем с легкостью это проверить:

>>> foo.func_code.co_consts[1]
3
>>> foo.func_code.co_varnames[1]
'x'

Это также объясняет вторую инструкцию STORE_FAST которая находится по позиции 3 в байткоде. Если инструкция имеет аргумент следующие два байта и есть этот аргумент. Работа интерпретатора как раз таки в том чтобы не запутается и продолжать сеять разумное, доброе, вечное. (вы могли заметить что BINARY_ADD не имеет аргументов, не волнуйтесь мы ещё вернемся к этому)

Была одна вешь которая удивляла меня когда я начел разбираться в том как работает python, как python может быть динамическим, если он ещё и «компилируется»? Обычно эти два слова «антонимы», есть динамические языки такие как Python, Ruby, и Javascript, а есть компилируемые таки как C, Java, и Haskell.

Когда люди говорят об компилируемых языках они имеют ввиду компиляцию в нативные x86/ARM/etc инструкции. Интерпретируемый язык не имеет компиляции вообще, разве что только «компилируется» на лету в байткод. Интерпретатор питона разбирает байткод и выполняет его внутри виртуальной машины, что кстати достаточно много работы, но мы поговорим об этом позднее.

Для того чтобы быть динамическим надо быть абстрактным, давайте посмотрим что это значит:

>> def modulus(x, y):
...     return x % y
...
>>> [ord(b) for b in modulus.func_code.co_code]
[124, 0, 0, 124, 1, 0, 22, 83]
>>> dis.dis(modulus.func_code)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_FAST                1 (y)
              6 BINARY_MODULO
              7 RETURN_VALUE

Эта дизассемблированая функция в байткоде. К тому времени как мы получаем приглашение функция modus была скомпилирована и объект корда был сгенерирован. Достаточно внезапно, но операция остатка от деления % (операция modulus) преобразуется в BINARY_MODULO. Похоже этой функцией можно воспользоваться для чисел:

>>> modulus(15,4)
3

Неплохо, а что если мы передадим что то другое, например строку.

>>> modulus("hello %s", "world")
'hello world'

Опана, что это тут? Вы наверно уже видели это раньше:

>>> print "hello %s" % "world"
hello world

Когда операция BINARY_MODULO выполняется для двух строк она выполняет подстановку строк вместо остатка от деления. Эта ситуация отличный пример динамической типизации. Когда компилятор генерирует объект кода для modulus он не имеет понятия что такое x и y, строки ли они или числа или что-то ещё. Он просто выполняет инструкции: загрузить одну перемененную, загрузить другую, выполнять препарацию бинарного модуля, вернуть результат. Работа интерпретатора в том чтобы понимать что BINARY_MODULO значит в текущем контексте. Наша функция modulus может считать остаток, подставлять строки… может что-то ещё? Если мы определим класс с методом __mod__ то мы сможем сделать что угодно.

>>> class Surprise(object):
...     def __init__(self, num):
...         self.num = num
...     def __mod__(self, other):
...         return self.num + other.num
...
>>> seven = Surprise(7)
>>> four = Surprise(4)
>>> modulus(seven, four)
11
>>> modulus(7,4)
3
>>> modulus("hello %s", "world")
'hello world'

Одна и та же функция с одним и тем же байткодом может выполнять разные операции в зависимости от типа контекста. Также функция modulus может возбудить исключение для примера TypeError если мы вызовем его для объектов, которые не реализованы.

Это является одной из причин того, почему трудно оптимизировать python. Вы не знаете, когда вы генерируете код объекта и байт-код, что за объекты будут в конечном итоге. Russell Power и Alex Rubinsteyn написали статью «как быстр может быть python», это статья достаточного содержательная.

На сегодня пока все. Оригинал статьи тут. Прошу прошения за возможные ошибки т.к. от природы обладаю врождённой безграмотностью и вынужден пользоваться машинным способом проверки текста.

Python 3 — Компиляция в байт-код

Когда вы запускаете программу, практически незаметно для вас Python сначала компилирует ваш исходный текст (инструкции в файле) в формат, известный под названием байт-код. Компиляция — это просто этап перевода программы, а байт-код — это низкоуровневое, платформонезависимое представление исходного текста программы. Интерпретатор Python транслирует каждую исходную инструкцию в группы инструкций байт-кода, разбивая ее на отдельные составляющие. Такая трансляция в байт-код производится для повышения скорости — байт-код выполняется намного быстрее, чем исходные

инструкции в текстовом файле. В предыдущем абзаце вы могли заметить фразу — практически незаметно для вас. Если интерпретатор Python на вашем компьютере обладает правом записи, он будет сохранять байт-код вашей программы в виде файла с расширением .рус (.рус — это компилированный исходный файл .ру). Вы будете обнаруживать эти файлы после запуска программ по соседству с файлами, содержащими исходные тексты (то есть в том же каталоге).

Интерпретатор сохраняет байт-код для ускорения запуска программ. В следующий раз, когда вы попробуете запустить свою программу, Python загрузит файл .рус и минует этап компиляции — при условии, что исходный текст программы не изменялся с момента последней компиляции. Чтобы определить, необходимо ли выполнять перекомпиляцию, Python автоматически сравнит время последнего изменения файла с исходным текстом и файла с байт-кодом.

Если исходный текст сохранялся на диск после компиляции, при следующем его запуске интерпретатор автоматически выполнит повторную компиляцию программы.

Если интерпретатор окажется не в состоянии записать файл с байт-кодом на диск, программа от этого не пострадает, просто байт-код будет сгенерирован в памяти и исчезнет по завершении программы.1 Однако поскольку файлы .рус повышают скорость запуска программы, вам может потребоваться иметь возможность сохранять их, особенно для больших программ. Кроме того, файлы с байт-кодом — это еще один из способов распространения программ на языке Python. Интерпретатор запустит файл .рус, даже если нет оригинальных файлов с исходными текстами. (В разделе «Фиксированные двоичные файлы»  описывается еще один способ распространения программ).

Интернет-магазин plitka.ua, предлагает керамическую мозаику на складе и под заказ. Выставочные образцы в салонах компании.

Как сгенерировать файл байт-кода в Python?

Всякий раз, когда скрипт Python компилируется, он автоматически генерирует скомпилированный код, называемый байтовым кодом. Байт-код фактически не интерпретируется для машинного кода, если не существует какой-либо экзотической реализации, такой как PyPy .

Байт-код загружается во время выполнения Python и интерпретируется виртуальной машиной, которая представляет собой фрагмент кода, который читает каждую инструкцию в байт-коде и выполняет любую указанную операцию.

Байт-код автоматически создается в том же каталоге, что и файл .py, когда модуль python импортируется впервые или когда источник более поздний, чем текущий скомпилированный файл. В следующий раз, когда программа будет запущена, интерпретатор Python использует этот файл, чтобы пропустить этап компиляции.

Запуск сценария не считается импортом, и файл .pyc не будет создан. Например, давайте напишем файл скрипта abc.py, который импортирует другой модуль xyz.py. Теперь запустите файл abc.py , xyz.pyc будет создан, поскольку xyz импортирован, но файл abc.pyc не будет создан, так как abc.py не импортируется.

Но существуют встроенные модули и команды py_compile и compileall, которые облегчают создание файла .pyc.

  1. Использование функции py_compile.compile : модуль py_compile может вручную скомпилировать любой модуль. Одним из способов является интерактивное использование функции py_compile.compile в этом модуле:
    >>> import py_compile
    >>> py_compile.compile('abc.py')
    

    Это запишет .pyc в то же место, что и abc.py.

  2. Использование функции py_compile.main () : компилирует несколько файлов одновременно.
    >>> import py_compile
    >>> py_compile.main(['File1.py','File2.py','File3.py'])
    
  3. Использование функции compileall.compile_dir () : компилирует каждый файл python, присутствующий в предоставленном каталоге.
    >>> import compileall
    >>> compileall.compile_dir(directoryname)
    
  4. Использование py_compile в терминале:
    $ python -m py_compile File1.py File2.py File3.py ...
    

    Или для интерактивной компиляции файлов

    $ python -m py_compile -
      File1.py
      File2.py
      File3.py
       .
       .
       .
    
  5. Использование compileall в терминале: эта команда автоматически рекурсивно переходит в подкаталоги и создает файлы .pyc для всех найденных файлов python.
    $ python -m compileall 
    

Примечание . Модуль compileall и py_compile является частью стандартной библиотеки python, поэтому для его использования не нужно устанавливать ничего лишнего.

Ссылки:
1. https://docs.python.org/3/library/py_compile.html
2. https://docs.python.org/2/library/compileall.html
3. Эффбот

Эта статья предоставлена Shubham Bansal . Если вы как GeeksforGeeks и хотели бы внести свой вклад, вы также можете написать статью с помощью contribute.geeksforgeeks.org или по почте статьи [email protected]. Смотрите свою статью, появляющуюся на главной странице GeeksforGeeks, и помогите другим вундеркиндам.

Рекомендуемые посты:

Как сгенерировать файл байт-кода в Python?

0.00 (0%) 0 votes

Все, что нужно знать о байтах в Python

Новые типы двоичных последовательностей во многих отношениях похожи на тип str в Python 2. Главное что нужно знать — это то, что существуют два основных встроенных типа двоичных последовательностей: неизменяемый тип bytes, появившийся в Python 3, и изменяемый тип bytearray, добавленный в Python 2.6.

Кстати, в Python 2.6 был также введен тип bytes, но лишь как псевдоним типа str, он ведет себя иначе, чем тип bytes в Python 3.

Каждый элемент bytes или bytearray — целое число от 0 до 255, а не односимвольная строка, как в типе str в Python 2 str.

Однако срез двоичной последовательности всегда является двоичной последовательностью того же типа, даже если это срез длины.

Пример №1. Пятибайтовая последовательность в виде bytes и bytearray.

>>> cafe = bytes(‘café’, encoding=»utf_8″) # (1)
>>> cafe
b’caf\xc3\xa9’
>>> cafe[0] # (2)
99
>>> cafe[:1] # (3)
b’c’
>>> cafe_arr = bytearray(cafe)
>>> cafe_arr # (4)
bytearray(b’caf\xc3\xa9′)
>>> cafe_arr[-1:] # (5)
bytearray(b’\xa9′)
>>>



>>> cafe = bytes(‘café’, encoding=»utf_8″) # (1)

>>> cafe

b’caf\xc3\xa9′

>>> cafe[0] # (2)

99

>>> cafe[:1] # (3)

b’c’

>>> cafe_arr = bytearray(cafe)

>>> cafe_arr # (4)

bytearray(b’caf\xc3\xa9′)

>>> cafe_arr[-1:] # (5)

bytearray(b’\xa9′)

>>>

1. bytes можно получить из str, если известна кодировка.

2. Каждый элемент — целое число в диапазоне range(256).

3. Срезы bytes также имеют тип bytes, даже если срез состоит из одного байта

4. Для типа bytearray не существует литерального синтаксиса: в оболочке объекты этого типа представляются в виде конструктора bytearray(), аргументом которого является литерал типа bytes.

5. Срез cafe_arr также имеет типа bytearray

Тот факт, что my_bytes[0] возвращает int, а my_bytes[:1] — объект bytes длины 1, не должен вызывать удивления. Единственный тип последовательности, для которого s[0] == s[:1] — это типа str.

И хотя на практике этот тип используется сплошь и рядом, его поведение — исключение из правила. Для всех остальных последовательностей s[i] возвращает один элемент, а s[i:i+1] — последовательность, состоящую из единственного элемента s[i].

Хотя двоичные последовательности — на самом деле, последовательности целых чисел, в их литеральной нотации отражен тот факт, что часто они включают ASCII-текст.

Поэтому применяются различные способы отображения, зависящие от значения каждого байта.

  • Для байтов из диапазона символов ASCII, имеющих графическое начертание — от пробела до ~ выводится сам символ ASCII.
  • Для байтов, соответствующих символам табуляции, новой строки, возврата каретки и \, выводятся управляющие последовательности \t, \n, \r и \\.
  • Для все остальных байтов выводится шестнадцатеричное представление, например, нулевой байт представляется последовательностью \x00.

Именно поэтому в примере №1 мы видим представление b’caf\xc3\xa9′. Первые три байта b’caf’ принадлежат диапазону символов ASCII с графическим начертанием, последний — нет.

Оба типа bytes и bytearray поддерживают все методы типа str кроме тех, что относятся к форматированию(format, format_map), и еще нескольких, прямо зависящих от особенностей Unicode, в том числе casefold, isdecimal, isidentifier, isnumeric, isprintable и encode.

Это означает, что при работе с двоичными последовательностями мы можем пользоваться знакомыми методами строк, например endswith, replace, strip, translate, upper и десятками других, только аргументы должны иметь тип bytes, а не str.

К двоичным последовательностям применимы и функции для работы с регулярными выражениями из модуля re, если регулярное выражение откомпилировано из двоичной последовательности, а не из str.

Оператор % не работает с двоичными последовательностями в версиях от Python 3.0 до 3.4, но, если верить документу PEP 461, то его предполагается поддержать в будущем.

Оператор % часто используется во многих языках программирования. Один мой знакомый который разрабатывает шаблоны для WordPress как например ThemeForest Grand Conference рассказал что без оператора % пришлось бы изобретать велосипеды каждый день.

Для двоичных последовательностей существует метод класса, отсутствующий в типе str: fromhex, который строит последовательность, разбирая пары шеснадцатеричных цифр, которые могут быть разделены пробелами, хотя это и не обязательно.

>>> bytes.fromhex(«34 FF FA A3 A5»)
b’4\xff\xfa\xa3\xa5’
>>>



>>> bytes.fromhex(«34 FF FA A3 A5»)

b’4\xff\xfa\xa3\xa5′

>>>

Другие способы построения объектов bytes и bytearray связаны с вызовом различных конструкторов:

  • с именованными аргументами str и encoding;
  • с итерируемым объектом, порождающим элементы со значениями от 0 до 255;
  • с объектом который реализует протокол буфера (например, bytes, bytearray, memoryview, array.array), при этом байты копируются из исходного объекта во вновь созданную двоичную последовательность.

Построение двоичной последовательности из буфероподобного объекта  — это низкоуровневая операция, которая может потребовать приведения типов.

Пример №2. Инициализация байтов данными, хранящимися в массиве.

>>> import array
>>> numbers = array.array(‘h’, [-3, -2, -1, 0, 1, 2, 3]) # (1)
>>> octets = bytes(numbers) # (2)
>>> octets
b’\xfd\xff\xfe\xff\xff\xff\x00\x00\x01\x00\x02\x00\x03\x00′ # (3)
>>>



>>> import array

>>> numbers = array.array(‘h’, [-3, -2, -1, 0, 1, 2, 3]) # (1)

>>> octets = bytes(numbers) # (2)

>>> octets

b’\xfd\xff\xfe\xff\xff\xff\x00\x00\x01\x00\x02\x00\x03\x00′ # (3)

>>>

1. Код типа ‘h’ означает создание массива коротких целых (16-разрядных).
2. В объекте octets хранится копия байтов, из которых составлены числа в массиве numbers.
3. Это четырнадцать байтов, представляющих семь коротких целых чисел.

Зачем нужны боты в группе ВК? Почему иногда они лучше, чем люди? Портал https://pricesmm.com/ ответил на каверзные вопросы и выяснил, как накрутить ботов Вконтакте за деньги и без затрат. Для вас полный и честный обзор всех доступных способов, а также условия и секреты, чтобы обезопасить процесс.

Создание объекта bytes или bytearray из буфероподобного источника всегда сопровождается копированием байтов.

Напротив, объекты типа memoryview позволяют разным двоичным структурам данных использовать одну и ту же область памяти.

Для извлечения структурированной информации из двоичной последовательности бесценную пользу может оказать модуль struct.

Интересное видео для нашей аудитории

Python | метод bytes ()

Взаимодействие между различными типами данных обеспечивается языком Python с легкостью. Эта статья направлена на демонстрацию и обработку взаимного преобразования различных типов данных в bytes (), обычно полезных для схем кодирования.
byte () преобразует объект в неизменяемый представленный байтом объект заданного размера и данных.

Syntax : bytes(src, enc, err)

Parameters :
src : The source object which has to be converted
enc : The encoding required in case object is a string
err : Way to handle error in case the string conversion fails.

Returns :
Byte immutable object consisting of unicode 0-256 characters according to src type.
integer : Returns array of size initialized to null
iterable : Returns array of iterable size with elements equal to iterable elements( 0-256 )
string : Returns the encoded string acc. to enc and if encoding fails, performs action according
to err specified.
no arguments : Returns array of size 0.

Код №1: демонстрация byte () для целых чисел, none и итераций

  

a = 4

lis1 = [1, 2, 3, 4, 5]

  

print ("Byte conversion with no arguments : " + str(bytes())) 

  

print ("The integer conversion results in : "  + str(bytes(a)))

print ("The iterable conversion results in : "  + str(bytes(lis1)))

Выход:

Byte conversion with no arguments : b''
The integer conversion results in : b'\x00\x00\x00\x00'
The iterable conversion results in : b'\x01\x02\x03\x04\x05'

Поведение байтов со строками

Байты принимают строку в качестве аргумента и для ее выполнения требуется схема кодирования. Наиболее важным аспектом этого является обработка ошибок в случае сбоя кодирования, некоторые из определенных схем обработки ошибок:

String Error Handlers :
strict : Raises the default UnicodeDecodeError in case of encode failure.
ignore : Ignores the unencodable character and encodes the remaining string.
replace : Replaces the unencodable character with a ‘?’.

Код № 2: демонстрация bytes () с использованием строки

  

str1 = 'GeeksfÖrGeeks'

  

print ("Byte conversion with ignore error : " +

      str(bytes(str1, 'ascii', errors = 'ignore'))) 

  

print ("Byte conversion with replace error : " +

      str(bytes(str1, 'ascii', errors = 'replace'))) 

  

print ("Byte conversion with strict error : " +

      str(bytes(str1, 'ascii', errors = 'strict'))) 

Выход:

Byte conversion with ignore error : b'GeeksfrGeeks'
Byte conversion with replace error : b'Geeksf?rGeeks'

Исключение:

Traceback (most recent call last):
  File "/home/0458f7fae57c6f4102366356593842ef.py", line 15, in 
    print ("Byte conversion with strict error : " + str(bytes(str1, 'ascii', errors = 'strict'))) 
UnicodeEncodeError: 'ascii' codec can't encode character '\xd6' in position 6: ordinal not in range(128)

Рекомендуемые посты:

Python | метод bytes ()

0.00 (0%) 0 votes

Байт Объекты против Строки в Питоне

В Python 2 и str, и байты являются одними и теми же объектами typeByte, тогда как в Python 3 байтовые объекты, определенные в Python 3, представляют собой « последовательность байтов » и аналогичны объектам « unicode » из Python 2. Однако есть много различий в строках и Байтовые объекты. Некоторые из них изображены ниже:
`

  • Байтовые объекты — это последовательность байтов , а строки — это последовательность символов .
  • Внутренние байтовые объекты находятся в машиночитаемой форме, строки — только в удобочитаемой форме.
  • Поскольку байтовые объекты являются машиночитаемыми, они могут быть непосредственно сохранены на диске . Принимая во внимание, что строки нуждаются в кодировании, перед которым они могут быть сохранены на диске.

Существуют методы для преобразования байтового объекта в String и String в байтовые объекты.

кодирование

PNG, JPEG, MP3, WAV, ASCII, UTF-8 и т. Д. Являются различными формами кодирования. Кодировка — это формат для представления аудио, изображений, текста и т. Д. В байтах. Преобразование строк в байтовые объекты называется кодированием. Это необходимо для того, чтобы текст мог быть сохранен на диске с использованием сопоставления с использованием методов кодирования ASCII или UTF-8 .

Эта задача достигается с помощью encode () . В качестве аргумента используется техника кодирования. По умолчанию используется методика UTF-8 .

  

a = 'GeeksforGeeks'

  

c = b'GeeksforGeeks'

  

d = a.encode('ASCII')

  

if (d==c):

    print ("Encoding successful")

else : print ("Encoding Unsuccessful")

Выход:

Encoding successful

Декодирование

Аналогично, Decoding — это процесс для преобразования объекта Byte в String . Это реализовано с использованием decode () . Строка байтов может быть декодирована обратно в строку символов, если вы знаете, какая кодировка использовалась для ее кодирования. Кодирование и декодирование являются обратными процессами.

  

a = 'GeeksforGeeks'

  

c = b'GeeksforGeeks'

  

d = c.decode('ASCII')

  

if (d==a):

    print ("Decoding successful")

else : print ("Decoding Unsuccessful")

Выход:

Decoding successful

Эта статья предоставлена Манджитом Сингхом . Если вы как GeeksforGeeks и хотели бы внести свой вклад, вы также можете написать статью с помощью contribute.geeksforgeeks.org или по почте статьи [email protected]. Смотрите свою статью, появляющуюся на главной странице GeeksforGeeks, и помогите другим вундеркиндам.

Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.

Рекомендуемые посты:

Байт Объекты против Строки в Питоне

0.00 (0%) 0 votes

Введение в байт-код Python

Если вы когда-либо писали или даже просто использовали Python, вы, вероятно, привыкли видеть файлы исходного кода Python; их имена заканчиваются на .py . Возможно, вы также видели другой тип файла с именем, заканчивающимся на .pyc , и, возможно, слышали, что это файлы «байт-кода» Python. (Их немного сложнее увидеть в Python 3 — вместо того, чтобы оказаться в том же каталоге, что и ваши файлы .py , они попадают в подкаталог с именем __pycache__ .) И, возможно, вы слышали, что это своего рода экономия времени, которая предотвращает необходимость повторного синтаксического анализа исходного кода Python при каждом запуске.

Но помимо «о, это байт-код Python», действительно ли вы знаете, что находится в этих файлах и как Python их использует?

Если нет, то сегодня ваш счастливый день! Я расскажу вам, что такое байт-код Python, как Python использует его для выполнения вашего кода и как знание этого может вам помочь.

Как работает Python

Python часто описывается как интерпретируемый язык, на котором ваш исходный код транслируется в собственные инструкции ЦП по мере выполнения программы, но это верно лишь отчасти.Python, как и многие интерпретируемые языки, фактически компилирует исходный код в набор инструкций для виртуальной машины, а интерпретатор Python является реализацией этой виртуальной машины. Этот промежуточный формат называется «байт-кодом».

Итак, те .pyc файлов, которые Python оставляет без внимания, не просто «более быстрая» или «оптимизированная» версия вашего исходного кода; это инструкции байт-кода, которые будут выполняться виртуальной машиной Python при запуске вашей программы.

Рассмотрим пример.Вот классический вариант «Hello, World!» написано на Python:

 

def hello ()
print («Hello, World!»)

А вот байт-код, в который он превращается (переведенный в удобочитаемую форму):

 

2 0 LOAD_GLOBAL 0 (печать)
2 LOAD_CONST 1 ('Hello, World!')
4 CALL_FUNCTION 1

Если вы наберете эту функцию hello () и воспользуетесь интерпретатором CPython для ее запуска, то приведенный выше список будет тем, что Python выполнит.Однако это может выглядеть немного странно, поэтому давайте глубже посмотрим, что происходит.

Внутри виртуальной машины Python

CPython использует виртуальную машину на основе стека. То есть он полностью ориентирован на структуры данных стека (где вы можете «протолкнуть» элемент «наверх» структуры или «вытолкнуть» элемент «сверху»).

CPython использует три типа стеков:

  1. Стек вызовов . Это основная структура работающей программы Python. Он имеет один элемент — «фрейм» — для каждого текущего активного вызова функции, причем нижняя часть стека является точкой входа в программу.Каждый вызов функции помещает новый кадр в стек вызовов, и каждый раз, когда вызов функции возвращается, его кадр удаляется.
  2. В каждом кадре есть стек оценки (также называемый стеком данных ). В этом стеке происходит выполнение функции Python, а выполнение кода Python состоит в основном из помещения объектов в этот стек, манипулирования ими и их удаления.
  3. Также в каждом кадре есть стека блоков . Это используется Python для отслеживания определенных типов структур управления: циклы, попыток, /, кроме блоков, и с блоками, все заставляют записи помещаться в стек блоков, и стек блоков всплывает всякий раз, когда вы выйти из одной из этих структур.Это помогает Python знать, какие блоки активны в любой данный момент, так что, например, оператор continue или break может повлиять на правильный блок.

Большинство инструкций байт-кода Python манипулируют стеком оценки текущего кадра стека вызовов, хотя есть некоторые инструкции, которые делают другие вещи (например, переход к определенным инструкциям или манипулирование стеком блоков).

Чтобы почувствовать это, предположим, что у нас есть код, который вызывает функцию, например: my_function (my_variable, 2) .Python переведет это в последовательность из четырех инструкций байт-кода:

  1. Инструкция LOAD_NAME , которая ищет объект функции my_function и помещает его на вершину стека оценки
  2. Еще одна инструкция LOAD_NAME , чтобы найти переменную my_variable и поместить ее поверх стека оценки
  3. Инструкция LOAD_CONST для помещения буквального целочисленного значения 2 на вершину стека оценки
  4. A CALL_FUNCTION инструкция

Команда CALL_FUNCTION будет иметь аргумент 2, что указывает на то, что Python необходимо извлечь два позиционных аргумента из верхней части стека; тогда вызываемая функция будет наверху, и ее также можно будет извлечь (для функций с ключевыми аргументами используется другая инструкция — CALL_FUNCTION_KW , но с аналогичным принципом работы, и третья инструкция, CALL_FUNCTION_EX , используется для вызовов функций, которые включают распаковку аргументов с помощью операторов * или ** ).Как только Python получит все это, он выделит новый фрейм в стеке вызовов, заполнит локальные переменные для вызова функции и выполнит байт-код my_function внутри этого фрейма. Как только это будет сделано, кадр будет извлечен из стека вызовов, а в исходном кадре возвращаемое значение my_function будет помещено поверх стека оценки.

Доступ и понимание байт-кода Python

Если вы хотите поэкспериментировать с этим, модуль dis в стандартной библиотеке Python вам очень поможет; модуль dis предоставляет «дизассемблер» для байт-кода Python, что упрощает получение удобочитаемой версии и поиск различных инструкций байт-кода.Документация для модуля dis рассматривает его содержимое и предоставляет полный список инструкций байт-кода вместе с тем, что они делают и какие аргументы они принимают.

Например, чтобы получить список байт-кода для функции hello () , приведенной выше, я ввел его в интерпретатор Python, а затем запустил:

 

Функция dis.dis () будет дизассемблировать функцию, метод, класс, модуль, скомпилированный объект кода Python или строковый литерал, содержащий исходный код, и распечатать версию, удобную для чтения.Еще одна удобная функция в модуле dis distb () . Вы можете передать ему объект трассировки Python или вызвать его после возникновения исключения, и он будет дизассемблировать самую верхнюю функцию в стеке вызовов во время исключения, распечатать свой байт-код и вставить указатель на инструкцию, которая вызвала исключение.

Также полезно посмотреть на скомпилированные объекты кода, которые Python создает для каждой функции, поскольку при выполнении функции используются атрибуты этих объектов кода.Вот пример функции hello () :

 

>>> hello .__ code__
<объект кода hello at 0x104e46930, файл "", строка 1>
>>> hello .__ code __. Co_consts
(None, 'Hello, World!')
>>> hello .__ code __. co_varnames
()
>>> привет .__ code __. co_names
('print',)

Кодовый объект доступен как атрибут __code__ функции и несет несколько важных атрибутов:

  • co_consts — кортеж любых литералов, которые встречаются в теле функции
  • co_varnames — это кортеж, содержащий имена любых локальных переменных, используемых в теле функции
  • co_names — это кортеж любых нелокальных имен, на которые есть ссылки в теле функции

Многие инструкции байт-кода — особенно те, которые загружают значения, которые должны быть помещены в стек или сохраняют значения в переменных и атрибутах, — используют индексы в этих кортежах в качестве своих аргументов.

Итак, теперь мы можем понять список байт-кода функции hello () :

  1. LOAD_GLOBAL 0 : указывает Python искать глобальный объект, на который ссылается имя с индексом 0 из co_names (что является функцией print ), и помещает его в стек оценки
  2. LOAD_CONST 1 : принимает буквальное значение с индексом 1 из co_consts и толкает его (значение с индексом 0 — это литерал None , который присутствует в co_consts , потому что вызовы функций Python имеют неявное возвращаемое значение Нет , если не достигнуто явное return )
  3. CALL_FUNCTION 1 : указывает Python вызвать функцию; ему нужно будет извлечь из стека один позиционный аргумент, тогда новая вершина стека будет функцией для вызова.

«Необработанный» байт-код — как байты, нечитаемые человеком — также доступен в кодовом объекте как атрибут co_code . Вы можете использовать список dis.opname для поиска имен инструкций байт-кода по их десятичным значениям байтов, если вы хотите попытаться вручную дизассемблировать функцию.

Использование байт-кода

Теперь, когда вы дочитали до этого места, вы можете подумать: «Хорошо, я думаю, это круто, но в чем практическая ценность этого знания?» Оставив в стороне любопытство ради любопытства, понимание байт-кода Python полезно в нескольких отношениях.

Во-первых, понимание модели выполнения Python поможет вам понять свой код. Людям нравится шутить о том, что C является своего рода «портативным ассемблером», где вы можете хорошо догадываться о том, в какие машинные инструкции превратится конкретный фрагмент исходного кода C. Понимание байт-кода даст вам те же возможности, что и в Python — если вы можете предвидеть, в какой байт-код превращается ваш исходный код Python, вы сможете принимать более обоснованные решения о том, как его писать и оптимизировать.

Во-вторых, понимание байт-кода — полезный способ ответить на вопросы о Python.Например, я часто вижу, как начинающие программисты Python задаются вопросом, почему одни конструкции быстрее других (например, почему {} быстрее, чем dict () ). Знание того, как получить доступ и прочитать байт-код Python, позволит вам найти ответы (попробуйте: dis.dis ("{}") против dis.dis ("dict ()") ).

Наконец, понимание байт-кода и того, как Python выполняет его, дает полезный взгляд на конкретный вид программирования, которым программисты Python не часто занимаются: программирование на основе стека.Если вы когда-либо использовали стек-ориентированный язык, такой как FORTH или Factor, это может быть старая новость, но если вы не знакомы с этим подходом, изучение байт-кода Python и понимание того, как работает его стек-ориентированная модель программирования, — это изящная задача. способ расширить свои знания в области программирования.

Дополнительная литература

Если вы хотите узнать больше о байт-коде Python, виртуальной машине Python и о том, как они работают, я рекомендую эти ресурсы:


Чтобы узнать больше, посетите доклад Джеймса Беннета «Немного о байтах: понимание байт-кода Python» на PyCon Cleveland 2018.

.

Использование Python Как я могу прочитать биты в байтах?

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

Загрузка…

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *