2010-09-24 4 views
2

J'ai le code suivant:classe de base dynamique et les usines

class EntityBase (object) : 
    __entity__ = None 

    def __init__ (self) : 
     pass 

def entity (name) : 
    class Entity (EntityBase) : 
     __entity__ = name 

     def __init__ (self) : 
      pass 

    return Entity 

class Smth (entity ("SMTH")) : 
    def __init__ (self, a, b) : 
     self.a = a 
     self.b = b 

# added after few comments --> 
def factory (tag) : 
    for entity in EntityBase.__subclasses__() : 
     if entity.__entity__ == tag : 
      return entity.__subclasses__()[0] 

    raise FactoryError (tag, "Unknown entity") 

s = factory ("SMTH") (1, 2) 
print (s.a, s.b) 
# <-- 

Maintenant en usine je peux obtenir toutes les sous-classes de EntityBase, trouver sous-classe concrète pour « SMTH » et créer.

Est-ce une approche valable ou peut-être que j'ai quelque chose d'incompris et de mal fait?

+0

Qu'essayez-vous de faire? Python est dactylographié, pas besoin de relations d'héritage quand vous n'êtes pas en train d'hériter ... – delnan

+0

Comment puis-je trouver mes entités sans héritage? Itère toutes les sous-classes d'objets? – W55tKQbuRu28Q4xv

+0

@ W55tKQbuRu28Q4xv Comment pouvez-vous trouver toutes vos entités * avec * héritage? De plus, ce que vous faites ici ne "trouve" pas une sous-classe concrète - elle crée une nouvelle sous-classe chaque fois que 'entity()' est appelé, même avec les valeurs 'name' avec lesquelles il a été appelé auparavant. – llasram

Répondre

9

Je le ferais avec un décorateur. En outre, stocker la carte de sous-classe entity -> dans un dictionnaire vous permet de remplacer une analyse linéaire par une recherche dict.

class EntityBase(object): 
    _entity_ = None 
    _entities_ = {} 

    @classmethod 
    def factory(cls, entity): 
     try: 
      return cls._entities_[entity] 
     except KeyError: 
      raise FactoryError(tag, "Unknown entity") 

    @classmethod 
    def register(cls, entity): 
     def decorator(subclass): 
      cls._entities_[entity] = subclass 
      subclass._entity_ = entity 
      return subclass 
     return decorator 

factory = EntityBase.factory 
register = EntityBase.register 

@register('Smith') 
class Smith(EntityBase): 
    def __init__(self, a, b): 
     self.a = a 
     self.b = b 

s = factory('Smith')(1, 2) 

Je ne suis pas sûr si l'attribut __entity__ est réellement utile pour vous de si vous étiez juste à l'utiliser pour mettre en œuvre l'analyse linéaire. Je l'ai laissé dedans mais si vous l'avez sorti, alors les classes associées à l'entité n'auraient même pas besoin d'hériter de EntityBase et vous pourriez le renommer en quelque chose comme Registry. Cela réduit votre arbre d'héritage et ouvre la possibilité d'utiliser sur des classes qui ne sont pas liées par la descendance commune.

Selon ce que votre cas d'utilisation est une meilleure façon de le faire, il pourrait juste être

factory = {} 

class Smith(object): 
    def __init__(self, a, b): 
     self.a = a 
     self.b = b 
factory['Smith'] = Smith 

class Jones(object): 
    def __init__(self, c, d): 
     self.c = c 
     self.d = d 
factory['Jones'] = Jones 

s = factory['Smith'](1, 2) 
j = factory['Jones'](3, 4) 

Le décorateur est colombophile et nous allons nous sentir bien et de fantaisie sur nous-mêmes, mais le dictionnaire est clair, utile et le point. C'est facile à comprendre et difficile à se tromper. A moins que vous ayez vraiment besoin de faire quelque chose de magique, alors je pense que c'est la voie à suivre. Pourquoi voulez-vous faire cela, de toute façon?

+0

Gardez à l'esprit que les identificateurs qui commencent et se terminent par «__» sont réservés à Python. Pas besoin d'encourager les mauvaises habitudes! :-) – llasram

+0

@llasram bien regarder. tous les enfants cool le font et j'oublie parfois. – aaronasterling

+0

Merci pour vos réponses. Je connais les décorateurs et j'ai mis en place un "truc d'usine" sur les décorateurs. Par ce code je veux réduire "l'interface d'usine" du décorateur + héritage à l'héritage seulement. Je sais que je ne peux l'implémenter que sur des décorateurs, mais si je prolonge la classe dans les avertissements de salon pylint décorateur sur cette extension. – W55tKQbuRu28Q4xv

-1

cette approche valide ou peut-être que j'ai quelque chose d'incompris et mal faire?

Cela fonctionne. Donc, dans un sens, c'est "valide".

C'est un gaspillage total de code. Donc, dans un sens, ce n'est pas "valide".

Il n'y a pas de cas d'utilisation pour ce type de construction. Maintenant que vous l'avez construit, vous pouvez passer à la résolution de problèmes pratiques.

5

Je pense que c'est l'un des rares cas où vous voulez un Python metaclass:

class Entity(object): 
    class __metaclass__(type): 
     ENTITIES = {} 

     def __new__(mcs, name, bases, cdict): 
      cls = type.__new__(mcs, name, bases, cdict) 
      try: 
       entity = cdict['_entity_'] 
       mcs.ENTITIES[entity] = cls 
      except KeyError: 
       pass 
      return cls 

    @classmethod 
    def factory(cls, name): 
     return cls.__metaclass__.ENTITIES[name] 

class Smth(Entity): 
    _entity_ = 'SMTH' 

    def __init__(self, a, b): 
     self.a = a 
     self.b = b 

s = Entity.factory("SMTH")(1, 2) 
print (s.a, s.b) 

Quelques différences plus subtiles de votre code:

  • Il n'y a pas besoin de créer une sous-classe votre entity() usine fonction puis sous-classe cette sous-classe. Cette approche crée non seulement plus de sous-classes que nécessaire, mais rend également votre code ne fonctionne pas car EntityBase.__subclasses__() ne contient pas la classe Smth.
  • Les identificateurs qui commencent et se terminent par __ sont réservés à Python. J'utilise donc l'attribut _entity_ au lieu de __entity__.
+0

Merci pour votre réponse. Par mon code, je veux minimiser l'interface de l'usine et je pense que l'héritage de l'entité et _entity_ sur différentes lignes d'erreur sujettes et parfois quelqu'un oublie de le faire;) – W55tKQbuRu28Q4xv

3

Une métaclasse peut garder une trace des classes définies. Register.__init__ est appelée lorsqu'une classe avec cette métaclasse est définie. Nous pouvons simplement ajouter le nom et l'objet à une dict du registre dans la métaclasse.De cette façon, vous pouvez le rechercher directement plus tard.

registry = {} # dict of subclasses 

def get_entity(name): 
    return registry[name]  

class Register(type): 
    def __init__(cls, name, bases, dict): 
     registry[name] = cls 
     type.__init__(cls,name, bases, dict) 

class EntityBase(object): 
    __metaclass__ = Register 

class OneThing(EntityBase): 
    pass 

class OtherThing(OneThing): 
    pass 

print registry # dict with Entitybase, OneThing, OtherThing 
print get_entity("OtherThing") # <class '__main__.OtherThing'> 

BTW, une usine instancier des classes, de sorte que le nom ne convient pas pour une fonction qui retourne une seule classe.

Questions connexes