Julia: cas d'utilisation pour `import` et` using`
J'ai donc lu la documentation pour usingetimport dans Julia. Cependant, ce que cela ne me dit pas, c'est comment je devrais utiliser ces deux énoncés dans la pratique (et, étant donné le manque d'orthogonalité, ce n'est pas trop facile).
Exemple concret: mettons le code trivial suivant dans "myfile.jl":
module MyModule
f() = 1
export f
end
import .MyModule # or: using .MyModule
si j'utilise
importsur la dernière ligne, alorsfn'est pas exporté vers l'Mainespace de noms. Cependant, lorsque je change"myfile.jl"(par exemple, en modifiant la valeur de retour def) puis queincludeje la réinitialise , la fonction est remplacée (c'est le comportement que je souhaite). (Notez que je pourrais explicitementimport .MyModule: f, mais cela introduit une redondance inutile; aussi, le cas réel impliquera une longue liste de fonctions avec des noms longs. OK, je pourrais aussi écrire une macro qui utilisenames(Main.MyModule), mais je pense que cela devrait être plus simple.)si je remplace
importparusing, alors ceci est inversé:fest maintenant exporté, mais changer quoi que ce soit dans le module nécessite maintenant de redémarrer l'interpréteur Julia.en utilisant les deux
importetusingn'exporte que la première version def()vers l'espace de noms principal: lorsque je mets à jour le code, seule la première valeur de retour est utilisée.
Ma question ne porte donc pas sur le comportement des deux déclarations importet using, qui sont documentées (sinon expliquées ) dans la page liée, mais sur l'intention derrière celles-ci. Pourquoi deux déclarations alors qu'une seule suffirait? Pourquoi l'un de ceux-ci ignore-t-il toutes les exportdirectives? Dans quel cas suis-je censé, en pratique, utiliser chaque énoncé?
(la version est 1.1.0. De plus, cela fonctionne sur un système sans Pkgaccès facile , donc je n'ai pas Reviseencore essayé .)
Réponses
La convention de Julia est à utiliser using PackageNamesauf si vous avez une raison spécifique de faire autrement.
Cette décision de conception du langage est simplement motivée par le confort des utilisateurs. Julia utilise de multiples répartitions et regroupe généralement des fonctions d'exportation qui opèrent sur des arguments typés (ce sont cependant généralement des abstracttypes, donc la fonctionnalité des fonctions n'est pas limitée).
Avec cette conception, toutes les fonctions et méthodes qui les implémentent peuvent être dans le même espace de noms car elles diffèrent sur les types de paramètres qui sont spécifiques à leurs packages. Par conséquent, un scénario où usingapporte un package à l'espace de noms qui remplace les implémentations de méthode existantes est très rare (et Julia signale un avertissement). Ce processus est en outre contrôlé par les auteurs de packages grâce à l'utilisation du exportmot - clé.
Je pense que cela vous semble étrange car vous êtes habitué à des langages comme Python. Par exemple, il numpya exactement les mêmes noms de fonction que les fonctions par défaut et il n'y a pas de bon moyen de les nommer différemment (par exemple, essayez de penser à ce qui serait une bonne alternative pour une fonction nommée sin:-)). Par conséquent, en Python, vous devez toujours importer des fonctions dans leurs propres espaces de noms, peut-être avec un alias tel que import numpy as np. Après avoir eu plusieurs années d'expérience avec les deux langues, je dirais que la façon dont Julia le fait est naturelle et la manière pytonienne est étrange :-)
En ce qui concerne importet les possibilités d'apporter des fonctions uniques d'un package à votre espace de noms, je vois cela comme des solutions de contournement pour les situations où un conflit de nom se produit. Vous devez également le faire explicitement si vous souhaitez ajouter de nouvelles implémentations de méthode à un autre package (comme définir une nouvelle signification pour l' +opérateur). À part cela, vous tapez simplement usinget ne regardez jamais en arrière.
Une chose à ajouter, en plus de la réponse de Przemyslaw Szufel. Lorsque vous étendez une méthode, il est possible de le faire sans utiliser importpour la fonction que vous étendez. Par exemple, si je veux étendre à fpartir du module Foo, je peux faire using Fooet définir une nouvelle méthode pour Foo.f. Voici un exemple complet:
module Foo
struct A
x::Int
end
f(a::A) = a.x + 1
export A, f
end
using .Foo
struct B
x::Int
end
Foo.f(b::B) = b.x + 2
L'avantage de cette approche est que lorsque vous lisez le code, il est plus facile de voir l'origine de la fonction que vous étendez.