Les métaclasses sont rarement utilisées sauf s'il s'agit de bibliothèques, mais elles sont utilisées dans SQLAlchemy, je les résumerai donc à titre de référence uniquement.
Une métaclasse est un type de classe. Dans le cas de python, le type de la classe définie par l'utilisateur est essentiellement type.
>>> class A(object):
>>> pass
>>>
>>> type(A)
type
Les métaclasses autres que type peuvent être utilisées à des fins spéciales.
Ceci est utilisé dans la déclaration de SQL Alchemy comme une utilisation familière des métaclasses.
>>> from sqlalchemy.ext.declarative import declarative_base
>>> Base = declarative_base()
>>> type(Base)
<class 'sqlalchemy.ext.declarative.api.DeclarativeMeta'>
Avec déclarative, la définition de classe est automatiquement associée à la définition de table.
class User(Base):
__tablename__ = 'user_account'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(Varchar(100))
En interne, il utilise sa propre méta-classe appelée sqlalchemy.ext.declarative.api.DeclarativeMeta, et intercepte le traitement au moment de la définition de la classe pour effectuer le traitement du mappage.
La définition de sqlalchemy.ext.declarative.api.DeclarativeMeta est la suivante. init est appelé lorsqu'une classe qui hérite de Base est créée et que la classe et la table y sont liées.
sqlalchemy/ext/declarative/api.py
class DeclarativeMeta(type):
def __init__(cls, classname, bases, dict_):
if '_decl_class_registry' not in cls.__dict__:
_as_declarative(cls, classname, cls.__dict__)
type.__init__(cls, classname, bases, dict_)
def __setattr__(cls, key, value):
_add_attribute(cls, key, value)
En python, il semble que l'utilisation de la métaclasse soit une méthode relativement courante lorsque vous voulez faire quelque chose automatiquement à partir de la définition de classe. Par exemple, la classe Form de wtforms fait à peu près la même chose.
Selon le cas d'utilisation, vous souhaiterez peut-être arrêter ce processus. Considérons un cas d'utilisation tel que l'association d'un objet Table créé par réflexion à partir de DB avec une classe créée par declarative_base. Dans de tels cas, le code suivant peut être utilisé.
from sqlalchemy import MetaData, Table
from sqlalchemy.ext.declarative import api, declarative_base
from sqlalchemy.ext.declarative.api import DeclarativeMeta
class_registry = {}
metadata = MetaData()
Base = declarative_base(class_registry=class_registry,
metadata=MetaData())
class MyMetaClass(DeclarativeMeta):
def __init__(cls, classname, bases, dict_):
#Ne pas associer à la table
type.__init__(cls, classname, bases, dict_)
@classmethod
def create_class(cls, table_name, class_name, dbsession):
tableobj = Table(table_name, metadata, autoload=True,
autoload_with=dbsession.bind)
class_ = MyMetaClass(class_name, (Base), {})
#Associez-le explicitement au tableau ci-dessous.
class_.__tablename__ = tablename
class_.__table__ = tableobj
for c in tableobj.columns:
setattr(class_, c.name, c)
api.instrument_declarative(class_, class_registry, metadata)
return class_
Recommended Posts