Question technique sur l'utilisation du mot clé super pour héritage.
Ma question est simple pourquoi le code ci dessous fonctionne alors que je n'utilise pas le mot clé super pour l'héritage ?
from abc import ABC, abstractmethod
import ldap
from mce_sync.config import ConfigLoader
from mce_sync.core.ldap import LDAP
from mce_sync.core.treatments import Treatments
class TreatmentStrategy(ABC):
def __init__(
self,
configuration: ConfigLoader,
annuaire: LDAP,
treatments: Treatments,
):
self._configuration = configuration
self._annuaire = annuaire
self._treatments = treatments
self._frequency = None
self._enabled = None
self._NAME = None
@abstractmethod
def frequency(self):
"""Fréquence du traitement."""
pass
@abstractmethod
def enabled(self):
"""Activation du traitemnt"""
pass
@abstractmethod
def name(self):
"""Nom du traitement"""
pass
@abstractmethod
def execute(self):
pass
def _build_generic_ldap_filter(self) -> str:
ldap_object_class_attr_name: str = (
self._configuration.config.ldap_attributes.object_class
)
ldap_mailhost_filter: str = (
self._configuration.config.ldap_syncprov.filter
)
ldap_account_type: str = (
self._configuration.config.ldap_values.mce_account
)
ldap_filter: str = (
"(&"
f"{ldap_mailhost_filter}"
f"({ldap_object_class_attr_name}={ldap_account_type})"
)
return ldap_filter
def _search(self, ldap_filter: str):
ldap_base_dn = self._configuration.config.ldap_syncprov.base
return self._annuaire.search(
ldap_base_dn, ldap.SCOPE_SUBTREE, ldap_filter
)
def _extract_user_from_entry(self, entry):
"""Extrait l'utilisateur à partir de l'entrée LDAP."""
ldap_user_id_attr_name = (
self._configuration.config.ldap_attributes.user_id_mce
)
return entry[0][1][ldap_user_id_attr_name][0].decode("utf-8")
import logging
import ldap
from mce_sync.logger.log_msgs import MessagePurgeLogMsg
class MessagePurgeTreatment(TreatmentStrategy):
"""Pour tous les comptes où mcePurge est valué à une valeur différente
de -1 : T9 Purge messages"""
def __init__(self):
super().__init__()
self._NAME: str = "message_purge"
self._frequency = (
self._configuration.config.scheduled_treatment.message_purge.frequency
)
self._enabled = (
self._configuration.config.scheduled_treatment.message_purge.enabled
)
self.log_msg = MessagePurgeLogMsg()
self.logger = logging.getLogger(
"mce_sync.ScheduledTreatment.Message_Purge"
)
@property
def frequency(self):
"""Propriété pour accéder à la fréquence."""
return self._frequency
@property
def enabled(self):
"""Propriété pour accéder à l'activation ou non du traitement"""
return self._enabled
@property
def name(self):
"""Propriété pour accéder au nom du traitement"""
return self._NAME
def _search_for_accounts_to_purge(self, ldap_filter: str):
return self._search(ldap_filter)
def _build_ldap_message_purge_filter(self) -> str:
"""Construit le filtre LDAP pour rechercher les comptes à purger."""
generic_ldap_filter = self._build_generic_ldap_filter()
return self._do_build_ldap_message_purge_filter(generic_ldap_filter)
def _do_build_ldap_message_purge_filter(self, ldap_filter: str) -> str:
ldap_purge_attr_name: str = (
self._configuration.config.ldap_attributes.imap_purge
)
ldap_filter += (
f"({ldap_purge_attr_name}=*)" f"(!({ldap_purge_attr_name}=-1))" f")"
)
return ldap_filter
def _extract_user_and_purge_days(self, entry):
"""Extrait l'utilisateur et le nombre de jours à purger à partir de l'entrée LDAP."""
ldap_user_id_attr_name = (
self._configuration.config.ldap_attributes.user_id_mce
)
ldap_purge_attr_name = (
self._configuration.config.ldap_attributes.imap_purge
)
user = entry[1][ldap_user_id_attr_name][0].decode("utf-8")
days = entry[1][ldap_purge_attr_name][0].decode("utf-8")
return user, days
def execute(self):
"""Exécute la purge des messages pour un utilisateur."""
_, msg = self.log_msg.run_purge_message
self.logger.info(msg, self.name)
ldap_filter: str = self._build_ldap_message_purge_filter()
search_result = self._search_for_accounts_to_purge(ldap_filter)
if search_result:
for entry in self._search_result:
dn = entry[0][0]
user, days = self._extract_user_and_purge_days(entry)
self._treatments.messages_purge(user, days, dn)
else:
_, msg = self.log_msg.no_purge
self.logger.info(msg)
J'utilise bien les variables d'instances self.configuration, self._annuaire, et self._treatments qui proviennent de ma classe parente pourtant je n'utilise pas la méthode super dans le __init_ de la classe enfant.
Pareil dans la méthode _search_for_accounts_to_purge de la classe enfant je fais appel à la méthode search de la classe parente sans utiliser le mot clé super.
J'ai fouillé dans quelques articles, mais à chaque fois on préconise d'utiliser le mot clé super, est-ce que c'est simplement une recommandation ? Ou est-ce que les classes abstraites ont un mécanisme différent ?
Merci d'avance,
Cordialement
Salut Flavien,
Utiliser super() est une recommandation pour plusieurs raisons quand tu travailles avec l'héritage, surtout lorsqu'il s'agit d'un système d'héritage complexe avec des chaînes d'héritage multiples.
L'utilisation de super() est principalement importante pour :
-
Assurer que les constructeurs des classes de base sont appelés correctement ;
-
Maintenir la bonne ordonnance des appels dans les cas d'héritage multiple ;
-
Permettre une coopération correcte entre les classes dans une hiérarchie complexe.
Dans ton cas précis, l'absence de super() dans ton __init__ de MessagePurgeTreatment devrait normalement causer un problème parce que tu ne passes pas les arguments nécessaires au constructeur de la classe de base TreatmentStrategy. Tu peux montrer comment tu utilises tes classes ? Le code n'est peut-être jamais exécuté dans un contexte qui provoque l'appel de __init__ de MessagePurgeTreatment, donc l'erreur éventuelle n'est pas déclenchée.
Pour ton cas, si tu veux que ton héritage fonctionne comme il se doit, tu devrais passer les arguments nécessaires en faisant l'appel avec super().__init__(configuration, annuaire, treatments) dans __init__ de MessagePurgeTreatment.
Dis nous ce que tu en penses.
En ce qui concerne le super dans le init c'est une erreur de ma part, le code ne fonctionne effectivement pas. Je me suis mélangé dans mes tests.
Par contre je ne comprends pas pourquoi je peux faire appel à des méthodes de la classe mère sans utiliser le super. Avez vous une explication pour cela ?
il est possible d'appeler les méthodes de la classe parente sans utiliser explicitement super(). Quand tu appelles une méthode sur une instance d'une classe enfant, Python va d'abord chercher cette méthode dans la classe enfant. Si la méthode n'est pas trouvée dans la classe enfant, Python va chercher dans la classe parente, puis dans toutes les classes parentes dans l'ordre de la hiérarchie jusqu'à ce qu'il trouve la méthode.
Dans ton exemple, lorsque tu appelles self._search() dans la classe MessagePurgeTreatment, Python recherche d'abord cette méthode dans MessagePurgeTreatment. Si elle n'est pas trouvée, Python remonte dans la hiérarchie des classes jusqu'à ce qu'il trouve la méthode _search() dans la classe parente TreatmentStrategy.
Cependant, même si ça fonctionne, c'est bien d'utiliser super() pour appeler explicitement les méthodes de la classe parente, surtout si tu envisages d'utiliser l'héritage multiple ou si la hiérarchie de classes est complexe. Cela rend le code plus clair et aide à éviter les problèmes potentiels liés à l'ordre d'appel des méthodes dans des situations d'héritage complexe.
Je rajouterai à la réponse de Pierre-André que pour l'utilisation de super ça dépend de ce que tu veux faire.
Un exemple de code sera plus clair qu'un long discours ;)
Sans super :
class User:
def foo(self):
print("Foo de base")
class Admin(User):
def foo(self):
print("Foo d'admin")
admin = Admin()
admin.foo() # Affiche uniquement "Foo d'admin"
Avec super :
class User:
def foo(self):
print("Foo de base")
class Admin(User):
def foo(self):
super().foo()
print("Foo d'admin")
admin = Admin()
admin.foo() # Affiche "Foo de base et Foo d'admin"
Tu vois qu'avec super ça permet juste d'appeler la méthode foo de la classe dont Admin hérite : de cette façon tu peux avoir donc la fonctionnalité de la méthode de la classe parente + celle de la classe Admin. Sans super, la méthode foo de Admin va "écraser" le fonctionnement de la méthode de la classe parente qui ne sera jamais appelée.
Inscris-toi
(c'est gratuit !)
Tu dois créer un compte pour participer aux discussions.
Créer un compte