I am trying to automatically create some SQL tables from the definition of some Python classes, I tried using dir()
but since it returns a Python Dictionary, it's not ordered so the definition order of the class members is lost.
Reading on the internet I found the following here
class OrderedClass(type):
@classmethod
def __prepare__(metacls, name, bases, **kwds):
return collections.OrderedDict()
def __new__(cls, name, bases, namespace, **kwds):
result = type.__new__(cls, name, bases, dict(namespace))
result.members = tuple(namespace)
return result
class A(metaclass=OrderedClass):
def one(self): pass
def two(self): pass
def three(self): pass
def four(self): pass
>>> A.members
('__module__', 'one', 'two', 'three', 'four')
I successfuly implemented a copy of it, and it appears to be doing what it should except that it's only saving the methods
in the members
variable, and I need to have also the class
member variables.
Question:
How could I get a list of the member variables preserving their definition order?, I don't care about class methods, and I am actually ignoring them.
Note: The reason why the order is important is because the tables will have constraints that reference some of the table columns, and they must go after defining the column, but they are appearing before.
Edit: This is a sample class in my real program
class SQLTable(type):
@classmethod
def __prepare__(metacls, name, bases, **kwds):
return OrderedDict()
def __new__(cls, name, bases, namespace, **kwds):
result = type.__new__(cls, name, bases, dict(namespace))
result.members = tuple(namespace)
return result
class AreaFisicoAmbiental(metaclass = SQLTable):
def __init__(self, persona, datos):
# edificacion
self.persona = persona
self.tipoEdificacion = datos[0]
self.tipoDeParedes = datos[1]
self.detallesTipoDeParedes = datos[2]
self.tipoDeTecho = datos[3]
self.detallesTipoDeTecho = datos[4]
self.tipoDePiso = datos[5]
self.detallesTipoDePiso = datos[6]
# ambientes
self.problemaDeInfraestructura = datos[7]
self.detallesProblemaDeInfraestructura = datos[9]
self.condicionDeTenencia = datos[10]
self.detallesCondicionDeTenencia = datos[11]
self.sala = toBool(datos[12])
self.comedor = toBool(datos[13])
self.baño = toBool(datos[14])
self.porche = toBool(datos[15])
self.patio = toBool(datos[16])
self.lavandero = toBool(datos[17])
self.habitaciones = toInt(datos[19])
# servicios básicos
self.aguasServidas = toBool(datos[21])
self.aguaPotable = toBool(datos[22])
self.luz = toBool(datos[23])
self.gas = datos[24]
self.internet = toBool(datos[25])
Doing
print(AreaFisicoAmbiental.members)
Outputs:
('__module__', '__qualname__', '__init__')
Variable names are in spanish because their names will be used as the table column names, and also as the labels for a web application that will be generated from the database structure.
I know that Django does something like this, but I already have my database inspector which does the opposite thing, so know I need a Django like functionality to use my generator.
Updated
As I commented, I think you're probably confusing instance attributes with class attributes and really want to keep track of the latter. Instance attributes are dynamic and can be added, changed, or removed at any time, so trying to do this with a metaclass like shown in your question won't work (and different instances may have a different group of them defined).
You may be able to keep track of their creation and deletion by overloading a couple of the class's special methods, namely __setattr__()
and __delattr__()
and storing their effects in a private data member which is an OrderedSet
. Do so will keep track of what they are and preserve the order in which they were created.
Both of these methods will need to be careful not to operate upon the private data member itself.
That said, here's something illustrating such an implementation:
# -*- coding: iso-8859-1 -*-
# from http://code.activestate.com/recipes/576694
from orderedset import OrderedSet
class AreaFisicoAmbiental(object):
def __init__(self, persona, datos):
self._members = OrderedSet()
self.persona = persona
self.tipoEdificacion = datos[0]
self.tipoDeParedes = datos[1]
def __setattr__(self, name, value):
object.__setattr__(self, name, value)
if name != '_members':
self._members.add(name)
def __delattr__(self, name):
if name != '_members':
object.__delattr__(self, name)
self._members.discard(name)
def methodA(self, value1, value2): # add some members
self.attribute1 = value1
self.attribute2 = value2
def methodB(self):
del self.attribute1 # remove a member
if __name__ == '__main__':
a = AreaFisicoAmbiental('Martineau', ['de albañilería', 'vinilo'])
a.methodA('attribute1 will be deleted', 'but this one will be retained')
a.methodB() # deletes a.attribute1
a.attribute3 = 42 # add an attribute outside the class
print('current members of "a":')
for name in a._members:
print(' {}'.format(name))
Output:
current members of "a":
persona
tipoEdificacion
tipoDeParedes
attribute2
attribute3
A final note: It would be possible to create a metaclass that added these two methods automatically to client classes, which would make it easier to modify existing classes.