Supposons que ma fonction utilise un fichier texte - par exemple, la lit et supprime le mot "a". Je pouvais soit lui passer un nom de fichier et gérer l'ouverture / la fermeture de la fonction, soit le transmettre le fichier ouvert et m'attendre à ce que celui qui l'appelle s'occupe de le fermer.
Le premier moyen semble être un meilleur moyen de garantir qu'aucun fichier n'est laissé ouvert, mais m'empêche d'utiliser des objets tels que des objets StringIO.
La deuxième façon pourrait être un peu dangereuse - aucun moyen de savoir si le fichier sera fermé ou non, mais je pourrais utiliser des objets de type fichier
def ver_1(filename):
with open(filename, 'r') as f:
return do_stuff(f)
def ver_2(open_file):
return do_stuff(open_file)
print ver_1('my_file.txt')
with open('my_file.txt', 'r') as f:
print ver_2(f)
Est-ce que l'un de ceux-ci est généralement préféré? Est-il généralement prévu qu'une fonction se comporte de l'une de ces deux manières? Ou devrait-il simplement être bien documenté pour que le programmeur puisse utiliser la fonction de manière appropriée?
la source
your_function
peut être utilisé à cet égard.La vraie question est celle de la complétude. Votre fonction de traitement de fichier est-elle le traitement complet du fichier ou s'agit-il simplement d'une partie d'une chaîne d'étapes de traitement? S'il est complet et autonome, n'hésitez pas à encapsuler tous les accès aux fichiers au sein d'une fonction.
Cela a la très belle propriété de finaliser la ressource (fermeture du fichier) à la fin de la
with
déclaration.Si toutefois il est éventuellement nécessaire de traiter un fichier déjà ouvert, la distinction entre vous
ver_1
et aver_2
plus de sens. Par exemple:Ce type de test de type explicite est souvent mal vu , en particulier dans des langages tels que Java, Julia et Go, où la distribution par type ou par interface est directement prise en charge. En Python, toutefois, il n'y a pas de prise en charge linguistique pour la répartition basée sur un type. Vous pouvez parfois rencontrer des critiques sur les tests de type directs en Python, mais dans la pratique, elles sont extrêmement courantes et très efficaces. Il permet à une fonction d’avoir un degré de généralité élevé et de manipuler tous les types de données susceptibles de lui arriver, c'est-à-dire "frappe de canard". Notez le trait de soulignement principal sur
_ver_file
; c’est une manière conventionnelle de désigner une fonction (ou une méthode) "privée". Bien qu’elle puisse techniquement être appelée directement, elle suggère que la fonction n’est pas destinée à une consommation externe directe.2019 Mise à jour: Compte tenu des mises à jour récentes en Python 3, par exemple , que les chemins sont maintenant potentiellement stockées sous forme d'
pathlib.Path
objets non seulementstr
oubytes
(3.4+), et que le type hinting est passé de ésotérique à intégrer (vers 3.6+, mais toujours en évolution active), voici code mis à jour qui prend en compte ces avancées:la source
read
quelque chose qui pourrait ressembler à un fichier, ou appeleropen(fileobj, 'r')
et intercepter leTypeError
sifileobj
n'est pas une chaîne.ver
opération indépendamment du type.ver
Comme vous le dites, il est peut-être également possible d’implémenter la technique de frappe de canard. Cependant, générer des exceptions est plus lent que la simple inspection de type et l’OMI ne procure aucun avantage particulier (clarté, généralité, etc.). D’après mon expérience, la frappe de canards est géniale "dans le grand", mais neutre à contre-productive "dans le petit . "hasattr(fileobj, 'read')
test serait la frappe de canard; unisinstance(fileobj, str)
test n'est pas. Voici un exemple de la différence: leisinstance
test échoue avec les noms de fichiers unicode, caru'adsf.txt'
n’est pas unstr
. Vous avez testé un type trop spécifique. Un test de frappe de canard, qu'il soit basé sur un appelopen
ou sur unedoes_this_object_represent_a_filename
fonction hypothétique , n'aurait pas ce problème.is_instance(x, str)
mais plutôt quelque chose commeis_instance(x, string_types)
, avecstring_types
un réglage approprié pour un fonctionnement correct sur les années PY2 et PY3. Étant donné que quelque chose qui plaisante comme une ficelle,ver
réagirait correctement; étant donné quelque chose qui plaisante comme un fichier, le même. Pour un utilisateur dever
, il n'y aurait aucune différence, à l'exception du fait que l'implémentation de l'inspection de type serait plus rapide. Puristes de canard: n'hésitez pas à être en désaccord.Si vous passez le nom du fichier à la place du descripteur de fichier, rien ne garantit que le deuxième fichier est le même fichier que le premier à l'ouverture du fichier; Cela peut entraîner des erreurs de correction et des failles de sécurité.
la source
Il s'agit de la propriété et de la responsabilité de fermer le fichier. Vous pouvez transmettre un flux ou un descripteur de fichier ou tout autre élément qui devrait être fermé / éliminé à un moment donné à une autre méthode, à condition que vous sachiez qui est le propriétaire et certain qu'il sera fermé par le propriétaire lorsque vous aurez terminé. . Cela implique généralement une construction try-finally ou le motif jetable.
la source
Si vous choisissez de transmettre des fichiers ouverts, vous pouvez procéder de la manière suivante, MAIS vous n’avez pas accès au nom de fichier dans la fonction qui écrit dans le fichier.
Je le ferais si je voulais avoir une classe qui était à 100% responsable des opérations de fichier / flux et d'autres classes ou fonctions qui seraient naïves et ne devraient pas ouvrir ou fermer lesdits fichiers / flux.
Rappelez-vous que les gestionnaires de contexte fonctionnent comme si vous aviez une clause finally. Donc, si une exception est levée dans la fonction writer, le fichier va être fermé quoi qu'il arrive.
la source
with open
? Comment cela résout-il la question de l'utilisation des noms de fichiers par rapport aux objets de type fichier?with open
bien, non? Et ce que vous préconisez efficacement, c’est une fonction qui n’utilise que des objets de type fichier, sans se soucier de sa provenance?