Skip to content

Instantly share code, notes, and snippets.

@zbentley
Last active July 28, 2021 05:02
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 zbentley/4d37b2123458c6d9da2abc991ad3ca09 to your computer and use it in GitHub Desktop.
Save zbentley/4d37b2123458c6d9da2abc991ad3ca09 to your computer and use it in GitHub Desktop.
The worst object system in the world
"""
Are you tired of Python being slow? Wish it was faster in irrelevant ways at the cost of being massively slower
and more fragile in every relevant way? Tired of how costly it is to look up attributes on classes? Wish you could
"stop" a class when you're done with it, and cause all future interactions with it to raise baffling exceptions? Do
you hate that attribute assignment is a statement? Then you might like this alternative* object system I wrote!
Don't have any of those problems? Well, that's fine, because nobody asked for this anyway.
It has:
- Access without dots. Dots are so .... Java.
- Assignment is an expression! Just like you've always wanted!
- Constructors, but no destructors. And on some older versions of Python, refcount-zero may not actually destroy objects
because of reference loops. Garbage collection is for wimps anyway.
- Methods.
- Fields.
- Classmethods.
- Initializers.
- *All class state is stored in the runtime frame of a single function that never returns*. You know. For performance.
- Multiple inheritance, but *without a deterministic MRO.* This discourages the use of inheritance abuse to reconcile
overridden fields, and nudges people towards pure mixin-based composition. Because layer-cake objects are so passé.
- All object fields must be hashable, to enforce immutability. Attempting to interact with an object with mutable fields
will cause the object to enter an internally broken state after which all subsequent behavior is undefined (seriously
undefined--like, it might raise exceptions. It might reset your object back to the initial state. You don't know. So
don't do anything mutable.)
- No type system underneath, all classes fail isinstance checks on everything (well, almost everything). Objects are
just floating, unique, programmable closure scopes, as god intended.
- Seriously, why would you use this ever. Run away.
*alternative as in "alternative facts": incorrect in every way.
"""
from collections import defaultdict
import random
import inspect
from contextlib import contextmanager
def indirect(send):
def inner(instr, *a, **b):
if instr == 'new':
return send((instr, '', a, b))()
subj, *a = a
return send((instr, subj, a, b))()
return inner
def sasi_class_gen():
vars = defaultdict(list)
def getvar(v):
return random.choice(vars[v])
next_yield = lambda: 'not started'
instr, subj, args, kwargs = '', '', [], {}
while True:
try:
if instr == 'setvar':
vars[subj].append(args[0])
next_yield = lambda: "don't you wish this was an expression in real python?"
elif instr == 'getvar':
next_yield = lambda: getvar(subj)
elif instr == 'new':
inst = sasi_class_gen()
inst.send(None)
call = indirect(inst.send)
for k, v in vars.items():
call('setvar', k, getvar(k))
call('setvar', '__self__', call)
if '__init__' in vars:
getvar('__init__')(call, *args, **kwargs)
next_yield = lambda: call
elif instr == 'call':
try:
v = getvar(subj)
if getattr(v, '__classmethod__', False):
next_yield = lambda: v(getvar('__class__'), *args, **kwargs)
else:
if not '__self__' in vars:
raise ValueError('Cannot call unbound method')
next_yield = lambda: v(getvar('__self__'), *args, **kwargs)
except KeyError:
raise ValueError(f'No method {subj} found')
elif instr == 'subclass_onto':
for k, v in vars.items():
args[0]('setvar', k, getvar(k))
finally:
instr, subj, args, kwargs = yield next_yield
@contextmanager
def Class(*superclasses):
gen = sasi_class_gen()
gen.send(None)
call = indirect(gen.send)
call('setvar', '__class__', indirect)
old = inspect.currentframe().f_back.f_back.f_locals.copy()
yield call # TODO extract name
new = inspect.currentframe().f_back.f_back.f_locals
for superclass in superclasses:
superclass('subclass_onto', '', call)
for k, v in new.items():
if callable(v):
if k not in old or old[k] != v:
call('setvar', k, v)
def class_method(func):
func.__classmethod__ = True
return func
##### BEGIN USAGE
with Class() as MyClass:
def __init__(self, v1, v2):
self('setvar', 'v1', v1)
self('setvar', 'v2', v2)
def mymethod(self):
print("I'm a method", self('getvar', 'v1'))
@class_method
def myclassmethod(cls):
print("I'm a classmethod")
with Class(MyClass) as SubClass:
def mymethod(self):
print("Subclass method")
inst = MyClass('new', 'a', 'b')
inst2 = MyClass('new', 'c', 'd')
print(inst('getvar', 'v1'))
print(inst2('getvar', 'v1'))
inst('call', 'mymethod')
inst('setvar', 'v1', 'e')
print(inst('getvar', 'v1'))
inst2 = SubClass('new', 1, 2)
for _ in range(10):
inst2('call', 'mymethod')
inst2('call', 'myclassmethod')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment