class Base:
__BASE_PARAMS__ = ["base"]
class A(Base):
__PARAMS__ = ["a"]
class B(Base):
__PARAMS__ = ["b"]
Comme mentionné ci-dessus, il existe une classe «Base» et les classes «A» et «B» héritent de la classe «Base».
Je veux ajouter un attribut de classe appelé __ALL_PARAMS__
qui intègre la classe parente __BASE_PARAMS__
et mon propre __PARAMS__
aux classes ʻA et
B`. Autrement dit, nous voulons obtenir les résultats suivants.
A.__ALL_PARAMS__
# ['base', 'a']
B.__ALL_PARAMS__
# ['base', 'b']
S'il est implémenté normalement, cela ressemblerait à ceci:
class Base:
__BASE_PARAMS__ = ["base"]
class A(Base):
__PARAMS__ = ["a"]
__ALL_PARAMS__ = Base.__BASE_PARAMS__ + __PARAMS__
class B(Base):
__PARAMS__ = ["b"]
__ALL_PARAMS__ = Base.__BASE_PARAMS__ + __PARAMS__
A.__ALL_PARAMS__
# ['base', 'a']
B.__ALL_PARAMS__
# ['base', 'b']
Cependant, il existe de nombreuses classes qui héritent de la classe Base
, et il est difficile d'écrire l'attribut de classe __ALL_PARAMS__ = Base .__ BASE_PARAMS__ + __PARAMS__
, qui n'est pas lié au traitement, dans toutes les classes héritées.
Existe-t-il un moyen simple de résoudre ce problème sans que la classe héritée n'écrive sans signification? Je veux que vous y réfléchissiez.
La métaprogrammation est une technique permettant de définir un programme par programmation.
Je ne parlerai pas des bases de la métaprogrammation ici. Si vous souhaitez en savoir plus sur la métaprogrammation, veuillez vous référer aux articles suivants.
Métaprogrammation avec Python \ _ \ _ Nouveau \ _ \ _ et \ _ \ _ init \ _ \ _ et métaclasse Dans la métaclasse python, passez l'opérateur de comparaison sous forme de chaîne à la méthode ORM pour construire la clause where.
Maintenant, en utilisant des métaclasses, nous pouvons résoudre le problème ci-dessus comme suit.
Métaclasse pour Python3
class MetaClass(type):
def __init__(cls, name, bases, attribute):
super(MetaClass, cls).__init__(name, bases, attribute)
cls.__ALL_PARAMS__ = cls.__BASE_PARAMS__ + getattr(cls, "__PARAMS__", [])
class Base(metaclass=MetaClass):
__BASE_PARAMS__ = ["base"]
class A(Base):
__PARAMS__ = ["a"]
class B(Base):
__PARAMS__ = ["b"]
A.__ALL_PARAMS__
# ['base', 'a']
B.__ALL_PARAMS__
# ['base', 'b']
Pour Python2, assignez une métaclasse à l'attribut spécial «metaclass».
Pour Python 2
class Base(object):
__metaclass__ = MetaClass
Vous pouvez même étendre la langue avec la métaprogrammation. Les classes finales en Java n'existent pas en Python, mais les métaclasses vous permettent de définir des fonctionnalités similaires.
final_metaclass.py
class FinalMetaClass(type):
def __init__(cls, name, bases, namespace):
super(FinalMetaClass, cls).__init__(name, bases, namespace)
for _class in bases:
if isinstance(_class, FinalMetaClass):
raise TypeError()
class A:
pass
class B(A, metaclass=FinalMetaClass):
pass
# Error!!
class C(B):
pass
Dois-je utiliser «init» ou «new» dans une métaclasse qui hérite du type? C'est essentiellement une question de goût, et peu importe lequel. Cependant, «new» a un degré de liberté plus élevé, comme la possibilité de réécrire «slots».
Une méthode spéciale «prepare» a été ajoutée à partir de Python3.
Normalement, __dict__
est un type de dict dont l'ordre n'est pas garanti (ce qui s'est passé dans Python3.6), et vous pouvez contrôler cela avec __prepare__
.
Ce qui suit est un extrait de la documentation Python.
import collections
class OrderedClass(type):
@classmethod
def __prepare__(metacls, name, bases, **kwds):
return collections.OrderedDict()
def __new__(metacls, name, bases, namespace, **kwds):
cls = type.__new__(metacls, name, bases, dict(namespace))
cls.members = tuple(namespace)
return cls
class A(metaclass=OrderedClass):
def one(self): pass
def two(self): pass
def three(self): pass
def four(self): pass
A.members
# ('__module__', '__qualname__', 'one', 'two', 'three', 'four')
Il peut être confirmé que l'énumération des membres de la classe est dans l'ordre de définition.
class MetaA(type):
def __new__(mcls, *args, **kwargs):
cls = type.__new__(mcls, *args, **kwargs)
cls.MetaA = "Yes"
return cls
class MetaB(type):
def __new__(mcls, *args, **kwargs):
cls = type.__new__(mcls, *args, **kwargs)
cls.MetaB = "Yes"
return cls
class A(metaclass=MetaA):
pass
class B(A, metaclass=MetaB):
pass
Lorsque j'essaye de définir la classe B
, j'obtiens l'erreur suivante:
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Non seulement les sous-classes de «type» peuvent être passées à la «métaclasse». Tout ce qui est appelable qui correspond à un argument spécifique peut être utilisé. Dans ce cas, une fonction peut être transmise pour éliminer l'héritage du diamant.
class MetaA(type):
def __new__(mcls, *args, **kwargs):
cls = type.__new__(mcls, *args, **kwargs)
cls.MetaA = "Yes"
return cls
class A(metaclass=MetaA):
pass
def MetaB(mcls, *args, **kwargs):
cls = type(mcls, *args, **kwargs)
cls.MetaB = "Yes"
return cls
class B(A, metaclass=MetaAB):
pass
B.MetaA, B.MetaB
# ('Yes', 'Yes')
La métaprogrammation est suffisamment puissante pour changer les spécifications linguistiques. Par conséquent, une utilisation intensive de la métaprogrammation peut être une source de confusion dans la programmation d'équipe.
Fondamentalement, si le design est solide, la métaprogrammation devrait rarement entrer en jeu. Exceptionnellement, lors de la génération de l'attribut de classe hérité, il est souvent possible d'écrire clairement en utilisant une méta-classe.
http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html
Recommended Posts