Skip to content

Instantly share code, notes, and snippets.

@mattvonrocketstein
Last active August 29, 2015 14: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 mattvonrocketstein/76d6b96d536d2c8ef991 to your computer and use it in GitHub Desktop.
Save mattvonrocketstein/76d6b96d536d2c8ef991 to your computer and use it in GitHub Desktop.
"""
A curiosity for your programming zoo inspired by monty python,
famous philosophers, and GVR's multimethods demo.
This code demonstrates metaprogramming as well as runtime reflection
and introspection. It also shows python's flexibility by bending language
semantics and demonstrates that python's "self" is convention (not keyword).
"""
import inspect
import new
import sys
registry = {}
class proxy(object):
""" master registry for methodnames,
decorators, and the proxy scope
dummy object
"""
pass
def personalityDependant(func):
""" have to save func in the registry so each duplicate
function name doesnt just overwrite the previous one
NOTE: "which_self" is the name of the variable used where
"self" is usually found in instancemethod definitions
"""
which_self = inspect.getargspec(func)[0][0]
if which_self not in registry:
registry[which_self] = {}
registry[which_self][func.__name__] = func
return func
def schizophrenic(func):
""" tag a function as schizophrenic so the
metaclass knows when to effect the scope
mangling.
"""
func.schizophrenic = True
return func
class schizotypal(type):
""" metaclass for all schizoids """
@staticmethod
def __new__(mcls, name, bases, clsdict):
err1 = ('expected a schizotypal object would '
'have a list of personalities.')
if 'personalities' not in clsdict:
raise TypeError(err1)
else:
personalities = clsdict['personalities']
replacement = {}
for pName in personalities:
replacement[pName] = registry[pName]
personalities = replacement
clsdict['personalities'] = personalities
klassobj = type.__new__(mcls, name, bases, clsdict)
return klassobj
def __call__(cls, *args, **kargs):
inst = type.__call__(cls, *args, **kargs)
for var in dir(inst):
val = getattr(inst, var)
test = var != '__metaclass__' and var != '__class__'
if hasattr(val, 'schizophrenic'):
for pName in inst.personalities:
val.func_globals[pName] = inst._as(pName)
return inst
class schizoid(object):
""" base schizoid object (inherit from this) """
__metaclass__ = schizotypal
personalities = []
def _as(self, pName):
""" this method is used to obtain a representation
of self with respect to a specific personality
"""
cls = self.__class__
err = 'illegal personality for ' + str(cls) + ': ' + pName
assert pName in cls.personalities, err
out = cls.personalities[pName]
P = proxy()
for funcname in out:
func = out[funcname]
func = new.instancemethod(func, self, cls)
setattr(P, funcname, func)
return P
class myschizoid(schizoid):
""" a simple demo.
NB: without the decorators, each status() definition below would simply
overwrite the previous one. notice also under normal python semantics
how theres no reason the philosopher names in the run() method should
be in scope.
"""
personalities = ['kant', 'heidegger', 'wittgenstein', 'schlegel']
@personalityDependant
def status(kant):
return "a real pissant"
@personalityDependant
def status(heidegger):
return "a boozy beggar"
@personalityDependant
def status(wittgenstein):
return "beery swine"
@personalityDependant
def status(schlegel):
return "schloshed"
@schizophrenic
def run(self):
print
print 'kant:\t\t\t', kant.status()
print 'heidegger:\t\t', heidegger.status()
print 'wittgenstein:\t\t', wittgenstein.status()
print 'schlegel:\t\t', schlegel.status()
print
if __name__ == '__main__':
s = myschizoid()
s.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment