109 votes

Les modules peuvent-ils avoir des propriétés de la même manière que les objets ?

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.

3 votes

Ver __getattr__ sur un module pour une solution plus moderne.

1voto

Ben Mares Points 305

Une réponse courte : utilisez proxy_tools

El proxy_tools Le paquet tente de fournir @module_property fonctionnalité.

Il s'installe avec

pip install proxy_tools

En utilisant une légère modification de l'exemple de @Marein, en the_module.py nous mettons

from proxy_tools import module_property

@module_property
def thing():
    print(". ", end='')  # Prints ". " on each invocation
    return 'hello'

Maintenant, à partir d'un autre script, je peux faire

import the_module

print(the_module.thing)
# . hello

Comportement inattendu

Cette solution n'est pas sans inconvénient. A savoir , the_module.thing es pas une chaîne ! Il s'agit d'un proxy_tools.Proxy dont les méthodes spéciales ont été surchargées afin qu'il imite une chaîne de caractères. Voici quelques tests de base qui illustrent ce point :

res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]

print(type(res))
# <class 'proxy_tools.Proxy'>

print(isinstance(res, str))
# False

print(res)
# . hello

print(res + " there")
# . hello there

print(isinstance(res + "", str))
# . True

print(res.split('e'))
# . ['h', 'llo']

En interne, la fonction originale est stockée dans the_module.thing._Proxy__local :

print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>

Autres réflexions

Honnêtement, je ne comprends pas pourquoi les modules n'ont pas cette fonctionnalité intégrée. Je pense que le nœud du problème est que the_module est une instance de la types.ModuleType classe. Définir une "propriété de module" revient à définir une propriété sur une classe instance de cette classe, plutôt que sur le types.ModuleType la classe elle-même. Pour plus de détails, voir cette réponse .

Nous pouvons en fait mettre en œuvre des propriétés sur 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 malédiction les :

# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))

Cela nous donne une propriété qui existe sur tous les modules. C'est un peu lourd, car nous cassons le comportement de réglage dans tous les modules :

import sys

print(sys.thing2)
# hi from sys

sys.thing2 = 5
# AttributeError: can't set attribute

0voto

guoyongzhi Points 21

Sur la base de La réponse de user2124834 :

import sys
class AttrGeter:
    def __new__(cls, gt):
        if isinstance(gt, cls):
            return gt
        else:
            o = super().__new__(cls)
            o.oldgetattr = gt
            o.funcmap = {}
            return o

    def __call__(self, name):
        name2 = "_" + name
        if name2 in self.funcmap:
            return self.funcmap[name2]()
        else:
            return self.oldgetattr(name)

    def add(self, func):
        self.funcmap[func.__name__] = func

def module_property(func):
    """Decorator to turn module functions into properties.
    Function names must be prefixed with an underscore."""
    module = sys.modules[func.__module__]
    def base_getattr(name):
        raise AttributeError(
            f"module '{module.__name__}' has no attribute '{name}'")
    ag = AttrGeter(getattr(module, '__getattr__', base_getattr))
    module.__getattr__ = ag
    ag.add(func)
    return func

Utilisation (notez le trait de soulignement en tête), dans the_module.py :

@module_property
def _thing():
    return 'hello'

Ensuite :

import the_module

print(the_module.thing)  # prints 'hello'

J'utilise un dict au lieu d'emboîtés function dans la solution originale. Cela peut être plus efficace lorsque le décorateur est utilisé plusieurs fois dans un même module.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X