Created
November 8, 2019 20:25
-
-
Save Grumblesaur/82e26463a4ba1587483bd8be3854ec59 to your computer and use it in GitHub Desktop.
Making objects in Python the wrong way.
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
# prototypes.py -- James Murphy | |
# Construct arbitrary objects with default-initialized values | |
# and internal member dict-storage that provides javascript-like | |
# object prototyping. | |
import collections | |
def define(name, *args, bases=(object,), **fields): | |
if set(args) & set(fields.keys()): | |
raise ValueError('Member name(s) duplicated.') | |
none_init = {k : v for k, v in zip(args, len(args) * [None])} | |
members = {**none_init, **fields} | |
for key in members.keys(): | |
if not key.isidentifier(): | |
raise ValueError('Member name not an identifier: "{}"'.format(key)) | |
if not isinstance(bases, collections.Iterable): | |
raise TypeError('Base classes not provided in iterable object.') | |
elif object not in bases: | |
bases = tuple(bases) + (object,) | |
def method_maker(): | |
names = [] | |
subscript = lambda s, k: s.__dict__[k] | |
equals = lambda s, o: vars(s) == vars(o) | |
copy = lambda s: type(s)(**s.__dict__) | |
representation = lambda s: '{tname}({mems})'.format( | |
tname=name, | |
mems=vars(s)) | |
def initializer(self, dict_=None, **kw_members): | |
nonlocal names | |
new_values = kw_members if dict_ is None else dict_ | |
names = new_values.keys() | |
self.__dict__ = new_values.copy() | |
def assign(self, key, item): | |
self.__dict__[key] = item | |
def delete(self, key): | |
del self.__dict__[key] | |
return { | |
'__init__' : initializer, | |
'__repr__' : representation, | |
'__eq__' : equals, | |
'__getitem__': subscript, | |
'__setitem__': assign, | |
'__delitem__': delete, | |
'copy' : copy | |
} | |
methods = method_maker() | |
for key in methods: | |
if key not in members: | |
members[key] = methods[key] | |
return type(name, bases, members) | |
class unit_tests(): | |
def demo_creation(): | |
Prototype = define('Prototype') | |
p = Prototype() | |
assert(p is not None) | |
assert(type(p) is Prototype) | |
different_name = Prototype | |
q = different_name() | |
assert(q is not None) | |
assert(type(q) is Prototype) | |
Point3D = define('Point3D', x=0, y=0, z=0) | |
w = Point3D() | |
assert(w.x == 0 and w.y == 0 and w.z == 0) | |
assert(w.__dict__ == {}) | |
w = Point3D(x=1, y=2, z=3) | |
assert(w.x == 1 and w.y == 2 and w.z == 3) | |
assert(w.__dict__ != {}) | |
return True | |
def demo_manipulation(): | |
Prototype = define('Prototype') | |
p = Prototype() | |
assert(not hasattr(p, 'x')) | |
p.x = 1 | |
assert(hasattr(p, 'x')) | |
Point2D = define('Point2D', x=0, y=0) | |
q = Point2D() | |
assert(hasattr(q, 'x')) | |
assert('x' not in q.__dict__) | |
q.x = 9 | |
assert(hasattr(q, 'x')) | |
assert('x' in q.__dict__) | |
del q.x | |
assert(hasattr(q, 'x')) | |
assert('x' not in q.__dict__) | |
q['y'] = 6 | |
assert(q['y'] == q.y) | |
assert(q['y'] is q.y) | |
del q['y'] | |
assert(hasattr(q, 'y')) | |
assert('y' not in q.__dict__) | |
return True | |
def demo_duplication(): | |
Unit = define('Unit', n=0) | |
u = Unit() | |
h = Unit(n=5) | |
assert(u is not h) | |
assert(u != h) | |
uc = u.copy() | |
assert(u is not uc) | |
assert(u == uc) | |
hc = h.copy() | |
assert(h is not hc) | |
assert(h == hc) | |
return True | |
if __name__ == "__main__": | |
tests = [test for test in dir(unit_tests) if 'demo_' in test] | |
total = len(tests) | |
tests_completed = 0 | |
for test in tests: | |
tests_completed += eval('unit_tests.{name}()'.format(name=test)) | |
print('{} tests completed of {} total.'.format(tests_completed, total)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment