Puisque vous voulez utiliser self.format
comme argument par défaut, cela implique que la méthode doit être spécifique à l'instance (c'est-à-dire qu'il n'y a aucun moyen de la définir au niveau de la classe). Au lieu de cela, vous pouvez définir la méthode spécifique lors de la création de la classe. __init__
par exemple. C'est là que vous avez accès aux attributs spécifiques de l'instance.
Une approche consiste à utiliser functools.partial
afin d'obtenir une version actualisée (spécifique) de la méthode :
from functools import partial
class C:
def __init__(self, format):
self.format = format
self.process = partial(self.process, formatting=self.format)
def process(self, formatting):
print(formatting)
c = C('default')
c.process()
# c.process('custom') # Doesn't work!
c.process(formatting='custom')
Notez qu'avec cette approche vous ne pouvez passer l'argument correspondant que par mot-clé, car si vous le fournissiez par position, cela créerait un conflit dans partial
.
Une autre approche consiste à définir et à paramétrer la méthode dans le fichier __init__
:
from types import MethodType
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=self.format):
print(formatting)
self.process = MethodType(process, self)
c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')
Cela permet également de passer l'argument par position, cependant l'ordre de résolution de la méthode devient moins apparent (ce qui peut affecter l'inspection de l'IDE par exemple, mais je suppose qu'il existe des solutions de contournement spécifiques à l'IDE pour cela).
Une autre approche consisterait à créer un type personnalisé pour ce type de "défaut d'attribut d'instance", ainsi qu'un décorateur spécial qui exécute les fonctions correspondantes de l'attribut d'instance. getattr
le remplissage des arguments :
import inspect
class Attribute:
def __init__(self, name):
self.name = name
def decorator(method):
signature = inspect.signature(method)
def wrapper(self, *args, **kwargs):
bound = signature.bind(*((self,) + args), **kwargs)
bound.apply_defaults()
bound.arguments.update({k: getattr(self, v.name) for k, v in bound.arguments.items()
if isinstance(v, Attribute)})
return method(*bound.args, **bound.kwargs)
return wrapper
class C:
def __init__(self, format):
self.format = format
@decorator
def process(self, formatting=Attribute('format')):
print(formatting)
c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')