web-dev-qa-db-fra.com

Regroupement de fonctions à l'aide de classes en Python

Je suis programmeur scientifique Python depuis quelques années maintenant et je me trouve confronté à un problème spécifique en raison de l’agrandissement de mes programmes. Je suis un autodidacte, donc je n’ai jamais suivi de formation formelle et n’ai passé vraiment de temps à «convenir» de coder en Python.

Quoi qu'il en soit, je me trouve toujours en train de créer un fichier utils.py dans lequel sont stockées toutes les fonctions définies et utilisées par mes programmes. Je me retrouve alors à grouper ces fonctions dans leurs buts respectifs. Bien sûr, l’utilisation des classes est un moyen de regrouper les choses, mais j’ignore si ma stratégie va à l’encontre de l’utilisation des classes.

Disons que j'ai un tas de fonctions qui font à peu près la même chose comme ceci:

def add(a,b):
    return a + b

def sub(a,b):
    return a -b

def cap(string):
    return string.title()

def lower(string):
    return string.lower()

Il est évident que ces 4 fonctions peuvent être considérées comme faisant deux choses distinctes: le calcul et le formatage. C'est ce que la logique me dit de faire, mais je dois contourner le problème car je ne souhaite pas initialiser une variable qui correspond à la classe evertime.

class calc_funcs(object):

    def __init__(self):
        pass

    @staticmethod
    def add(a,b):
        return a + b

    @staticmethod
    def sub(a, b):
        return a - b

class format_funcs(object):
    def __init__(self):
        pass

    @staticmethod
    def cap(string):
        return string.title()

    @staticmethod
    def lower(string):
        return string.lower()

De cette façon, j'ai maintenant regroupé ces méthodes dans un package Nice permettant de trouver beaucoup plus rapidement les méthodes souhaitées en fonction de leur rôle dans le programme.

print calc_funcs.add(1,2)
print format_funcs.lower("Hello Bob")

Cependant, cela étant dit, j’estime que c’est une façon très «unpython-y» de faire les choses, et cela semble désordonné. Est-ce que j'y pense ou est-ce qu'il y a une autre méthode?

15
Uys of Spades

Une autre approche consiste à créer un paquet util package et à répartir vos fonctions dans différents modules de ce paquet. Notions de base sur les packages: créez un répertoire (dont le nom sera le nom du package) et placez-y un fichier spécial, le fichier __init__.py. Ceci can contient du code, mais pour l’organisation de paquetage de base, il peut s’agir d’un fichier vide.

my_package/
  __init__.py
  module1.py/
  modle2.py/
  ...
  module3.py

Alors disons que vous êtes dans votre répertoire de travail:

mkdir util
touch util/__init__.py

Puis dans votre répertoire util, créez calc_funcs.py

def add(a,b):
    return a + b

def sub(a,b):
    return a -b

Et format_funcs.py:

def cap(string):
    return string.title()

def lower(string):
    return string.lower()

Et maintenant, depuis votre répertoire de travail, vous pouvez faire les choses suivantes:

>>> from util import calc_funcs
>>> calc_funcs.add(1,3)
4
>>> from util.format_funcs import cap
>>> cap("the quick brown fox jumped over the lazy dog")
'The Quick Brown Fox Jumped Over The Lazy Dog'

Édité pour ajouter 

Notez, cependant, si nous relançons la session d'interprétation:

>>> import util
>>> util.format_funcs.cap("i should've been a book")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'util' has no attribute 'format_funcs'

C'est ce que le __init__.py est pour!

Dans __init__.py, ajoutez ce qui suit:

import util.calc_funcs, util.format_funcs

Maintenant, redémarrez l'interprète à nouveau:

>>> import util
>>> util.calc_funcs.add('1','2')
'12'
>>> util.format_funcs.lower("I DON'T KNOW WHAT I'M YELLING ABOUT")
"i don't know what i'm yelling about"

Yay! Nous avons un contrôle flexible sur nos espaces de noms avec une importation facile! Fondamentalement, le __init__.py joue un rôle analogue à la méthode __init__ dans une définition de classe.

13
juanpa.arrivillaga

Je ne voudrais pas utiliser un class pour cela, je voudrais utiliser un module. Une classe composée uniquement de méthodes statiques me semble également être une odeur de code. Voici comment procéder avec des modules: chaque fois que vous collez du code dans un fichier séparé et l'importez dans un autre fichier, Python colle ce code dans un module portant le même nom que le fichier. Donc dans votre cas:

Dans mathutil.py

def add(a,b):
    return a+b

def sub(a,b):
    return a-b

Dans main.py

import mathutil

def main():
    c = mathutil.add(a,b)

Ou bien, si vous utilisez mathutil dans de nombreux endroits et que vous ne voulez pas taper (et lire) le nom complet du module à chaque fois, créez une abréviation standard et utilisez-la partout:

Dans main.py, version alternative

import mathutil as mu

def main():
    c = mu.add(a,b)

Comparé à votre méthode, vous aurez plus de fichiers avec moins de fonctions dans chacun d'eux, mais je pense qu'il est plus facile de naviguer dans votre code de cette façon.

À propos, il existe un peu une convention Python pour nommer des fichiers/modules: des noms courts, tous en minuscules, sans trait de soulignement entre les mots. Ce n'est pas ce que j'ai commencé à faire, mais je suis passé à le faire de cette façon dans mon code et cela a rendu plus facile la compréhension de la structure des modules des autres personnes que j'ai utilisés.

2
JonathanZ

Je pense que cela est parfaitement Pythonic. C'est exactement le but du constructeur staticmethod.

Pour les conventions python, voir PEP 8 .

0
nalzok