Qui probabilmente vuoi un metaclasse: una metaclasse semplicemente definisce come viene creata una classe. Per impostazione predefinita, tutte le classi vengono creati utilizzando classe built-in di Python type
:
>>> class Foo:
... pass
...
>>> type(Foo)
<class 'type'>
>>> isinstance(Foo, type)
True
Così le classi sono in realtà casi di type
. Ora, possiamo sottoclasse type
per creare un metaclasse personalizzato (una classe che crea classi):
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
Abbiamo bisogno di controllare la creazione delle nostre classi, quindi vogliamo ignorare il type.__new__
qui, e utilizzare il patch
decoratore su tutte le nuove istanze:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __new__(meta, name, bases, attrs):
cls = type.__new__(meta, name, bases, attrs)
cls = patch("some.core.function", mocked_method)(cls)
return cls
E ora è sufficiente impostare la metaclasse utilizzando __metaclass__ = PatchMeta
:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
# methods
La questione è questa linea:
cls = patch("some.core.function", mocked_method)(cls)
Quindi attualmente abbiamo sempre decoriamo con argomenti "some.core.function"
e mocked_method
. Invece si potrebbe fare in modo che esso utilizza gli attributi della classe, in questo modo:
cls = patch(*cls.patch_args)(cls)
e quindi aggiungere patch_args
alle classi:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
patch_args = ("some.core.function", mocked_method)
Edit: Come @mgilson menzionato nei commenti, patch()
modifica i metodi della classe, invece di restituire una nuova classe.A causa di questo, siamo in grado di sostituire il __new__
con questo __init__
:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __init__(cls, *args, **kwargs):
super(PatchMeta, self).__init__(*args, **kwargs)
patch(*cls.patch_args)(cls)
Quale è più pulita abbastanza indiscutibilmente.
Questo è probabilmente il luogo in cui vuoi cercare ["metaclasses python" di google (https://www.google.fi/search?q=python+metaclasses) e continuare a leggere finché non capisci come funzionano. Metaclass è ereditato dalle sottoclassi, i decoratori decorano solo la classe su cui sono utilizzati. –
Ah, penso di capire un po 'cosa intendi. Le patch si verificano solo su istanze di classi? – sihrc
No, 'patch' è un decoratore che prende solo la classe direttamente al di sotto e decora quello. Ora qualsiasi sottoclasse non sarà decorata, saranno solo classi normali. I metaclassi controllano il comportamento di come vengono create le classi e quindi possono applicare una patch a una classe quando viene creata per la prima volta. I metaclassi funzionano anche su sottoclassi dopo che è stato impostato il metaclasse della classe base, quindi anche le sottoclassi verranno applicate. –