Comment modifier dynamiquement la classe de base de cas au moment de l'exécution?
Cet article a un extrait montrant l'utilisation de __bases__
pour modifier dynamiquement la hiérarchie d'héritage de code Python, par l'ajout d'une classe à une des classes existantes collection de classes dont il hérite. Ok, c'est dur à lire, le code est probablement le plus clair:
class Friendly:
def hello(self):
print 'Hello'
class Person: pass
p = Person()
Person.__bases__ = (Friendly,)
p.hello() # prints "Hello"
Qui est, Person
n'héritent pas de Friendly
au niveau de la source, mais plutôt cette relation d'héritage est ajouté dynamiquement à l'exécution, par la modification de la __bases__
attribut de la classe Personne. Toutefois, si vous modifiez Friendly
et Person
à de nouvelles classes de style (par hérite de l'objet), vous obtenez l'erreur suivante:
TypeError: __bases__ assignment: 'Friendly' deallocator differs from 'object'
Un peu de Googling sur ce qui semble indiquer certaines incompatibilités entre un style nouveau et vieux style de classes en ce qui concerne la modification de la hiérarchie d'héritage au moment de l'exécution. Plus précisément: "Nouveau style des objets de la classe ne prennent pas en charge l'affectation à leur les bases l'attribut".
Ma question, est-il possible de faire de la ci-dessus Amicale/Personne exemple de travail à l'aide de nouvelles classes en Python 2.7+, éventuellement par l'utilisation de la __mro__
attribut?
Avertissement: j'ai pleinement conscience que ce n'est pas le code. J'ai pleinement conscience que la production réelle du code des astuces comme cette tendance à la frontière de illisible, c'est une pure expérience de pensée, et pour funzies d'apprendre quelque chose sur la façon Python traite de questions relatives à l'héritage multiple.
- Il est également agréable pour les apprenants de lire ceci si ils ne sont pas familiers avec la métaclasse, type(), ...: slideshare.net/gwiener/metaclasses-in-python 🙂
- Voici mon cas d'utilisation. Je suis de l'importation d'une bibliothèque qui a de la classe B hérite de la classe A.
- Voici mon cas d'utilisation. Je suis de l'importation d'une bibliothèque qui a de la classe B hérite de la classe A. je veux créer New_A héritant de A, avec new_A_method(). Maintenant, je veux créer New_B héritant de... eh bien, de B, si B hérité de New_A, de sorte que le B de méthodes, les méthodes, et new_A_method() sont tous disponibles à des instances de New_B. Comment puis-je le faire sans singe-correctifs existants de la classe A?
- Ne pourriez-vous pas avoir
New_B
hériter de deuxB
etNew_A
? Rappelez-vous Python supporte l'héritage multiple. - Après un peu de googling, le python suivant rapport de bug nous a semblé pertinent... bugs.python.org/issue672115
Vous devez vous connecter pour publier un commentaire.
Ok, encore une fois, ce n'est pas quelque chose que vous devrait le faire normalement, ceci est à titre informatif seulement.
Où Python recherche d'une méthode sur une instance de l'objet est déterminée par la
__mro__
attribut de la classe qui définit un objet (le M méthode R esolution O rder attribut). Ainsi, si l'on pouvait modifier la__mro__
dePerson
, nous aimerions obtenir le comportement désiré. Quelque chose comme:Le problème est que
__mro__
est un attribut lecture seule, et donc setattr ne fonctionne pas. Peut-être que si vous êtes un Python gourou, il y a un moyen de contourner cela, mais clairement, je manque de statut de gourou que je ne peux pas penser à une.Une solution de contournement possible est tout simplement de redéfinir la classe:
Ce que ce n'est pas faire est de modifier tout créé précédemment
Person
instances pour avoir lehello()
méthode. Par exemple (juste en modifiantmain()
):Si les détails de la
type
appel ne sont pas clairs, alors lisez la e-satis "excellente réponse sur" Ce qui est une métaclasse en Python?'.type('Person', (Friendly) + Person.__mro__, dict(Person.__dict__))
(et mieux encore, d'ajouter des garanties afin que Sympathique de ne pas finir deux fois là-bas. ) il y a d'autres questions ici, pour le cas où la "Personne" de la classe est en fait défini et utilisé: votre fonction uniquement le changer sur le module en cours d'autres modules en cours d'exécution Personne sera unafectd - vous feriez mieux d'effectuer une monkeypatch sur le module de Personne est définie. (et même là, il y a des problèmes)-1
Vous avez raté totalement la raison de l'exception dans la première place. Vous pouvez heureusement modifierPerson.__class__.__bases__
en Python 2 et 3, à condition d'Person
n'héritent pas deobject
directement. Voir akaRem et Sam Gulve réponses ci-dessous. Cette solution de contournement ne fonctionne autour de votre propre incompréhension du problème.__dict__
attribut des objets.J'ai eu du mal avec ça aussi, et il a été intrigué par votre solution, mais Python 3, il enlève de nous:
En fait, j'ai un besoin légitime d'un décorateur qui remplace le (seul) de la superclasse de la salle de classe. Il nécessiterait une trop longue description de l'inclure ici (j'ai essayé, mais ne pouvais pas le faire raisonnablement une longueur et une complexité limitée -- il est venu dans le cadre de l'utilisation par de nombreux Python applications d'un Python d'entreprise basées sur le serveur où différentes applications nécessaires des variations légèrement différentes d'une partie du code.)
La discussion sur cette page, et d'autres, comme il a fourni des conseils que le problème de l'affectation de
__bases__
se produit uniquement pour les classes sans super-classe définie (c'est à dire, dont la seule super-classe est un objet). J'ai été en mesure de résoudre ce problème (pour Python 2.7 et 3.2) en définissant les classes dont la super-classe j'avais besoin de le remplacer comme étant des sous-classes de trivial classe:Je ne peux pas se porter garant pour les conséquences, mais que ce code fait ce que vous voulez à py2.7.2.
Nous savons que cela est possible. Cool. Mais nous ne serons jamais à l'utiliser!
Droit de la chauve-souris, toutes les mises en garde de vous embêter avec la hiérarchie de classes dynamiquement sont en vigueur.
Mais si cela doit être fait, puis, apparemment, il y a un hack pour obtenir autour de la
"deallocator differs from 'object" issue when modifying the __bases__ attribute
pour les nouvelles classes de style.Vous pouvez définir un objet de classe
Qui dérive d'une classe de la métaclasse
type
.Voilà, maintenant votre nouveau style de classes peuvent modifier la
__bases__
sans aucun problème.Dans mes tests, cela fonctionne très bien comme tous les existants (avant de changer l'héritage) ses instances et ses classes dérivées sentit l'effet de la modification, y compris leurs
mro
obtenir des mises à jour.J'ai besoin d'une solution pour cela:
unittest.mock.patch
de fonctionner comme prévu.Voici ce que je suis venu avec:
Utilisé comme cela, au sein de l'application:
Utiliser comme cela de l'intérieur de l'unité de test de code:
Les réponses ci-dessus sont bonnes si vous avez besoin de changer une classe existante au moment de l'exécution. Toutefois, si vous êtes simplement à la recherche pour créer une nouvelle classe qui hérite d'une autre classe, il est beaucoup plus propre solution. J'ai eu cette idée de https://stackoverflow.com/a/21060094/3533440, mais je pense que l'exemple ci-dessous illustre le mieux l'utilisation légitime de cas.
Corrigez-moi si je me trompe, mais cette stratégie semble très lisible pour moi, et je voudrais l'utiliser dans le code de production. Ceci est très similaire à foncteurs en OCaml.
Map
directement et redéfinition des méthodes selon les besoins (un peu comme ce que la normecollections.defaultdict
ne)? Comme il estmake_default
ne peut jamais retourner un type de chose, alors pourquoi ne pas simplement faireDefaultMap
haut niveau identificateur plutôt que d'avoir à appelermake_default
pour obtenir la classe à instancier?make_default
pour créer une version par défaut de certains autres type de dictionnaire-comme la classe (Map
). Vous ne pouvez pas hériter deMap
directement parce queMap
n'est pas défini jusqu'à l'exécution. Dans ce cas, vous souhaitez hériter dedict
directement, mais on imagine qu'il pourrait y avoir des cas où vous ne savez pas quel type dictionnaire de classe d'hériter de jusqu'à ce que l'exécution.