Rock It 《ML》JupyterLab 【丁】Code《七》語義【六】解讀‧四‧Racket 二

150px-Greek_lc_lamda_thin.svg

λ 何謂也?

λ 表達式的『核心』理念是︰將『函數』應用於其『引數』;藉『抽象』形成『函數』。用著『簡潔』的『語法』,專注於『表徵』函數;視『函數』為計算的『規則』,深得『計算』之『內蘊』。其『語詞』表達『疏落』大方,概念『表現力』與『柔軔性』十足。故為『邏輯』、『數學』和『程式』理念之『聚寶盆』!!

辨明『異同』與分解『差別』是學習的功夫

真積力,久則入。

讓我們再次『觀止』λ 表達式的『定義』︰

變元集合 V = \{ v_1, v_2, v_3, \dots, v_n, \dots \}
抽象符號 『 λ  』與『 . 』,符號本身不是λ 表達項
結構括號 『 ( 』與『 ) 』,括號本身不是λ 表達項

λ 表達式的論域集合 \Lambda,由下面三條語法歸納定義︰

一、如果 x \in V ,那麼 x \in \Lambda

二、如果 x \in V 而且 M \in \Lambda ,那麼 ( \lambda x. M ) \in \Lambda

三、如果 M, N \in \Lambda ,那麼 ( M \  N) \in \Lambda

假使觀察一個合乎語法的 λ 表達式 (\lambda f.  (\lambda x. (f x))),然後問著『 f 』變元是指『什麼』?這個表達式最內層的 (f x) 是函式的應用,明寫 f 是某個函式,然而如何計算卻隻字未提。如此看來,所謂的『變元』也可以是『函式』。那要怎麼『詮釋』那個『 λ表達式』呢?由於我們不知道 f, x 指的是什麼?也可以說它們『未被定義』,假使『給與定義』,我們或許可以講『在這個解釋下』,該個『 λ表達式』的『語意』是『□□□』。比方談著『用量角器量角度』一事,假使︰

f =_{df} 用量角器量角度
x =_{df} 東西的角度

,那麼 (\lambda f.  (\lambda x. (f x))) 是說︰拿某種『待指定』的量角器來量『尚未說』之物的角度。

要是講到『計算某種三角函數的數值』時,設想︰

f =_{df} 某種三角函數的計算
x =_{df} 角度

,那麼 (\lambda f.  (\lambda x. (f x))) 是說︰用某個『待指定』的三角函數來計算『未輸入』的角度。
……

由於每個『變元』的『論域』未被定義,所以各種『詮釋』都是『可能的』,因此說『函式建構』是『抽象』的,『應用化約』是廣義的『計算結構』之『同等性』。這也使得『應用化約』成為『 λ 運算』的『核心原則』,也就是說『 \beta 化約』是最重要的『轉換規則』。

─── 《Λ 運算︰概念導引《二》

 

假使你還不知道『\lambda 表達式』是什麼?或可參讀

Λ 運算︰概念導引…》系列文本。

至於如何用『球拍』打球,作者也是新手,只能跟著 racket 文件走

 v7.2

Getting Started

To get started with Racket, download it from the web page and install it. If you are a beginner or would like to use a graphical environment to run programs, run theDrRacket executable. Otherwise, the racket executable will run a command-line Read-Eval-Print-Loop (REPL).

On Windows, you can start DrRacket from the Racket entry in the Start menu. In Windows Vista or newer, you can just type DrRacket. You can also run it from its folder, which you can find in Program FilesRacketDrRacket.

On Mac OS, double click on the DrRacket icon. It is probably in a Racket folder that you dragged into your Applications folder. If you want to use command-line tools, instead, Racket executables are in the “bin” directory of the Racket folder (and if you want to set your PATH environment variable, you’ll need to do that manually).

On Unix (including Linux), the drracket executable can be run directly from the command-line if it is in your path, which is probably the case if you chose a Unix-style distribution when installing. Otherwise, navigate to the directory where the Racket distribution is installed, and the drracket executable will be in the “bin” subdirectory.

If you are new to programming or if you have the patience to work through a textbook:

If you’re already a programmer and you’re in more of a hurry:

