À l'aide de méthodes statiques en python - meilleures pratiques
Quand et comment sont les méthodes statiques suppose être utilisé en python? Nous avons déjà établi à l'aide d'une méthode de classe dans l'usine de la méthode pour créer une instance d'un objet doit être évitée lorsque cela est possible. En d'autres termes, il n'est pas recommandé d'utiliser les méthodes de la classe comme un autre constructeur (Voir Usine méthode pour objet python - meilleures pratiques).
Permet de dire que j'ai une classe utilisée pour représenter une entité de données dans une base de données. Imaginez les données est un dict
objet contenant les noms des champs et des valeurs et de l'un des champs est un numéro d'identification qui rend les données unique.
class Entity(object):
def __init__(self, data, db_connection):
self._data = data
self._db_connection
Ici mon __init__
méthode prend les données de l'entité dict
objet. Disons que je n'ai qu'un numéro d'identification et je veux créer un Entity
instance. Je vais d'abord vous devez trouver le reste des données, puis créer une instance de ma Entity
objet. À partir de ma question précédente, nous avons établi que l'utilisation d'une méthode de la classe comme une méthode de fabrique doit probablement être évitée lorsque cela est possible.
class Entity(object):
@classmethod
def from_id(cls, id_number, db_connection):
filters = [['id', 'is', id_number]]
data = db_connection.find(filters)
return cls(data, db_connection)
def __init__(self, data, db_connection):
self._data = data
self._db_connection
# Create entity
entity = Entity.from_id(id_number, db_connection)
Ci-dessus est un exemple de ce que ne pas le faire ou au moins à ne pas faire si il y a une alternative. Maintenant je me demande si la modification de ma méthode de classe de sorte qu'il est plus d'une méthode utilitaire, et à moins d'une usine méthode est une solution valable. En d'autres termes, l'exemple suivant se conformer aux meilleures pratiques pour l'utilisation de méthodes statiques.
class Entity(object):
@staticmethod
def data_from_id(id_number, db_connection):
filters = [['id', 'is', id_number]]
data = db_connection.find(filters)
return data
# Create entity
data = Entity.data_from_id(id_number, db_connection)
entity = Entity(data)
Ou est-il plus logique d'utiliser une fonction autonome de trouver les données de l'entité à partir d'un numéro d'identification.
def find_data_from_id(id_number, db_connection):
filters = [['id', 'is', id_number]]
data = db_connection.find(filters)
return data
# Create entity.
data = find_data_from_id(id_number, db_connection)
entity = Entity(data, db_connection)
Remarque: je ne veux pas changer mon __init__
méthode. Auparavant, les gens ont suggéré de faire mon __init__
méthode à ressembler à quelque chose comme ceci __init__(self, data=None, id_number=None)
mais il pourrait y avoir des 101 différentes façons de trouver de l'entité de données, donc je préfère garder cette logique de séparer, dans une certaine mesure. Un sens?
OriginalL'auteur Yani | 2013-02-22
Vous devez vous connecter pour publier un commentaire.
La glib réponse est: Pas très souvent.
La même glibber mais pas tout à fait inutile réponse est: Quand ils font de votre code plus lisible.
Tout d'abord, prenons un détour pour les docs:
Donc, quand vous avez besoin d'une méthode statique en C++, vous avez besoin d'une méthode statique en Python, droit?
Eh bien, non.
En Java, il n'existe pas de fonctions, juste de méthodes, de sorte que vous finissent par créer des pseudo-classes qui sont des faisceaux de méthodes statiques. La façon de faire la même chose en Python est simplement utiliser les fonctions libres.
C'est assez évident. Toutefois, il est bon de Java style à l'air aussi dur que possible pour une classe appropriée à la cale d'une fonction, de sorte que vous pouvez éviter d'écrire ces pseudo-classes, tout en faisant la même chose est mauvais Python de style encore, utiliser des fonctions libres—et c'est beaucoup moins évident.
C++ n'ont pas la même limitation que Java, mais beaucoup de C++ styles sont assez similaires, de toute façon. (D'un autre côté, si vous êtes un "C++ Moderne" programmeur qui a intériorisé la "libre fonctions font partie d'une classe de l'interface de" l'idiome, votre instinct de "où sont des méthodes statiques utiles" sont sans doute assez décent pour Python.)
Mais si tu viens à ce à partir des premiers principes, plutôt que d'une autre langue, il y a un moyen plus simple de voir les choses:
Un
@staticmethod
est fondamentalement juste une fonction globale. Si vous avez une fonctionfoo_module.bar()
qui serait plus lisible pour quelque raison que si c'était écrit commefoo_module.BazClass.bar()
, faire un@staticmethod
. Sinon, ne le faites pas. C'est vraiment tout là est à lui. Le seul problème est la construction de votre instinct pour ce qui est plus lisible pour un idiomatiques Python programmeur.Et bien sûr utiliser un
@classmethod
lorsque vous avez besoin d'accéder à la classe, mais pas l'exemple à d'autres constructeurs sont le paradigme de cas pour que, comme les docs impliquent. Bien que vous souvent peut simuler un@classmethod
avec un@staticmethod
juste en faisant explicitement référence à la classe (surtout quand vous n'avez pas beaucoup de sous-classement), vous ne devriez pas.Enfin, arriver à votre question précise:
Si la seule raison pour laquelle les clients jamais besoin de rechercher des données par l'ID est de construire un
Entity
, qui sonne comme un détail d'implémentation vous ne devriez pas vous exposer, et il fait aussi de la client code plus complexe. Il suffit d'utiliser un constructeur. Si vous ne souhaitez pas modifier votre__init__
(et vous avez raison, il y a de bonnes raisons pour lesquelles vous pourriez ne pas vouloir), utiliser un@classmethod
comme un autre constructeur:Entity.from_id(id_number, db_connection)
.D'autre part, si cette recherche est quelque chose qui est intrinsèquement utile pour les clients dans d'autres cas qui n'ont rien à voir avec
Entity
de la construction, il semble que cela n'a rien à voir avec laEntity
de la classe (ou du moins pas plus que n'importe quoi d'autre dans le même module). Donc, il suffit de faire une fonction libre.OriginalL'auteur abarnert
La réponse à la question spécifiquement dit ceci:
Donc je ne sais pas comment vous est venue l'idée que l'utilisation d'un classmethod est intrinsèquement mauvais. En fait, j'aime l'idée d'utiliser un classmethod, dans votre situation, comme il le fait à la suite du code et à l'aide de l'api facile.
L'alternative serait d'utiliser le constructeur par défaut arguments:
Je préfère le classmethod version que vous avez écrit à l'origine, cependant. Surtout depuis
data
est assez ambiguë.OriginalL'auteur Josh Smeaton
Votre premier exemple qui fait le plus de sens pour moi:
Entity.from_id
est assez succincte et claire.Il évite l'utilisation de
data
dans les deux exemples suivants, qui ne décrit pas ce qui est retourné; ledata
est utilisée pour construire unEntity
. Si l'on voulait être précis sur ladata
utilisés pour construire lesEntity
, alors vous pourriez le nom de votre méthode de quelque chose commeEntity.with_data_for_id
ou l'équivalent de la fonctionentity_with_data_for_id
.À l'aide d'un verbe comme
find
peut également être assez déroutant, car il ne donne aucune indication de la valeur de retour — qu'est-ce que la fonction est censée faire quand il est déterminé que les données? (Oui, je me rends comptestr
a unfind
méthode; ne serait-il pas mieux nomméindex_of
? Mais ensuite, il y a aussiindex
...) Il me rappelle le classique:J'essaie toujours de penser ce nom semble indiquer à quelqu'un (une) pas de connaissance du système, et (b) la connaissance des autres parties du système — pour ne pas dire que je suis toujours un succès!
OriginalL'auteur Nicholas Riley
Ici est un bon cas d'utilisation de @staticmethod.
Je travaille sur un jeu comme un projet à part. Une partie de ce jeu comprend roulement de dés basé sur les statistiques, et la possibilité de ramasser des objets et effets qui ont un impact stats de votre personnage (pour le meilleur ou pour le pire).
Quand j'ai lancer les dés dans mon jeu, je dois dire... prendre le caractère de base stats puis ajoutez de l'inventaire et de l'effet de stats dans ce grand filet figure.
Vous ne pouvez pas prendre ces abstraite des objets et de les ajouter, sans même donner le programme comment. Je ne fais rien au niveau de la classe ou de niveau de l'instance. Je ne voulais pas de définir la fonction dans certains module global. La dernière meilleure option pour aller avec une méthode statique pour additionner les stats ensemble. Il est tout à fait le plus de sens de cette façon.
Qui ferait de la stat combinaison des appels comme ça
OriginalL'auteur Patrick Rutz