Last active
October 24, 2019 03:17
-
-
Save p7g/92b9152c80674e01926ba88fd9b027a4 to your computer and use it in GitHub Desktop.
A smalltalk/ruby-style object model implemented in python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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