Of course, you should feel free to mix and match the above two tracks, since there is information in each that is not in the other.

 

帶張鳥瞰小抄

Racket Cheat Sheet

 

先來點實際經驗

Quick: An Introduction to Racket with Pictures

Matthew Flatt

This tutorial provides a brief introduction to the Racket programming language by using one of its picture-drawing libraries. Even if you don’t intend to use Racket for your artistic endeavours, the picture library supports interesting and enlightening examples. After all, a picture is worth five hundred “hello world”s.

Along the same lines, we assume that you will run the examples using DrRacket. Using DrRacket is the fastest way to get a sense of what the language and system feels like, even if you eventually use Racket with Emacs, vi, or some other editor.

再多練習!

 

 

 

 

 

 

 

 

Rock It 《ML》JupyterLab 【丁】Code《七》語義【六】解讀‧四‧Racket 一

甲骨文名

金文名

論語‧子路篇

子路曰:『衛君待子而為政,子將奚先?』子曰:『必也正名乎!』子路曰:『有是哉,子之迂也!奚其正?』子曰:『野哉由也!君子於其所不知,蓋闕如也。名不正,則言不順;言不順,則事不成;事不成,則禮樂不興;禮樂不興,則刑罰不中;刑罰不中,則民無所措手足。故君子名之必可言也,言之必可行也。君子於其言,無所苟而已矣。』

説文解字》:

名,自命也。从口,从夕。夕者,冥也。冥不相見,故以口自名。

古來『名字』的傳統,幼時口呼『命名』,成年書寫『取字』。

許多讀過『λ 運算』的人,多半覺得它既『難懂』又『難解』。這是有原因的,如果用『抽象辦法』談論著『抽象事物』,又不知道為何如此表述當然『難懂』;假使不能『困思勉行』多次的『深思熟慮』,以至於能夠一旦了悟那就自然『難解』。通常越是『基本』的概念,由於太過『直覺』了,反而容易『誤解』。就像化學元素『週期表』上的元素不過一一八個,它所構成的世界卻是千嬌萬媚繁多複雜,要講『』的『性質』與『作用』,也許一大本書都不能窮盡,但換個方向說鐵不就是日用之物的嗎?

邱奇發展『λ 運算』Lambda calculus,這裡的『calculus』不是指『微積分』,是用著『函式』Function 和『變元』Variable 的概念,來談論『計算』一事是什麼?複雜的『函式』是如何清晰明白無歧異的『結構』而成?『變元』的『替換』Substitution 規則,要如何系統化的處理變元替換時『異物同名』衝突之問題?如果從『函式求值』上講,一個『λ 表達式』用著怎樣的『規則』可以『轉換』成為『同等』equivalent 的另一個 λ 表達式呢?……種種。假使給定了兩個『λ 表達式』是否會有一個普適的『演算法』能夠判定彼此間的『同等性』呢?……等等。

─── 《Λ 運算︰概念導引《一》

 

不覺間閉上眼睛,闔起了

Python: The Full Monty: A Tested Semantics for the Python Programming Language

文本︰

 

只覺腦海裡迴盪著︰這 \lambda 語言,豈宜今日再談?

 

更何況僅聽聞過『球拍』大名︰

 

又不熟悉

Beau­ti­ful Racket

 

內容,怕是不該講吧!

姑且遁入『童子問』耶??

易童子問第一

乾,元亨利貞

童子問曰:「乾,元亨利貞」何謂也?

曰:眾辭淆亂,質諸聖。《彖》者,聖人之言也。

童子曰:然則《乾》無四德,而《文言》非聖人書乎?

曰:是魯穆姜之言也,在襄公之九年。

 

假使思考歐陽修為什麼寫《易童子問》?

