a peek into python's metaclass and bytecode from a smalltalk user

42
A Peek into Python’s Metaclass and Bytecode as a Smalltalk User Koan-Sin Tan freedom_at_computer.org COSCUP 2015, Taipei

Upload: koan-sin-tan

Post on 14-Apr-2017

5.043 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: A peek into Python's Metaclass and Bytecode from a Smalltalk User

A Peek into Python’s Metaclass and Bytecode as a Smalltalk User

Koan-Sin Tan freedom_at_computer.org

COSCUP 2015, Taipei

Page 2: A peek into Python's Metaclass and Bytecode from a Smalltalk User

why this topic• ‘I was only vaguely aware of Smalltalk at the time; I remember being surprised

by its use of metaclasses (which is quite different from that in Python or Ruby!) when I read about them much later. Smalltalk's bytecode was a bigger influence of Python's bytecode though. I'd read about it in a book by Adele Goldberg and others, I believe "Smalltalk-80: The Language and its Implementation”’ [1]

• And, I had a talk comparing Ruby’s metaclass and byte code to Smalltalk’s in the end of 2012 [2]

• google “smalltalk and ruby bytecode”

[1] http://python-history.blogspot.com/2013/10/origin-of-metaclasses-in-python.html

[2] http://www.slideshare.net/kstan2/smalltalk-and-ruby-20121208-15542185

Page 3: A peek into Python's Metaclass and Bytecode from a Smalltalk User

what won’t be discussed• anything beyond simple metaclass and bytecode

comparisons

• So, no inline cache if you was in the SpiderMonkey talk yesterday. Yes, I know inline cache was for Smalltalk-80. If you want to know how inline cache is used in Smalltalk, read an excellent article [1]

• And no other stuff in object model (whatever object model means to you)

[1] http://www.mirandabanda.org/cogblog/2011/03/01/build-me-a-jit-as-fast-as-you-can/

Page 4: A peek into Python's Metaclass and Bytecode from a Smalltalk User

http://www.world.st/learn/books

Page 5: A peek into Python's Metaclass and Bytecode from a Smalltalk User

who am I• Learnt to write program on MPF-II

• Used to be a programming language junkie

• Learnt a bit Smalltalk during early '90s, use it on and off

• Recent interest in ST-80 because of Scratch and BYOB/SNAP

• Knew little about Python

Page 6: A peek into Python's Metaclass and Bytecode from a Smalltalk User

the first smalltalk-80 I used is on Sun’s SunView, image from Wikipedia

Page 7: A peek into Python's Metaclass and Bytecode from a Smalltalk User

http://squeak.org

http://pharo.org

Two Popular Open Source Smalltalk Systems

Page 8: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Scratch was written in Smalltalk

8

http://news.softpedia.com/images/reviews/large/Scratch_Large_001.png

Page 9: A peek into Python's Metaclass and Bytecode from a Smalltalk User

What I knew about python

• It’s a popular scripting language

• My classmate told me about Python in ’94 or ’95

• IPython notebook: using python and R to share notes

• And of course, Python is more than Smalltalk-80

Page 10: A peek into Python's Metaclass and Bytecode from a Smalltalk User

share my understanding of python metaclass as bytecode as a common programmer

Page 11: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Quick Overview of ST-80• Object-Oriented

Programming

• OO GUI environment, IDE

• stack VM, bytecode

• Lambda, functional language, block

• Message passing

• Design Pattern:

• if you read the GoF book, you ran into lots of Smalltalk patterns before

• Learning/educational

• Logo and the dream of Dynabook

Page 12: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Metaclass

Page 13: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Metaclass in ST-80• From "purple book", Chap 16

1. Every class is ultimately a subclass of class Object, except for Object itself, which has no superclass. In particular, Class is a subclass of ClassDescription, which is a subclass of Behavior which is a subclass of Object

2. Every object is an instance of a class

3. Every class is an instance of a metaclass

