Skip to content

Instantly share code, notes, and snippets.

@p7g
Last active October 24, 2019 03:17
Show Gist options
  • Save p7g/92b9152c80674e01926ba88fd9b027a4 to your computer and use it in GitHub Desktop.
Save p7g/92b9152c80674e01926ba88fd9b027a4 to your computer and use it in GitHub Desktop.
A smalltalk/ruby-style object model implemented in python
from typing import Any, Dict, Optional
from dataclasses import dataclass
MISSING = 'missing'
def mro(o):
yield o
o = o.parent
while o:
yield o
o = o.base()
def resolve_method(o, method):
for o in mro(o):
if method in o.methods and \
callable(o.methods[method]):
return o.methods[method]
return None
def send(o, name, *args):
method = resolve_method(o, name)
if method:
return method(o, *args)
missing = resolve_method(o, MISSING)
if missing:
return missing(o, name, *args)
raise KeyError(name)
@dataclass
class Object:
methods: Dict[str, callable]
parent: Optional['Object'] = None
userdata: Any = None
def __call__(self, method, *args):
return send(self, method, *args)
def __getattr__(self, name):
return lambda *a: self(name, *a)
def respond_to(self, method):
if resolve_method(self, method):
return True
if resolve_method(self, MISSING):
return True
return False
def default_new(cls, *args):
obj = Object(
methods={},
parent=cls,
)
cls.init(obj, *args)
return obj
def classclass_new(self, name, base, meths):
return Object(
methods={
'class': lambda s: s.parent,
'base': lambda _: base,
'name': lambda _: name,
'respond_to': Object.respond_to,
'mro': lambda o: list(mro(o)),
'new': default_new,
**meths,
},
parent=self,
)
classclass = Object(
methods={
'respond_to': Object.respond_to,
'new': classclass_new,
},
)
def objectclass_missing(self, name, *args):
if self.userdata and \
name in self.userdata:
return self.userdata[name]
raise KeyError(name)
def objectclass_init(cls, self):
self.userdata = {}
def objectclass_get(self, key):
return self.userdata[key]
def objectclass_set(self, key, value):
self.userdata[key] = value
objectclass = classclass.new(
'object',
None,
{
MISSING: objectclass_missing,
'init': objectclass_init,
'get': objectclass_get,
'set': objectclass_set,
},
)
def greeterclass_init(cls, self, name):
cls.base().init(self)
self.set('name', name)
def greeterclass_greet(self):
return 'Hello, %s' % self.get('name')
greeterclass = classclass.new(
'greeter',
objectclass,
{
'init': greeterclass_init,
'greet': greeterclass_greet,
},
)
greeter = greeterclass.new('stranger')
print(greeter.greet())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment