Python décorer une classe pour changer le type d'objet parent
-
05-07-2019 - |
Question
Supposons que vous ayez deux classes X & amp; Y. Vous souhaitez décorer ces classes en ajoutant des attributs à la classe pour produire de nouvelles classes X1 et Y1.
Par exemple:
class X1(X):
new_attribute = 'something'
class Y1(Y):
new_attribute = 'something'
new_attribute sera toujours le même pour X1 et Y1. X & amp; Y ne sont pas liés de manière significative, sauf que l'héritage multiple n'est pas possible. Il existe également un ensemble d’attributs, mais c’est dégénéré, pour illustrer.
J'ai l'impression de trop compliquer les choses, mais j'avais pensé utiliser un décorateur, un peu comme j'aime:
def _xywrap(cls):
class _xy(cls):
new_attribute = 'something'
return _xy
@_xywrap(X)
class X1():
pass
@_xywrap(Y)
class Y1():
pass
Il me semble qu’il me manque un schéma assez commun et je serais très obligé de nous faire part de ses réflexions, de sa contribution et de ses commentaires.
Merci d'avoir lu.
Brian
MODIFIER: Exemple:
Voici un extrait pertinent qui peut s’éclairer. Les classes communes sont les suivantes:
from google.appengine.ext import db
# I'm including PermittedUserProperty because it may have pertinent side-effects
# (albeit unlikely), which is documented here: [How can you limit access to a
# GAE instance to the current user][1].
class _AccessBase:
users_permitted = PermittedUserProperty()
owner = db.ReferenceProperty(User)
class AccessModel(db.Model, _AccessBase):
pass
class AccessExpando(db.Expando, _AccessBase):
pass
# the order of _AccessBase/db.* doesn't seem to resolve the issue
class AccessPolyModel(_AccessBase, polymodel.PolyModel):
pass
Voici un sous-document:
class Thing(AccessExpando):
it = db.StringProperty()
Parfois, Chose aura les propriétés suivantes:
Thing { it: ... }
Et d'autres fois:
Thing { it: ..., users_permitted:..., owner:... }
Je ne suis pas parvenu à comprendre pourquoi Thing aurait parfois ses propriétés _AccessParent et d'autres fois.
La solution
Utilisez les 3 arguments tapez :
def makeSomeNicelyDecoratedSubclass(someclass):
return type('MyNiceName', (someclass,), {'new_attribute':'something'})
C’est effectivement, comme vous l’imaginez, un idiome relativement populaire.
Modifier : dans le cas général, si une classe a une métaclasse personnalisée, vous devrez peut-être l'extraire et l'utiliser (avec un type
à la place de tapez
lui-même, pour le conserver (cela peut être le cas pour vos modèles Django et App Engine):
def makeSomeNicelyDecoratedSubclass(someclass):
mcl = type(someclass)
return mcl('MyNiceName', (someclass,), {'new_attribute':'something'})
Cela fonctionne également à la place de la version simplifiée ci-dessus (car dans les cas simples, aucune métaclasse personnalisée type (someclass) n'est que type
).
Autres conseils
En réponse à vos commentaires sur réponse du voyageur :
from google.appengine.ext import db
class Mixin(object):
"""Mix in attributes shared by different types of models."""
foo = 1
bar = 2
baz = 3
class Person(db.Model, Mixin):
name = db.StringProperty()
class Dinosaur(db.polymodel.PolyModel, Mixin):
height = db.IntegerProperty()
p = Person(name='Buck Armstrong, Dinosaur Hunter')
d = Dinosaur(height=5000)
print p.name, p.foo, p.bar, p.baz
print d.height, d.foo, d.bar, d.baz
L'exécution qui en résulte
Buck Armstrong, Dinosaur Hunter 1 2 3
5000 1 2 3
N’est-ce pas ce que vous aviez en tête?
Pourquoi ne pouvez-vous pas utiliser l'héritage multiple ?
class Origin:
new_attribute = 'something'
class X:
pass
class Y:
pass
class X1(Origin, X):
pass
class Y1(Origin, Y):
pass