I'm wondering if this is good design, or if the definition of the __init__
, __call__
, and __repr__
methods should be inside of mesh_class_meta
itself instead of inside of the factory, mesh_class
.
If it is better to for them to be defined inside of the metaclass definition, please explain specifically why? Or is there another even better way that I haven't thought of?
debugprint = print
##Metaclass for mesh data subclasses
##Receives a list of valid fields in a string (similar to namedtuple)
##Adds those fields to __slots__
class mesh_class_meta(type):
def __new__(meta, name, bases, fields):
if isinstance(fields, str): fields = fields.replace(',',' ').split()
fields = tuple(fields)
debugprint('\n-------------------------')
debugprint('Allocating memory for new mesh_class', name)
debugprint(meta)
debugprint(bases)
debugprint(fields)
debugprint('\n')
dctn = {}
dctn['__slots__'] = list(fields)
return super(mesh_class_meta, meta).__new__(meta, name, bases, dctn)
def __init__(klass, name, bases, fields):
debugprint('\n-------------------------')
debugprint('Initializing class', name)
debugprint(klass)
debugprint(bases)
debugprint(fields)
debugprint('\n')
super(mesh_class_meta, klass).__init__(name, bases, {})
##mesh_class_meta factory
##__init__, __call__, and __repr__ for the subclasses are defined here
##__call__ allows the values of slot fields to be changed by calling the class
##with a valid dictionary argument
def mesh_class(name, fields):
new_class = mesh_class_meta(name, (object,), fields)
def new_class__init__(self, *args, **kwargs):
for slot, arg in enumerate(args):
setattr(self, str(self.__slots__[slot]), arg)
for k,v in kwargs.items():
setattr(self, k, v)
debugprint('\n-------------------------')
debugprint('New', name, 'object created using', tuple(({attr: getattr(self, attr)} for attr in self.__slots__)))
debugprint('\n')
super(new_class, self).__init__() #Not sure if this is correct; seems to work
new_class.__init__ = new_class__init__
def new_class__call__(self, **kwargs):
for k,v in kwargs.items():
setattr(self, k, v)
debugprint('\n-------------------------')
debugprint('Fields in', name, 'object edited:', tuple(({k: v} for k,v in kwargs.items())))
debugprint('\n')
new_class.__call__ = new_class__call__
def new_class__repr__(self):
return str(name) + str({attr: getattr(self, attr) for attr in self.__slots__})
new_class.__repr__ = new_class__repr__
return new_class
##mesh classes created
Point = mesh_class('Point', 'x y')
Elpoints = mesh_class('Elpoints','i j k l')
Boundpoint = mesh_class('Boundpoint','b')
##Testing procedures##
##Create some instances
Points = [Point(p,p) for p in range(0,4,1)]
e1 = Elpoints(*tuple(p for p in Points))
b1 = Boundpoint(Points[2])
##Do some tests
assert Points[3].x == 3
Points[3](x = 4)
assert e1.l.x == 4
e1.j(y = 5)
assert Points[1].y == 5
e1.k(x=7)
assert b1.b.x == 7
assert Points[2].x == 7
e1(i = Points[3])
b1(b = Points[3])
Points[3](x=-1,y=-1)
assert e1.i.x, e1.i.y == (-1 , -1)
assert b1.b.x, b1.b.y == (-1 , -1)
CamelCased
, and variables arelowercase_with_underscore
. \$\endgroup\$ – Ethan Bierlein Nov 6 '14 at 19:33Elpoints
) to be CamelCase. \$\endgroup\$ – Rick supports Monica Nov 6 '14 at 20:18namedtuple
? Otherwise my review would be a suggestion to usenamedtuple
and confusion at the juxtaposition of beginner and a question on metaclasses. After all, the best metaclass is one you don't have to write. \$\endgroup\$ – Michael Urman Nov 7 '14 at 14:21