Question Existe-t-il un équivalent Python aux classes partielles?


En utilisant de "nouvelles" classes de style (je suis en python 3.2), existe-t-il un moyen de diviser une classe en plusieurs fichiers? J'ai une grande classe (qui devrait vraiment être une classe unique du point de vue de la conception orientée objet, en considérant le couplage, etc., mais ce serait bien de diviser en quelques fichiers pour faciliter la modification de la classe.


34
2018-03-09 17:30


origine


Réponses:


Si votre problème est de travailler avec une grande classe dans un éditeur, la première solution que je recherche est une meilleure façon de résoudre le problème. La seconde solution serait un meilleur éditeur, de préférence un éditeur de code.

Cela dit, il existe plusieurs manières de diviser une classe en plusieurs fichiers. Python vous permet d'utiliser un dossier en tant que module en mettant un __init__.py dans ce qui peut alors importer des choses à partir d'autres fichiers. Nous utiliserons cette fonctionnalité dans chaque solution. Créez un dossier appelé, disons, bigclass premier.

  1. Dans le dossier mettez les divers .py les fichiers qui finiront par constituer votre classe. Chacun devrait contenir des fonctions et des définitions de variables pour la classe éventuelle, ne pas Des classes. Dans __init__.py dans le même dossier, écrivez ce qui suit pour les joindre tous.

    class Bigclass(object):
    
        from classdef1 import foo, bar, baz, quux
        from classdef2 import thing1, thing2
        from classdef3 import magic, moremagic
        # unfortunately, "from classdefn import *" is an error or warning
    
        num = 42   # add more members here if you like
    

    Cela a l'avantage que vous vous retrouvez avec une seule classe dérivée directement de object, qui aura l'air bien dans vos graphiques d'héritage.

  2. Vous pouvez utiliser l'héritage multiple pour combiner les différentes parties de votre classe. Dans vos modules individuels, vous écrivez une définition de classe pour Bigclass avec des parties de la classe. Alors dans ton __init__.py écrire:

    import classdef1, classdef2, classdef3
    
    class Bigclass(classdef1.Bigclass, classdef2.Bigclass, classdef3.Bigclass):
        num = 42   # add more members if desired
    
  3. Si l'héritage multiple devient un problème, vous pouvez utiliser l'héritage simple: il suffit que chaque classe hérite d'un autre en mode chaîne. En supposant que vous ne définissiez rien dans plus d'une classe, l'ordre n'a pas d'importance. Par exemple, classdef2.py serait comme:

    import classdef1
    class Bigclass(classdef1.Bigclass):
         # more member defs here
    

    classdef3 importerait Bigclass de classdef2 et ajouter à cela, et ainsi de suite. Votre __init__.py importerait simplement le dernier:

    from classdef42 import Bigclass
    

Je préfère généralement le numéro 1 car il est plus explicite sur les membres que vous importez à partir de quels fichiers, mais n'importe laquelle de ces solutions pourrait vous convenir.

Pour utiliser la classe dans l'un de ces scénarios, vous pouvez simplement l'importer, en utilisant le nom du dossier comme nom du module: from bigclass import Bigclass


37
2018-03-09 17:48