歐陽修(1007年-1072年),字永叔,號醉翁、六一居士,諡文忠,北宋吉州廬陵(今屬江西省永豐縣)人。(歐陽修生平請參考維基百科

所著《易童子問》三卷,收錄於《歐陽文忠公集》(或簡稱《文忠集》)第七十六至七十八卷,以問答的方式表達其對於《易經》的一些見解。每個問題都以「童子問」做為開始。

文忠公是第一位對於《十翼》以及諸如「四元德」等傳統見解提出質疑與抨擊的大儒,而這些見解也影響了後世許多學者,甚至得到近現代許多學者考證的支持。

除了較為知名的《易童子問》三卷之外,易學網另外也收錄《文忠集》中其他與《易經》相關的論述,以完整呈現歐陽修對《易經》所提出的見解。

 

,難到不因懷疑也!『聖人』也是『人』??果能『全知』乎!!

─── 摘自《W!O+ 的《小伶鼬工坊演義》︰ 一窺全豹之系統設計《童子問》

 

然有道是︰傳義理而已矣!!

sudo apt-get install racket

git clone https://github.com/brownplt/lambda-py

cd lambda-py/base/

rock64@rock64:~/lambda-py/base$ echo "print('lambda-py works')" | racket python-main.rkt --interp
lambda-py works

 

 

 

 

 

 

 

Rock It 《ML》JupyterLab 【丁】Code《七》語義【六】解讀‧四‧Lambda-py

吉多·范羅蘇姆 Guido van Rossum 先生喜歡看『蒙提·派森的飛行馬戲團』── Monty Python’s Flying Circus,所以把他創始的程式語言叫做 『 Python 』 。Python 巨蟒,《爾雅·釋魚》蟒,王蛇。又《註》蟒,蛇最大者,故曰王蛇。正是『小王子』一書上所說的︰

吞了象,看起來像帽子的那個。

─── 《『騛罿』── 非同的禪!!

 

不想剛出城郭,見一 \lambda_{\pi} 旗幟,旗下寫著

Python: The Full Monty: A Tested Semantics for the Python Programming Language

Joe Gibbs Politz, Alejandro Martinez, Matthew Milano, Sumner Warren, Daniel Patterson, Junsong Li, Anand Chitipothu, Shriram Krishnamurthi

ACM SIGPLAN Conference on Object-Oriented Programming Systems, Languages & Applications, 2013

Abstract

We present a small-step operational semantics for the Python programming language. We present both a core language for Python, suitable for tools and proofs, and a translation process for converting Python source to this core. We have tested the composition of translation and evaluation of the core for conformance with the primary Python implementation, thereby giving confidence in the fidelity of the semantics. We briefly report on the engineering of these components. Finally, we examine subtle aspects of the language, identifying scope as a pervasive concern that even impacts features that might be considered orthogonal.

Comment

To play with the artifact, visit http://cs.brown.edu/research/plt/dl/lambda-py/ae/.

Paper

PDF

 

文字,怎能錯過?

略讀綱要,沉吟意指︰

 

實在大哉論也!

 

忘情駐足,想起

名稱已定事早生,

意義即揚思難了。

 

猶記初讀曾經意徘回☻

9.1. A Word About Names and Objects

Objects have individuality, and multiple names (in multiple scopes) can be bound to the same object. This is known as aliasing in other languages. This is usually not appreciated on a first glance at Python, and can be safely ignored when dealing with immutable basic types (numbers, strings, tuples). However, aliasing has a possibly surprising effect on the semantics of Python code involving mutable objects such as lists, dictionaries, and most other types. This is usually used to the benefit of the program, since aliases behave like pointers in some respects. For example, passing an object is cheap since only a pointer is passed by the implementation; and if a function modifies an object passed as an argument, the caller will see the change — this eliminates the need for two different argument passing mechanisms as in Pascal.

9.2. Python Scopes and Namespaces

Before introducing classes, I first have to tell you something about Python’s scope rules. Class definitions play some neat tricks with namespaces, and you need to know how scopes and namespaces work to fully understand what’s going on. Incidentally, knowledge about this subject is useful for any advanced Python programmer.

Let’s begin with some definitions.

A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that’s normally not noticeable in any way (except for performance), and it may change in the future. Examples of namespaces are: the set of built-in names (containing functions such as abs(), and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is absolutely no relation between names in different namespaces; for instance, two different modules may both define a functionmaximize without confusion — users of the modules must prefix it with the module name.

By the way, I use the word attribute for any name following a dot — for example, in the expression z.real, real is an attribute of the object z. Strictly speaking, references to names in modules are attribute references: in the expressionmodname.funcname, modname is a module object and funcname is an attribute of it. In this case there happens to be a straightforward mapping between the module’s attributes and the global names defined in the module: they share the same namespace! [1]

Attributes may be read-only or writable. In the latter case, assignment to attributes is possible. Module attributes are writable: you can write modname.the_answer = 42. Writable attributes may also be deleted with the del statement. For example, del modname.the_answer will remove the attribute the_answer from the object named by modname.

Namespaces are created at different moments and have different lifetimes. The namespace containing the built-in names is created when the Python interpreter starts up, and is never deleted. The global namespace for a module is created when the module definition is read in; normally, module namespaces also last until the interpreter quits. The statements executed by the top-level invocation of the interpreter, either read from a script file or interactively, are considered part of a module called __main__, so they have their own global namespace. (The built-in names actually also live in a module; this is called builtins.)

The local namespace for a function is created when the function is called, and deleted when the function returns or raises an exception that is not handled within the function. (Actually, forgetting would be a better way to describe what actually happens.) Of course, recursive invocations each have their own local namespace.

A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.

Although scopes are determined statically, they are used dynamically. At any time during execution, there are at least three nested scopes whose namespaces are directly accessible:

  • the innermost scope, which is searched first, contains the local names
  • the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
  • the next-to-last scope contains the current module’s global names
  • the outermost scope (searched last) is the namespace containing built-in names

If a name is declared global, then all references and assignments go directly to the middle scope containing the module’s global names. To rebind variables found outside of the innermost scope, the nonlocal statement can be used; if not declared nonlocal, those variables are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).

Usually, the local scope references the local names of the (textually) current function. Outside functions, the local scope references the same namespace as the global scope: the module’s namespace. Class definitions place yet another namespace in the local scope.

It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)

A special quirk of Python is that – if no global statement is in effect – assignments to names always go into the innermost scope. Assignments do not copy data — they just bind names to objects. The same is true for deletions: the statement del x removes the binding of x from the namespace referenced by the local scope. In fact, all operations that introduce new names use the local scope: in particular, import statements and function definitions bind the module or function name in the local scope.

The global statement can be used to indicate that particular variables live in the global scope and should be rebound there; the nonlocal statement indicates that particular variables live in an enclosing scope and should be rebound there.

 

 

 

 

 

 

 

Rock It 《ML》JupyterLab 【丁】Code《七》語義【六】解讀‧三外

漫步瀏覽山城景緻,不覺間走出廓外。眼前視野一片開闊,正可以欣賞對舉論辨也︰

Dynamic Typing: What the Compiler Doesn’t Know

One thing you’ve probably heard is that Python is a “dynamic” language—particularly that it’s “dynamically typed”. The work we’ve done to this point sheds some light on this description.

One of the things “dynamic” means in this context is that a lot of work is done at run time. We saw earlier that the Python compiler doesn’t have much information about what the code actually does. For example, consider the short function mod below. mod takes two arguments and returns the first modulo the second. In the bytecode, we see that the variables a and b are loaded, then the bytecode BINARY_MODULO performs the modulo operation itself.

>>> def mod(a, b):
...    return a % b
>>> dis.dis(mod)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 BINARY_MODULO
              7 RETURN_VALUE
>>> mod(19, 5)
4

Calculating 19 % 5 yields 4—no surprise there. What happens if we call it with different arguments?

>>> mod("by%sde", "teco")
'bytecode'

What just happened? You’ve probably seen this syntax before, but in a different context:

>>> print("by%sde" % "teco")
bytecode

Using the symbol % to format a string for printing means invoking the instruction BINARY_MODULO. This instruction mods together the top two values on the stack when the instruction executes—regardless of whether they’re strings, integers, or instances of a class you defined yourself. The bytecode was generated when the function was compiled (effectively, when it was defined) and the same bytecode is used with different types of arguments.

The Python compiler knows relatively little about the effect the bytecode will have. It’s up to the interpreter to determine the type of the object that BINARY_MODULO is operating on and do the right thing for that type. This is why Python is described as dynamically typed: you don’t know the types of the arguments to this function until you actually run it. By contrast, in a language that’s statically typed, the programmer tells the compiler up front what type the arguments will be (or the compiler figures them out for itself).

The compiler’s ignorance is one of the challenges to optimizing Python or analyzing it statically—just looking at the bytecode, without actually running the code, you don’t know what each instruction will do! In fact, you could define a class that implements the __mod__ method, and Python would invoke that method if you use % on your objects. So BINARY_MODULO could run any code at all!

Just looking at the following code, the first calculation of a % b seems wasteful.

def mod(a,b):
    a % b
    return a %b

Unfortunately, a static analysis of this code—the kind of you can do without running it—can’t be certain that the first a % b really does nothing. Calling __mod__ with% might write to a file, or interact with another part of your program, or do literally anything else that’s possible in Python. It’s hard to optimize a function when you don’t know what it does! In Russell Power and Alex Rubinsteyn’s great paper “How fast can we make interpreted Python?”, they note, “In the general absence of type information, each instruction must be treated as INVOKE_ARBITRARY_METHOD.”

Conclusion

Byterun is a compact Python interpreter that’s easier to understand than CPython. Byterun replicates CPython’s primary structural details: a stack-based interpreter operating on instruction sets called bytecode. It steps or jumps through these instructions, pushing to and popping from a stack of data. The interpreter creates, destroys, and jumps between frames as it calls into and returns from functions and generators. Byterun shares the real interpreter’s limitations, too: because Python uses dynamic typing, the interpreter must work hard at run time to determine the correct behavior of a program.

I encourage you to disassemble your own programs and to run them using Byterun. You’ll quickly run into instructions that this shorter version of Byterun doesn’t implement. The full implementation can be found at https://github.com/nedbat/byterun—or, by carefully reading the real CPython interpreter’s ceval.c, you can implement it yourself!

 

揣摩那『一動』『一靜』旨趣!

Peephole Optimizer

Peephole optimizer: optimize Python code objects. It is implemented with the BytecodeBlocksclass.

It is based on the peephole optimizer of CPython 3.6 which is written in C.

Example

Code:

import dis
from bytecode.peephole_opt import PeepholeOptimizer

code = compile('print(5+5)', '<string>', 'exec')
print("Non-optimized:")
dis.dis(code)
print()

code = PeepholeOptimizer().optimize(code)
print("Optimized:")
dis.dis(code)

Output of Python 3.6 patched with the PEP 511 with python -o noopt (to disable the builtin peephole optimizer):

Non-optimized:
  1           0 LOAD_NAME                0 (print)
              3 LOAD_CONST               0 (5)
              6 LOAD_CONST               0 (5)
              9 BINARY_ADD
             10 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             13 POP_TOP
             14 LOAD_CONST               1 (None)
             17 RETURN_VALUE

Optimized:
  1           0 LOAD_NAME                0 (print)
              3 LOAD_CONST               0 (10)
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE

……

Optimizations

Optimizations implemented in the peephole optimizer:

  • Constant folding
    • unary operations: +a, -a, ~a
    • binary operations:
      • a + b, a – b, a * b, a / b, a // b, a % b, a ** b
      • a << b, a >> b, a & b, a | b, a ^ b
    • BUILD_TUPLE: convert to a constant
    • Replace BUILD_TUPLE <n> + UNPACK_SEQUENCE <n> and BUILD_LIST <n> + UNPACK_SEQUENCE <n> with ROT_TWO (2 arguments) or ROT_THREE+ROT_TWO (3 arguments). For BUILD_LIST, if inputs are LOAD_CONST, rewrite LOAD_CONST in the reverse order.
    • BUILD_LIST + COMPARE_OP(in/not in): convert list to tuple
    • BUILD_SET + COMPARE_OP(in/not in): convert set to frozenset
    • COMPARE_OP:
      • replace not(a is b) with a is not b
      • replace not(a is not b) with a is b
      • replace not(a in b) with a not in b
      • replace not(a not in b) with a in b
  • Remove NOP instructions
  • Dead code elimination
    • Remove unreachable code after a final operation (Instr.is_final())
    • Remove unreachable blocks (Block)
  • Replace UNARY_NOT+POP_JUMP_IF_FALSE with POP_JUMP_IF_TRUE
  • Optimize jumps
    • Replace unconditional jumps to RETURN_VALUE with RETURN_VALUE
    • Replace jumps to unconditional jumps with jumps to the final target
    • Remove unconditional jumps to the following block

