Ce que je fais en imitant l'intégration en python, ou plutôt la délégation plutôt que l'héritage.
L'une des raisons pour lesquelles l'héritage est douloureux est que vous ne savez pas quelle méthode a été appelée.
Par exemple, pour C avec la relation d'héritage suivante
class A:
def f(self):
return "f"
def g(self):
return "g"
class B:
def h(self):
return "h"
def i(self):
return "i"
class C(A, B):
def m(self):
return (self.f(), self.i())
Il est difficile de comprendre le comportement de traitement de C # m ()
. Bien sûr, je pense que je suis généralement un peu plus précis sur le nom, donc je ne pense pas qu'il devrait être si mauvais.
En revanche, s'il est délégué, c'est encore mieux.
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
En effet, il sera spécifié quelle méthode de la variable d'instance qui est conservée en interne est appelée.
Voici l'intégration de go. Cela peut également être considéré comme une fonctionnalité qui effectue la délégation de manière semi-automatique.
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
ReadeWriter a des fonctions de lecture et d'écriture. En substance, il délègue simplement à la valeur qu'il a à l'intérieur. L'exemple ci-dessus était un exemple d'interface, mais il en va de même pour struct.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Si vous faites quelque chose comme rw.Read ()
pour une variable rw qui devient var rw * ReadWriter
ici, alors quelque chose comme` rw.Reader.Read () ʻest appelé.
Revenez à python. Vous avez mentionné dans le premier exemple que la délégation vaut mieux que l'héritage. Cependant, il n’a pas été complètement résolu par délégation. Vous pouvez appeler les méthodes A et B directement à partir de C. De plus, si vous faites quelque chose comme c.a.f ()
directement là où vous utilisez C, les valeurs qui peuvent réellement être utilisées seront fixées à l'instance C.
C'est pourquoi il arrive que vous vouliez que c.a.f ()
soit appelé en interne en appelant c.f ()
. Cela dit, il est difficile d'écrire explicitement les appels de méthode requis à chaque fois. Je serais heureux si vous pouviez le faire automatiquement. En d'autres termes, c'est une imitation de l'intégration de go.
Le sujet principal est d'ici, mais si vous voulez imiter l'intégration de go with python, je fais personnellement ce qui suit.
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
def __getattr__(self, name):
return getattr(self.a, name)
Désormais, «c.f» deviendra automatiquement «c.a.f». Cela n'imite pas complètement l'intégration de go. Si un attribut qui n'existe pas est spécifié, il sera entièrement délégué à self.a. Vous pouvez le faire un peu plus explicitement ou de manière plus restrictive en utilisant la méta-programmation etc. Je pense que c'est suffisant pour le moment.
Dans l'exemple d'origine, il y avait plusieurs sources d'héritage. Dans l'exemple précédent, nous ne pouvons gérer que le cas où il n'y a qu'une seule source d'héritage. Examinons également plusieurs cas. Le point à noter est comment utiliser getattr. Vous pouvez spécifier la valeur par défaut pour le troisième argument de getattr. Essayez de renvoyer une valeur spéciale au lieu de None. En effet, même si Aucun est défini, il recherchera la prochaine destination de recherche (au contraire, si vous souhaitez rechercher un autre candidat s'il n'y en a aucun, vous pouvez le faire. Comprenez le comportement. Je ne le recommande pas car ce sera difficile à faire).
missing = object()
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def m(self):
return (self.a.f(), self.b.i())
def __getattr__(self, name):
v = getattr(self.a, name, missing)
if v is not missing:
return v
return getattr(self.b, name)
Maintenant c.i ()
appellera c.b.i () ʻet
c.f () appellera
c.a.f ()`.
En passant, dans l'exemple d'origine, j'ai écrit un exemple dans lequel une valeur qui hérite de plusieurs A et B est prise en charge par plusieurs destinations de transfert dans l'exemple final. Dans le code réel, il n'est pas recommandé d'effectuer un tel transfert implicite vers plusieurs destinations de transfert. En effet, il devient difficile de saisir le comportement en regardant le putt. Par conséquent, il est préférable de limiter autant que possible la destination du transfert à une seule.
Recommended Posts