Les définitions de classes contenant des centaines de lignes apparaissent "dans la nature" (j'en ai vu dans les frameworks open-source basés sur Python), mais je pense que si vous réfléchissez aux méthodes, il sera possible de réduire la longueur de la plupart des classes à un point gérable. Quelques exemples:

  • Recherchez les endroits où le même code se produit plus d'une fois. Décomposez ce code dans sa propre méthode et appelez-le de chaque endroit avec des arguments.
  • Les méthodes "privées" qui n'utilisent aucun état d'objet peuvent être extraites de la classe en tant que fonctions autonomes.
  • Les méthodes qui ne devraient être appelées que sous certaines conditions peuvent indiquer la nécessité de placer ces méthodes dans une sous-classe.

Pour répondre directement à votre question, il est possible de diviser la définition d'une classe. L'une des méthodes consiste à "patcher" la classe en la définissant, puis en lui ajoutant des fonctions externes en tant que méthodes. Un autre est d'utiliser le intégré typefonction pour créer la classe "à la main", en fournissant son nom, ses classes de base, ses méthodes et ses attributs dans un dictionnaire. Mais je ne recommande pas de le faire simplement parce que la définition serait longue sinon. Ce type de guérison est pire que la maladie à mon avis.


7
2018-03-09 17:43



J'ai déjà joué avec quelque chose de similaire. Mon casse-tête était une hiérarchie de classes de nœuds dans un arbre de syntaxe abstraite, puis je voulais mettre tous prettyprinting fonctions dans un fichier prettyprint.py distinct mais les avoir toujours comme méthodes dans les classes.

Une chose que j'ai essayée était d'utiliser un décorateur qui place la fonction décorée comme un attribut sur une classe spécifiée. Dans mon cas, cela signifierait que prettyprint.py contient beaucoup de def prettyprint(self) tous décorés de différents @inclass(...)

Un problème avec ceci est que l'on doit s'assurer que les sous-fichiers sont toujours importés, et qu'ils dépendent de la classe principale, ce qui crée une dépendance circulaire, qui peut être compliquée.

def inclass(kls):
    """
    Decorator that adds the decorated function
    as a method in specified class
    """
    def _(func):
        setattr(kls,func.__name__, func)
        return func
    return _

## exampe usage
class C:
    def __init__(self, d):
        self.d = d

# this would be in a separate file.
@inclass(C)
def meth(self, a):
    """Some method"""
    print "attribute: %s - argument: %s" % (self.d, a)

i = C(10)
print i.meth.__doc__
i.meth(20)

5
2018-03-09 17:56



Vous pouvez le faire avec des décorateurs comme ça:

class Car(object):

    def start(self):
    print 'Car has started'


def extends(klass):
    def decorator(func):
      setattr(klass, func.__name__, func)
      return func
    return decorator

#this can go in a different module/file
@extends(Car)
def do_start(self):
    self.start()


#so can this
car = Car()
car.do_start()

#=> Car has started

4
2018-06-11 22:10



Je ne l'ai pas utilisé, mais ce paquet appelé partiel prétend ajouter un support pour les classes partielles.

Il semble qu'il y ait d'autres façons de mettre en œuvre cela vous-même.

Vous pouvez implémenter des parties distinctes de la classe en tant que mixins dans des fichiers séparés, puis les importer tous quelque part et les sous-classer.

Alternativement, vous pouvez implémenter chacune des méthodes de votre classe, puis les importer dans un fichier central et les attribuer en tant qu'attributs sur une classe, pour créer l'objet entier. Ainsi:

a.py:

def AFunc( self, something ):
    # Do something
    pass

b.py:

def BFunc( self, something ):
    # Do something else
    pass

c.py:

import a, b

class C:
    AFunc = a.AFunc
    BFunc = b.BFunc

Vous pourriez même aller jusqu'à automatiser ce processus si vous vouliez vraiment - parcourir toutes les fonctions fournies par les modules a et b puis ajoutez-les en tant qu'attributs sur C. Bien que cela pourrait être un excès total.

Il y a peut-être d'autres façons (peut-être mieux) de s'y prendre, mais ce sont les deux qui ont été évoquées.


2
2018-03-09 17:40



J'ai rencontré la même situation - je veux glisser ma classe à 2 fichiers. la raison en est que - je veux la partie 1 pour la disposition de l'interface graphique, seule la mise en page et un autre fichier garde toutes les fonctions. comme la classe Partielle de c #. un pour XAML et un autre pour les fonctions.


1
2017-08-08 21:22



Tout d'abord, je ne vois pas comment diviser la classe en plusieurs fichiers facilite l'édition. Un IDE décent devrait être capable de trouver facilement n'importe quelle méthode, que ce soit dans un fichier ou dans un multiple; Si vous n'utilisez pas un IDE décent, le fractionnement de la classe signifie que le responsable doit deviner dans quel fichier se trouve une méthode donnée, ce qui semble plus compliqué que facile.

Plus fondamentalement, cette classe - si volumineuse que vous voulez une fonctionnalité de langage spécifique juste pour supporter son poids - semble fondamentalement brisée. De combien de lignes de code parlons-nous? Ce serait presque certainement une meilleure idée de:

  • Refactor Code dupliqué en moins de primitives plus générales
  • Définir une classe de base et l'étendre avec des sous-classes comme le suggère Karoly Horvath dans les commentaires (c'est la chose la plus proche des «classes partielles» que vous demandez que j'approuve)
  • Définir quelques classes distinctes pour encapsuler différentes parties de cette fonctionnalité de la classe, et composer cette classe d'instances de ceux les plus petites

0
2018-03-09 18:32



Tout d’abord, je voudrais dire que quelque chose de compliqué, ce n’est probablement pas une bonne idée de simplement trouver votre place dans la classe - il serait préférable d’ajouter des commentaires, de mettre en évidence des sections, etc. Cependant, je vois deux façons de le faire :

  1. Écrivez la classe dans plusieurs fichiers, puis lisez-les en tant que texte, concaténez-les et exec la chaîne résultante.

  2. Créez une classe distincte dans chaque fichier, puis héritez-les toutes dans une classe principale sous forme de mixins. Cependant, si vous sous-classe une autre classe, cela pourrait conduire à des problèmes de MRO. Vous pouvez contourner ce problème en créant une métaclasse pour votre classe principale qui résout manuellement la MRO, mais cela pourrait être compliqué.

Le plus simple serait la première option.


-1
2018-03-09 17:45