Skip to content

Instantly share code, notes, and snippets.

@obriencj
Created May 6, 2018 13:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save obriencj/40adc3f6db4d32ea04d5d61e9b25feb5 to your computer and use it in GitHub Desktop.
Save obriencj/40adc3f6db4d32ea04d5d61e9b25feb5 to your computer and use it in GitHub Desktop.
building classes in python
def sample_1(parent=object):
class Sample(parent):
def __init__(self, val=None):
self.value = val
return Sample
def sample_2(parent=object):
class Sample(parent):
def __init__(self, val=None):
super().__init__(self)
self.value = val
return Sample
from dis import dis, show_code
from types import CodeType
def disassemble(obj, verbose=False):
if verbose:
show_code(obj)
print("Disassembly:")
dis(obj)
def find_code_const(obj, name):
obj = getattr(obj, "__code__", obj)
for c in obj.co_consts:
if isinstance(c, CodeType):
if c.co_name == name:
return c
else:
return None
# Sample1 = sample_1(object)
Sample1_class_code = find_code_const(sample_1, "Sample")
Sample1_init_code = find_code_const(Sample1_class_code, "__init__")
# Sample2 = sample_2(object)
Sample2_class_code = find_code_const(sample_2, "Sample")
Sample2_init_code = find_code_const(Sample2_class_code, "__init__")
if __name__ == "__main__":
print("\n === Sample1 class code ===")
disassemble(Sample1_class_code, True)
print("\n === Sample1 init code ===")
disassemble(Sample1_init_code, True)
print("\n === Sample2 class code ===")
disassemble(Sample2_class_code, True)
print("\n === Sample2 init code ===")
disassemble(Sample2_init_code, True)
# yuck.
@obriencj
Copy link
Author

obriencj commented May 6, 2018

Here's a side-by-side comparison of the Sample1 class and init code disassemblies
https://gist.github.com/obriencj/a32ad3de87487e4ee731625633f35aa0/revisions?diff=split

Here's how that __classcell__ closure is consumed by the type() call
https://github.com/python/cpython/blob/b1c70d0ffbb235def1deab62a744ffd9b5253924/Objects/typeobject.c#L2690

@obriencj
Copy link
Author

obriencj commented May 6, 2018

This is how the python 3.6 variation of a no-argument super() was implemented. When detected at compile time, it causes the compiler to inject a closure for a value named __class__ into any method code which uses the super. That __class__ closure is then captured in the class' namespace as the __classcell__. The type() call which creates a type from the namespace resulting from calling the class code will check for the cell in a __classcell__ attribute, and if it finds one, will set the cell contents to the resulting class instance that type has created.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment