Python decorar uma classe à mudança pai tipo de objeto
-
05-07-2019 - |
Pergunta
Suponha que você tenha duas classes X & Y. Você quer decorar essas classes, adicionando atributos para a classe para produzir novas classes X1 e Y1.
Por exemplo:
class X1(X):
new_attribute = 'something'
class Y1(Y):
new_attribute = 'something'
new_attribute será sempre o mesmo para ambos X1 e Y1. X e Y não estão relacionados de qualquer modo significativo, excepto que a herança múltipla não é possível. Há um conjunto de outros atributos, bem como, mas isso é degenerado para ilustrar.
Eu sinto que estou complicar isso, mas eu tinha pensado em usar um decorador, um pouco likeso:
def _xywrap(cls):
class _xy(cls):
new_attribute = 'something'
return _xy
@_xywrap(X)
class X1():
pass
@_xywrap(Y)
class Y1():
pass
Parece que eu estou sentindo falta de um padrão bastante comum, e eu ficaria muito grato por pensamentos, opiniões e comentários.
Obrigado pela leitura.
Brian
Aqui está um extrato relevante que possa iluminar. As classes comuns são as seguintes:
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
Aqui está um sub-documento:
class Thing(AccessExpando):
it = db.StringProperty()
Às vezes coisa vai ter as seguintes propriedades:
Thing { it: ... }
e outras vezes:
Thing { it: ..., users_permitted:..., owner:... }
Eu fui incapaz de descobrir por que coisa, às vezes, tem suas propriedades _AccessParent, e outras vezes não.
Solução
Use 3-argumentos digite :
def makeSomeNicelyDecoratedSubclass(someclass):
return type('MyNiceName', (someclass,), {'new_attribute':'something'})
Esta é, de facto, como você imaginou, uma linguagem razoavelmente popular.
Editar : no caso geral se SomeClass tem uma metaclass personalizado que você pode precisar para extrair e usá-lo (com um type
1-argumento) em vez de si type
, para preservá-lo (pode ser o caso para os seus modelos Django e Google App Engine):
def makeSomeNicelyDecoratedSubclass(someclass):
mcl = type(someclass)
return mcl('MyNiceName', (someclass,), {'new_attribute':'something'})
Isso também funciona onde a versão mais simples acima faz (uma vez que em casos simples w / não metaclasses personalizados type(someclass) is type
).
Outras dicas
Em resposta a seus comentários sobre a resposta :
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
Running que resulta em
Buck Armstrong, Dinosaur Hunter 1 2 3
5000 1 2 3
Não é isso que você tinha em mente?
Por que você não pode usar herança múltipla ?
class Origin:
new_attribute = 'something'
class X:
pass
class Y:
pass
class X1(Origin, X):
pass
class Y1(Origin, Y):
pass