Skip to content

Instantly share code, notes, and snippets.

@edcrypt
Last active September 28, 2016 05:11
Show Gist options
  • Save edcrypt/023f52c88411ba9f6d029e14cd362ff3 to your computer and use it in GitHub Desktop.
Save edcrypt/023f52c88411ba9f6d029e14cd362ff3 to your computer and use it in GitHub Desktop.
# There are a dozen ways to implement the Prototype/Properties Pattern in Python.
#
# You could use copy.copy(), or program with classes and classmethods only.
# for example.
#
# But if you want to use an existing library in this style, and don't want to loose
# Python's flexibility (multiple inheritance, etc) I believe this solution works best.
#
# Prototypes, in Pythonic terms, are objects that behave like instances and classes/types,
# unifying instantiation and inheritance. So the most obvious solution, even if wasteful,
# is to have a thin layer over a type and it's single instance.
# TODOs:
# - A way to add/remove prototypes from objects
class BaseObject:
""" Could be any class that you want to adapt to the prototyped style
"""
pass
class PObject:
__slots__ = ['ptype', 'pinstance', 'pname', 'pbases']
def __init__(self, pname='Object', ptype=None, pinstance=None,
pbases=(BaseObject,), **attribs):
self.pname = pname
self.ptype = ptype
self.pinstance = pinstance
self.pbases = pbases
if pinstance is not None:
self.pinstance.__dict__.update(attribs)
def clone(self, name=None, *args, **kw):
name = name or self.pname
bases = self.pbases if self.ptype is None else (self.ptype,)
new_ptype = type(name, bases, {})
new_pinstance = new_ptype(*args, **kw)
attrs = self.pinstance.__dict__ if self.pinstance is not None else {}
return PObject(name, new_ptype, new_pinstance, **attrs)
def add_method(self, func):
setattr(self.ptype, func.__name__, func)
def __getattr__(self, name):
return getattr(self.pinstance, name)
def __setattr__(self, name, value):
if name in self.__slots__:
super().__setattr__(name, value)
else:
self.pinstance.__setattr__(name, value)
def __repr__(self):
return '<Proto: {}>'.format(self.pname)
# Root object
Object = PObject().clone()
### Example
person = Object.clone('Person')
person.name = 'John Doe'
person.arms = 2
@person.add_method
def say_name(self):
print(self.name)
# Anna retains the number of arms of its parent
person1 = person.clone()
person1.name = 'Anna Doe'
person1.say_name()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment