2009-05-19 11 views
69

Avec des propriétés de python, je peux le faire de telle sorte queLes modules peuvent-ils avoir les mêmes propriétés que les objets?

obj.y 

appelle une fonction plutôt que de retourner une valeur.

Existe-t-il un moyen de faire cela avec les modules? J'ai un cas où je veux

module.y 

d'appeler une fonction, plutôt que de simplement retourner la valeur stockée là.

+2

Quel est le problème avec l'appel d'une fonction? –

+8

voulez faire moins de frappe pour faire certaines choses qui se font beaucoup. –

Répondre

50

Seules les instances de classes de style nouveau peuvent avoir des propriétés. Vous pouvez faire croire à Python qu'une telle instance est un module en la stockant dans sys.modules[thename] = theinstance. Ainsi, par exemple, votre fichier module m.py pourrait être:

import sys 
class _M(object): 
    def __init__(self): 
    self.c = 0 
    def afunction(self): 
    self.c += 1 
    return self.c 
    y = property(afunction) 
sys.modules[__name__] = _M() 

Edité: supprimé une dépendance implicite globals (n'a rien à voir avec le point de l'exemple mais ne confondez pas les choses en faisant la le code original échoue!).

+0

Je n'aurais jamais pensé faire ça. J'avoue que je ne vais probablement pas l'utiliser, mais on ne sait jamais. Toujours heureux d'apprendre des avenues inexplorées en Python ... merci. –

+1

Est-ce que quelqu'un d'autre a essayé cela? Lorsque je mets ce code dans un fichier x.py et l'importe d'un autre, alors l'appel de xy donne AttributeError: l'objet 'NoneType' n'a pas d'attribut 'c', puisque _M a une valeur None ... – Stephan202

+0

Vous devez faire une erreur de frappe . Vérifiez une session interactive: $ python ... >>> import sys >>> classe _M (objet): ... c = 0 ... def AFunction (auto): ... _M .c + = 1 ... return _M.c ... y = propriété (fonction) ... >>> sys.Les modules [ 'bowwow'] = _M() >>> import bowwow >>> bowwow.y 1 >>> bowwow.y 2 >>> ... exactement comme prévu, bien sûr. Alors, qu'est-ce qui est différent dans ce que vous faites exactement? –

44

je ferais ceci afin d'hériter correctement tous les attributs d'un module et être correctement identifiés par isinstance()

import types 

class MyModule(types.ModuleType): 
    @property 
    def y(self): 
     return 5 


>>> a=MyModule("test") 
>>> a 
<module 'test' (built-in)> 
>>> a.y 
5 

Et puis vous pouvez insérer dans ce sys.modules:

sys.modules[__name__] = MyModule(__name__) # remember to instantiate the class 
+0

Cela semble ne fonctionner que pour le plus simple des cas. Les problèmes possibles sont les suivants: (1) certains importateurs peuvent aussi s'attendre à ce que d'autres attributs tels que '__file__' soient définis manuellement, (2) les imports effectués dans le module contenant la classe ne seront pas visibles pendant l'exécution, etc. – tutuDajuju

+1

Il n'est pas nécessaire de dériver une sous-classe de 'types.ModuleType', _any_ (new-style) fera l'affaire. Exactement ce que les attributs de module spéciaux où vous espérez hériter? – martineau

+0

Que faire si le module d'origine est un package et que je souhaite accéder aux modules sous le module d'origine? –

1

Un cas d'utilisation typique est: enrichir un module (énorme) existant avec quelques (quelques) attributs dynamiques - sans transformer tout le contenu du module en une présentation de classe. Malheureusement, un correctif de classe de module plus simple comme sys.modules[__name__].__class__ = MyPropertyModule échoue avec TypeError: __class__ assignment: only for heap types. La création du module doit donc être recâblée.

cette approche, il fait sans crochets d'importation Python, tout en ayant une Prolog au-dessus du code du module:

# propertymodule.py 
""" Module property example """ 

if '__orgmod__' not in globals(): 

    # constant prolog for having module properties/supports reload() 

    print "PropertyModule stub execution", __name__ 
    import sys, types 
    class PropertyModule(types.ModuleType): 
     def __str__(self): 
      return "<PropertyModule %r from %r>" % (self.__name__, self.__file__) 
    modnew = PropertyModule(__name__, __doc__) 
    modnew.__modclass__ = PropertyModule   
    modnew.__file__ = __file__ 
    modnew.__orgmod__ = sys.modules[__name__] 
    sys.modules[__name__] = modnew 
    exec sys._getframe().f_code in modnew.__dict__ 

else: 

    # normal module code (usually vast) .. 

    print "regular module execution" 
    a = 7 

    def get_dynval(module): 
     return "property function returns %s in module %r" % (a * 4, module.__name__)  
    __modclass__.dynval = property(get_dynval) 

Utilisation:

>>> import propertymodule 
PropertyModule stub execution propertymodule 
regular module execution 
>>> propertymodule.dynval 
"property function returns 28 in module 'propertymodule'" 
>>> reload(propertymodule) # AFTER EDITS 
regular module execution 
<module 'propertymodule' from 'propertymodule.pyc'> 
>>> propertymodule.dynval 
"property function returns 36 in module 'propertymodule'" 

Note: Quelque chose comme from propertymodule import dynval produira une gelée copie du cours - correspondant à dynval = someobject.dynval