Last active
September 28, 2016 05:11
-
-
Save edcrypt/023f52c88411ba9f6d029e14cd362ff3 to your computer and use it in GitHub Desktop.
This file contains 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
# 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