For tuples, constant folding is only run if the result has 20 items or less.

By design, only basic optimizations can be implemented. A peephole optimizer has a narrow view on the bytecode (a few instructions) and only a very limited knownledge of the code.

 

反思聚物比類之所以重要耶?

 

 

 

 

 

 

 

Rock It 《ML》JupyterLab 【丁】Code《七》語義【六】解讀‧三…Λ…

終究紙筆手腦解讀『位元組碼』 bytecode 太過笨拙,因此推薦短短五百行原始碼之

Allison is an engineer at Dropbox, where she helps maintain one of the largest networks of Python clients in the world. Before Dropbox, she was a facilitator at the Recurse Center, a writers retreat for programmers in New York. She’s spoken at PyCon North America about Python internals and loves weird bugs. She blogs at akaptur.com.

(This chapter is also available in Simplified Chinese).

Introduction

Byterun is a Python interpreter implemented in Python. Through my work on Byterun, I was surprised and delighted to discover that the fundamental structure of the Python interpreter fits easily into the 500-line size restriction. This chapter will walk through the structure of the interpreter and give you enough context to explore it further. The goal is not to explain everything there is to know about interpreters—like so many interesting areas of programming and computer science, you could devote years to developing a deep understanding of the topic.

Byterun was written by Ned Batchelder and myself, building on the work of Paul Swartz. Its structure is similar to the primary implementation of Python, CPython, so understanding Byterun will help you understand interpreters in general and the CPython interpreter in particular. (If you don’t know which Python you’re using, it’s probably CPython.) Despite its short length, Byterun is capable of running most simple Python programs1.

A Python Interpreter

Before we begin, let’s narrow down what we mean by “a Python interpreter”. The word “interpreter” can be used in a variety of different ways when discussing Python. Sometimes interpreter refers to the Python REPL, the interactive prompt you get by typing python at the command line. Sometimes people use “the Python interpreter” more or less interchangeably with “Python” to talk about executing Python code from start to finish. In this chapter, “interpreter” has a more narrow meaning: it’s the last step in the process of executing a Python program.

Before the interpreter takes over, Python performs three other steps: lexing, parsing, and compiling. Together, these steps transform the programmer’s source code from lines of text into structured code objects containing instructions that the interpreter can understand. The interpreter’s job is to take these code objects and follow the instructions.

You may be surprised to hear that compiling is a step in executing Python code at all. Python is often called an “interpreted” language like Ruby or Perl, as opposed to a “compiled” language like C or Rust. However, this terminology isn’t as precise as it may seem. Most interpreted languages, including Python, do involve a compilation step. The reason Python is called “interpreted” is that the compilation step does relatively less work (and the interpreter does relatively more) than in a compiled language. As we’ll see later in the chapter, the Python compiler has much less information about the behavior of a program than a C compiler does.

A Python Python Interpreter

Byterun is a Python interpreter written in Python. This may strike you as odd, but it’s no more odd than writing a C compiler in C. (Indeed, the widely used C compiler gcc is written in C.) You could write a Python interpreter in almost any language.

Writing a Python interpreter in Python has both advantages and disadvantages. The biggest disadvantage is speed: executing code via Byterun is much slower than executing it in CPython, where the interpreter is written in C and carefully optimized. However, Byterun was designed originally as a learning exercise, so speed is not important to us. The biggest advantage to using Python is that we can more easily implement just the interpreter, and not the rest of the Python run-time, particularly the object system. For example, Byterun can fall back to “real” Python when it needs to create a class. Another advantage is that Byterun is easy to understand, partly because it’s written in a high-level language (Python!) that many people find easy to read. (We also exclude interpreter optimizations in Byterun—once again favoring clarity and simplicity over speed.)

……

 

安裝非常容易

sudo pip3 uninstall byterun

/byterun

A Python implementation of a Python bytecode runner

Byterun

This is a pure-Python implementation of a Python bytecode execution virtual machine. I started it to get a better understanding of bytecodes so I could fix branch coverage bugs in coverage.py.

 

使用方法也十分簡單呦☆