Avec les propriétés python, je peux faire en sorte que
obj.y
appelle une fonction plutôt que de simplement renvoyer une valeur.
Existe-t-il un moyen de faire cela avec des modules? J'ai un cas où je veux
module.y
pour appeler une fonction, plutôt que de simplement renvoyer la valeur qui y est stockée.
python
properties
python-module
Josh Gibson
la source
la source
__getattr__
sur un module pour une solution plus moderne.Réponses:
Seules les instances de classes de nouveau style peuvent avoir des propriétés. Vous pouvez faire croire à Python qu'une telle instance est un module en le cachant
sys.modules[thename] = theinstance
. Ainsi, par exemple, votre fichier de module m.py pourrait être:la source
types.ModuleType
comme indiqué dans la réponse par ailleurs très similaire de @ Unknown?builtins.module
, qui lui-même est une instance detype
(qui est la définition de la classe de nouveau style). Le problème est que les propriétés doivent être de la classe, et non pas l'instance: si vous le faitesf = Foo()
,f.some_property = property(...)
il échouera de la même manière que si vous le mettre dans un module ingénument. La solution est de le mettre dans la classe, mais comme vous ne voulez pas que tous les modules aient la propriété, vous sous-classez (voir la réponse de Unknown).globals()
(en gardant les clés intactes mais en réinitialisant les valeurs àNone
) lors de la réassociation du nom danssys.modules
est un problème Python 2 - Python 3.4 fonctionne comme prévu. Si vous avez besoin d'accéder à l'objet de classe dans Py2, ajoutez par exemple_M._cls = _M
juste après l'class
instruction (ou placez-le de manière équivalente dans un autre espace de noms) et accédez-y commeself._cls
dans les méthodes qui le nécessitent (celatype(self)
peut être OK mais pas si vous faites également un sous-classement de_M
) .Je ferais cela pour hériter correctement de tous les attributs d'un module, et être correctement identifié par isinstance ()
Et puis vous pouvez insérer ceci dans sys.modules:
la source
__file__
qui doivent être définis manuellement, (2) les importations effectuées dans le module contenant la classe ne seront pas "visibles" pendant l'exécution, etc ...types.ModuleType
, n'importe quelle classe (de style nouveau) fera l'affaire. Quels sont exactement les attributs spéciaux du module dont vous espérez hériter?__init__
une instance, et vous obtiendrez un comportement correct lors de l'utilisationisinstance
.Comme PEP 562 a été implémenté en Python> = 3.7, nous pouvons maintenant le faire
fichier: module.py
usage:
Notez que si vous omettez le
raise AttributeError
dans la__getattr__
fonction, cela signifie que la fonction se termine parreturn None
, alors lemodule.nosuch
obtiendra une valeur deNone
.la source
Basé sur la réponse de John Lin :
Utilisation (notez le trait de soulignement principal), dans
the_module.py
:Ensuite:
Le trait de soulignement de début est nécessaire pour différencier la fonction de propriété de la fonction d'origine. Je ne pouvais pas penser à un moyen de réaffecter l'identifiant, car pendant le temps de l'exécution du décorateur, il n'a pas encore été attribué.
Notez que les IDE ne sauront pas que la propriété existe et afficheront des ondulations rouges.
la source
@property def x(self): return self._x
je pense quedef thing()
sans soulignement, c'est plus conventionnel. Et pouvez-vous également créer un décorateur "module property setter" dans votre réponse?def thing()
suggestion. Le problème est qu'il__getattr__
n'est appelé que pour les attributs manquants . Mais après l'@module_property def thing(): …
exécution,the_module.thing
est défini, donc getattr ne sera jamais appelé. Nous devons en quelque sorte nous enregistrerthing
dans le décorateur, puis le supprimer de l'espace de noms du module. J'ai essayé de revenirNone
du décorateur, maisthing
est défini commeNone
. On pourrait faire@module_property def thing(): … del thing
mais je trouve cela pire que d'utiliserthing()
comme fonction__getattribute__
". Je vous remercie.Un cas d'utilisation typique est: enrichir un (énorme) module existant avec quelques (quelques) attributs dynamiques - sans transformer tous les éléments du module en une disposition de classe. Malheureusement, un correctif de classe de module le plus simple comme
sys.modules[__name__].__class__ = MyPropertyModule
échoue avecTypeError: __class__ assignment: only for heap types
. La création du module doit donc être recâblée.Cette approche le fait sans les hooks d'importation Python, simplement en ayant un prologue au-dessus du code du module:
Usage:
Remarque: quelque chose comme
from propertymodule import dynval
produira une copie figée bien sûr - correspondant àdynval = someobject.dynval
la source
Une réponse courte: utiliser
proxy_tools
Le
proxy_tools
package tente de fournir@module_property
fonctionnalités.Il s'installe avec
En utilisant une légère modification de l'exemple de @ Marein,
the_module.py
nous mettonsMaintenant à partir d'un autre script, je peux faire
Comportement inattendu
Cette solution n'est pas sans réserves. A savoir,
the_module.thing
n'est pas une chaîne ! C'est unproxy_tools.Proxy
objet dont les méthodes spéciales ont été surchargées pour imiter une chaîne. Voici quelques tests de base qui illustrent ce point:En interne, la fonction d'origine est stockée dans
the_module.thing._Proxy__local
:Réflexions supplémentaires
Honnêtement, je ne sais pas pourquoi les modules n'ont pas cette fonctionnalité intégrée. Je pense que le nœud du problème est qu'il
the_module
s'agit d'une instance de latypes.ModuleType
classe. Définir une "propriété de module" revient à définir une propriété sur une instance de cette classe, plutôt que sur latypes.ModuleType
classe elle-même. Pour plus de détails, consultez cette réponse .Nous pouvons réellement implémenter des propriétés
types.ModuleType
comme suit, bien que les résultats ne soient pas excellents. Nous ne pouvons pas modifier directement les types intégrés, mais nous pouvons les maudire :Cela nous donne une propriété qui existe sur tous les modules. C'est un peu difficile à manier, car nous cassons le comportement des paramètres dans tous les modules:
la source
@module_property
décorateur. De manière générale, le@property
décorateur intégré est utilisé lorsqu'une classe est définie, pas après qu'une instance de celle-ci a été créée, donc je suppose que la même chose serait vraie pour une propriété de module et c'est avec la réponse d'Alex - rappelez-vous que cette question pose "Les modules peuvent-ils avoir des propriétés de la même manière que les objets?". Cependant, il est possible de les ajouter par la suite et j'ai modifié mon extrait de code précédent pour illustrer une façon de le faire.cached_module_property
, le fait qu'il__getattr__()
ne sera plus appelé si l'attribut est défini est utile. (similaire à ce quifunctools.cached_property
accomplit).