4. All metaclasses are subclasses of Class

5. Every metaclass is an instance of Metaclass

6. The method of Class and it superclasses support the behavior common to all objects that are classes

7. The methods of instances of Metaclass add the behavior specific to particular classes

Page 14: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Smalltalk Object Model

•10 factorial --> 3628800 •10 factorial class --> SmallInteger •SmallInteger superclass --> Integer •Integersuperclass--> Number•Numbersuperclass--> Magnitude•Magnitudesuperclass--> Object•Objectsuperclass-->ProtoObject•ProtoObjectsuperclass-->nil

14

Page 15: A peek into Python's Metaclass and Bytecode from a Smalltalk User

• 10 factorial class allSuperclasses --> an OrderedCollection(Integer Number Magnitude Object ProtoObject)

Page 16: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Python intprint(type(1))<class 'int'>

print(type(1).__base__)<class 'object'>

print(type(1).__base__.__base__)None

print(type(1).__mro__)(<class 'int'>, <class ‘object'>)

• So int, then object, no hierarchy for numbers. How about container types, e.g., set and dict? NO!

• Fortunately, there is another path since python3000(?)

• There is PEP 3119 “Abstract Base Classes” (ABC) and its companion PEP 3141. So, there are ABCs for numbers in module numbers and collections module collections 

numbers.Integral.__base__<class 'numbers.Rational'>

numbers.Integral.__base__.__base__<class 'numbers.Real'>

numbers.Integral.__base__.__base__.__base__<class 'numbers.Complex'>

numbers.Integral.__base__.__base__.__base__.__base__<class 'numbers.Number'>

numbers.Integral.__base__.__base__.__base__.__base__.__base__<class ‘object'>

numbers.Integral.__mro__(<class 'numbers.Integral'>, <class 'numbers.Rational'>, <class 'numbers.Real'>, <class 'numbers.Complex'>, <class 'numbers.Number'>, <class 'object'>)

https://docs.python.org/3/reference/datamodel.html

Number

Complex

Real

Integral

10

Rational

object

Keyinstance-of

Page 17: A peek into Python's Metaclass and Bytecode from a Smalltalk User

a class named Metaclass

SmallIntegerclass-->SmallIntegerclassIntegerclass-->IntegerclassNumberclass-->NumberclassMagnitudeclass-->MagnitudeclassObjectclass-->ObjectclassProtoObjectclass-->ProtoObjectclass

SmallInteger class superclass --> Integer class Integer class superclass --> Number class Number class superclass --> Magnitude class Magnitude class superclass --> Object class Object class superclass --> ProtoObject class ProtoObject class superclass --> Class

Class class --> Class class Class class class --> Metaclass

Metaclass class --> Metaclass class Metaclass class class --> Metaclass

Page 18: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Object

Magnitude

Number

Object class

Magnitude class

Number class

Keyinstance-of

Integer class

SmallInteger SmallInteger class

10

Integer

ProtoObject ProtoObject class

figures modified from “Pharo by Example”

18

Page 19: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Object

Magnitude

Number

Object class

Magnitude class

Number class

Keyinstance-of

Integer class

SmallInteger SmallInteger class

10

Integer

ProtoObject ProtoObject class

Class

Class class

Metaclass

Metaclass class

19

Page 20: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Class class class --> Metaclass Metaclass superclass --> ClassDescription ClassDescription superclass --> Behavior Behavior superclass --> ObjectClass class superclass --> ClassDescription class ClassDescription class superclass --> Behavior class Behavior class superclass --> Object class

20

Page 21: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Object

Magnitude

Number

Object class

Magnitude class

Number class

Keyinstance-of

Integer classSmallInteger

SmallInteger class10

Integer

ProtoObject

ProtoObject class

Class

Class class

Metaclass

Metaclass class

ClassDescription

BehaviorClassDescription class

Behavior class

21

Page 22: A peek into Python's Metaclass and Bytecode from a Smalltalk User

from “PBE”

Page 23: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Python metaclasstype(1).__class__<class ‘type'>

type(1).__class__.__class_<class ‘type’>

numbers.Integral.__class__<class ‘abc.ABC’>

numbers.Rationl.__class__<class ‘abc.ABC’>

abc.ABC.__class__<class ‘type’>————————————————————————————————

import disdef testClass(): class Foo: def foo(self, a, b): return a + b bar = Foo() bar.foo(2+3) dis.dis(testClass)

3 0 LOAD_BUILD_CLASS 1 LOAD_CONST 1 (<code object Foo at 0x1101aee40, file "<ipython-input-37-12f9deff1239>", line 3>) 4 LOAD_CONST 2 ('Foo') 7 MAKE_FUNCTION 0 10 LOAD_CONST 2 ('Foo') 13 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 16 STORE_FAST 0 (Foo)

6 19 LOAD_FAST 0 (Foo) 22 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 25 STORE_FAST 1 (bar)

7 28 LOAD_FAST 1 (bar) 31 LOAD_ATTR 0 (foo) 34 LOAD_CONST 5 (5) 37 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 40 POP_TOP 41 LOAD_CONST 0 (None) 44 RETURN_VALUE

Page 24: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Python metaclass• LOAD_BUILD_CLASS:LOAD_BUILD_CLASS¶Pushes builtins.__build_class__() onto the stack. It is later called by CALL_FUNCTION to construct a class [1]

• builtins.__build_class__ static PyMethodDef builtin_methods[] = { {"__build_class__", (PyCFunction)builtin___build_class__, METH_VARARGS | METH_KEYWORDS, build_class_doc}, {"__import__", (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc}, {"abs", builtin_abs, METH_O, abs_doc},

[1] https://docs.python.org/3/library/dis.html

• builtin_build_class__

• if no specific metaclass was given and there no special metaclass in base classes of a class, the default metaclass is the ‘type’

• BTW, as far as I can tell there are some classes have non-type metaclasses in standard Python distribution. We know numbers and collections

• try “grep metaclass=“ in python source code

Page 25: A peek into Python's Metaclass and Bytecode from a Smalltalk User

builtin_build_class__()static PyObject * builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) { …

if (meta == NULL) { /* if there are no bases, use type: */ if (PyTuple_GET_SIZE(bases) == 0) { meta = (PyObject *) (&PyType_Type); } /* else get the type of the first base */ else { PyObject *base0 = PyTuple_GET_ITEM(bases, 0); meta = (PyObject *) (base0->ob_type); } Py_INCREF(meta); isclass = 1; /* meta is really a class */ } … }

Page 26: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Some others

Page 27: A peek into Python's Metaclass and Bytecode from a Smalltalk User

another use of python’s ABCs

isinstance(10, int)True

isinstance(10, complex)false

isinstance(10, numbers.Rational)True

isinstance(10, numbers.Real)True

isinstance(10, numbers.Complex)True

isinstance(10, numbers.Number)True

Number

Complex

Real

Integral

10

Rational

object

Keyinstance-of

Page 28: A peek into Python's Metaclass and Bytecode from a Smalltalk User

bytecode

Page 29: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Bytecode• Bytecode is not new at all

• Smalltalk is one of early bytecode users

• Smalltalk bytecode

• end of Chapter 26, http://www.mirandabanda.org/bluebook/bluebook_chapter26.html#TheBytecodes26

• Chap. 28, http://www.mirandabanda.org/bluebook/bluebook_chapter28.html

Page 30: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Smalltalk bytecode categories• pushes• indicatesthesourceofanobjecttobeaddedtothetopoftheinterpreter'sstack

• stores• indicatesthevariablewhosevalueshouldbechanged

• sends• specifiestheselectorofamessagetobesentandhowmanyargumentsitshouldhave.

• returns• avalueisreturnedforthemessagethatinvokedthatCompiledMethod

• andjumps• youknowwhattheseare

30

Page 31: A peek into Python's Metaclass and Bytecode from a Smalltalk User

SmalltalkbytecodesRange Bits FuncHon0-15 0000iiii PushReceiverVariable#iiii16-31 0001iiii PushTemporaryLocaHon#iiii32-63 001iiiii PushLiteralConstant#iiiii64-95 010iiiii PushLiteralVariable#iiiii96-103 01100iii PopandStoreReceiverVariable#iii104-111 01101iii PopandStoreTemporaryLocaHon#iii112-119 01110iii Push(receiver,true,false,nil,-1,0,1,2)[iii]120-123 011110ii Return(receiver,true,false,nil)[ii]FromMessage124-125 0111110i ReturnStackTopFrom(Message,Block)[i]126-127 0111111i unused128 10000000jjkkkkkk Push(ReceiverVariable,TemporaryLocaHon,LiteralConstant,LiteralVariable)[jj]#kkkkkk129 10000001jjkkkkkk Store(ReceiverVariable,TemporaryLocaHon,Illegal,LiteralVariable)[jj]#kkkkkk130 10000010jjkkkkkk PopandStore(ReceiverVariable,TemporaryLocaHon,Illegal,LiteralVariable)[jj]#kkkkkk131 10000011jjjkkkkk SendLiteralSelector#kkkkkWithjjjArguments132 10000100jjjjjjjjkkkkkkkk SendLiteralSelector#kkkkkkkkWithjjjjjjjjArguments133 10000101jjjkkkkk SendLiteralSelector#kkkkkToSuperclassWithjjjArguments134 10000110jjjjjjjjkkkkkkkk SendLiteralSelector#kkkkkkkkToSuperclassWithjjjjjjjjArguments135 10000111 PopStackTop136 10001000 DuplicateStackTop137 10001001 PushAcHveContext138-143 unused144-151 10010iii Jumpiii+1(i.e.,1through8)152-159 10011iii PopandJump0nFalseiii+1(i.e.,1through8)160-167 10100iiijjjjjjjj Jump(iii-4)*256+jjjjjjjj168-171 101010iijjjjjjjj PopandJumpOnTrueii*256+jjjjjjjj172-175 101011iijjjjjjjj PopandJumpOnFalseii*256+jjjjjjjj176-191 1011iiii SendArithmeHcMessage#iiii192-207 1100iiii SendSpecialMessage#iiii208-223 1101iiii SendLiteralSelector#iiiiWithNoArguments224-239 1110iiii SendLiteralSelector#iiiiWith1Argument240-255 1111iiii SendLiteralSelector#iiiiWith2Arguments

31

Page 32: A peek into Python's Metaclass and Bytecode from a Smalltalk User

• Anexamplemethod,forCompiledMethod "to show how CompiledMethod works" | foo | foo := 'test'. ^ 1 + 2

• Bytecode:inSystemBrowserofSqueak/Pharo,wecanseebytecodeeasily17 <20> pushConstant: 'test' 18 <68> popIntoTemp: 0 19 <76> pushConstant: 1 20 <77> pushConstant: 2 21 <B0> send: + 22 <7C> returnTop

32

Page 33: A peek into Python's Metaclass and Bytecode from a Smalltalk User

foo    | foo |    foo := 'test'.    ^ 1 + 2

17 <20> pushConstant: ‘test'18 <68> popIntoTemp: 019 <76> pushConstant: 120 <77> pushConstant: 221 <B0> send: +22 <7C> returnTop

 def test(): foo = "test" return 1 + 2

dis.dis(test)

2 0 LOAD_CONST 1 ('test') 3 STORE_FAST 0 (foo)

3 6 LOAD_CONST 4 (3) 9 RETURN_VALUE

Page 34: A peek into Python's Metaclass and Bytecode from a Smalltalk User

foo: a and: b    "to show how CompiledMethod works"     | foo |     foo := 'test'.    ^ a + b

17 <20> pushConstant: 'test'18 <6A> popIntoTemp: 219 <10> pushTemp: 020 <11> pushTemp: 121 <B0> send: +22 <7C> returnTop

def test(a, b): foo = "test" return a + b

dis.dis(test)

2 0 LOAD_CONST 1 ('test') 3 STORE_FAST 2 (foo)

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

Page 35: A peek into Python's Metaclass and Bytecode from a Smalltalk User

displaying smalltalk bytecode

• CompiledMethod

• Class>>compiledMethodAt

• (Integer compiledMethodAt: #factorial) symbolic.

• (COSCUP2015 compiledMethodAt: #forCompiledMethod) symbolic.

(Integer compiledMethodAt: #factorial) symbolic.'29 <70> self30 <75> pushConstant: 031 <B6> send: =32 <99> jumpFalse: 35 33 <76> pushConstant: 134 <7C> returnTop 35 <70> self36 <75> pushConstant: 037 <B3> send: >38 <9E> jumpFalse: 46 39 <70> self40 <70> self41 <76> pushConstant: 142 <B1> send: -43 <D0> send: factorial44 <B8> send: *45 <7C> returnTop 46 <70> self47 <22> pushConstant: ''Not valid for negative integers'' 48 <E1> send: error:49 <87> pop50 <78> returnSelf '

(COSCUP2015 compiledMethodAt: #forCompiledMethod) symbolic.'17 <20> pushConstant: ''test'' 18 <68> popIntoTemp: 0 19 <76> pushConstant: 120 <77> pushConstant: 221 <B0> send: +22 <7C> returnTop '

Page 36: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Add a methodCOSCUP2015 compile: ' foo     ^42'

Page 37: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Add a class|myClassmyInstance| myClass:=Behaviornew."createanonbehavior"myClasscompile:'theAnswer^42'."addamethodforinstances"(myClasscompiledMethodAt:#theAnswer)symbolic. myInstance:=myClassnew."createaninstance"Transcriptshow:myInstancetheAnswer;cr."shows42”

Page 38: A peek into Python's Metaclass and Bytecode from a Smalltalk User

Python Bytecode• General

• Unary

• Binary

• In-place

• Misc

• print: PRINT_EXPR

• set, map, tuple, list, etc.

https://docs.python.org/3/library/dis.html

Page 39: A peek into Python's Metaclass and Bytecode from a Smalltalk User

foobar: a and: b    "to show how CompiledMethod works"     | foo |     foo := 'test'.    ^ Array with: (a + b) with: foo

25 <20> pushConstant: ‘test'26 <6A> popIntoTemp: 227 <41> pushLit: Array28 <10> pushTemp: 029 <11> pushTemp: 130 <B0> send: +31 <12> pushTemp: 232 <F2> send: with:with:33 <7C> returnTop

import disdef foo(a, b): foo = "test" return (a+b, foo)

dis.dis(foo)

3 0 LOAD_CONST 1 ('test') 3 STORE_FAST 2 (foo)

4 6 LOAD_FAST 0 (a) 9 LOAD_FAST 1 (b) 12 BINARY_ADD 13 LOAD_FAST 2 (foo) 16 BUILD_TUPLE 2 19 RETURN_VALUE

Page 40: A peek into Python's Metaclass and Bytecode from a Smalltalk User

observations• ST-80’s bytecode is relatively simple or say

primitive one

• Python’s bytecode has many some unique instructions and some specialized instructions for builtin types (for legacy or performance reasons, maybe)

• I know ST-80’s bytecode showed its age, but I am surprised that Python’s bytecode is low level and old too

Page 41: A peek into Python's Metaclass and Bytecode from a Smalltalk User

So?

• We walked through metaclass and bytecode in ST-80 and Python? Do you agree Guido’s words I cited previously

Page 42: A peek into Python's Metaclass and Bytecode from a Smalltalk User

https://www.moedict.tw/' ?font=ebas