Can modules have properties the same way that objects can?

With python properties, I can make it such that

obj.y 

calls a function rather than just returning a value.

Is there a way to do this with modules? I have a case where I want

module.y 

to call a function, rather than just returning the value stored there.


Asked by: Roman729 | Posted: 06-12-2021






Answer 1

As PEP 562 has been implemented in Python >= 3.7, now we can do this

file: module.py

def __getattr__(name):
    if name == 'y':
        return 3
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

other = 4

usage:

>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "module.py", line 4, in __getattr__
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'

Note that if you omit the raise AttributeError in the __getattr__ function, it means the function ends with return None, then the module.nosuch will get a value of None.

Answered by: Elise571 | Posted: 07-01-2022



Answer 2

Only instances of new-style classes can have properties. You can make Python believe such an instance is a module by stashing it in sys.modules[thename] = theinstance. So, for example, your m.py module file could be:

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()

Answered by: Lucas160 | Posted: 07-01-2022



Answer 3

I would do this in order to properly inherit all the attributes of a module, and be correctly identified by isinstance()

import types

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


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

And then you can insert this into sys.modules:

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

Answered by: Ryan105 | Posted: 07-01-2022



Answer 4

Based on John Lin's answer:

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}'")

    old_getattr = getattr(module, '__getattr__', base_getattr)

    def new_getattr(name):
        if f'_{name}' == func.__name__:
            return func()
        else:
            return old_getattr(name)

    module.__getattr__ = new_getattr
    return func

Usage (note the leading underscore), in the_module.py:

@module_property
def _thing():
    return 'hello'

Then:

import the_module

print(the_module.thing)  # prints 'hello'

The leading underscore is necessary to differentiate the property-ized function from the original function. I couldn't think of a way to reassign the identifier, since during the time of the decorator execution, it has not been assigned yet.

Note that IDEs won't know that the property exists and will show red wavies.

Answered by: Chloe993 | Posted: 07-01-2022



Answer 5

Update Python 3

In Python 3, since 3.7 at least, the class of modules can be changed to a sub class, so real module properties (or descriptors) are now easy to implement - more solid and powerful than a PEP 562 module __getattr__ .

# mymodule.py

class ThisMod(sys.modules[__name__].__class__):
    y = property(lambda self: "Hi this is module %s." % __name__)
    const = property(lambda self: _const)  # block setting
sys.modules[__name__].__class__ = ThisMod

_const = 77

# rest of module code ...

Python 2 compatible

A typical use case is: enriching a (huge) existing module with some (few) dynamic attributes - without turning all module stuff into a class layout. Unfortunately a most simple module class patch like sys.modules[__name__].__class__ = MyPropertyModule fails with TypeError: __class__ assignment: only for heap types. So module creation needs to be rewired.

This approach does it without Python import hooks, just by having some prolog on top of the module code:

# 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)

Usage:

>>> 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: Something like from propertymodule import dynval will produce a frozen copy of course - corresponding to dynval = someobject.dynval

Answered by: Wilson898 | Posted: 07-01-2022



Answer 6

A short answer: use proxy_tools

The proxy_tools package attempts to provide @module_property functionality.

It installs with

pip install proxy_tools

Using a slight modification of @Marein's example, in the_module.py we put

from proxy_tools import module_property

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

Now from another script, I can do

import the_module

print(the_module.thing)
# . hello

Unexpected behavior

This solution is not without caveats. Namely, the_module.thing is not a string! It is a proxy_tools.Proxy object whose special methods have been overridden so that it mimicks a string. Here are some basic tests which illustrate the 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']

Internally, the original function is stored to the_module.thing._Proxy__local:

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

Further thoughts

Honestly, I'm baffled about why modules don't have this functionality built in. I think the crux of the matter is that the_module is an instance of the types.ModuleType class. Setting a "module property" amounts to setting a property on an instance of this class, rather than on the types.ModuleType class itself. For more details, see this answer.

We can actually implement properties on types.ModuleType as follows, although the results are not great. We can't directly modify built-in types, but we can curse them:

# 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__}'))

This gives us a property which exists over all modules. It's a bit unwieldly, since we break the setting behavior across all modules:

import sys

print(sys.thing2)
# hi from sys

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

Answered by: Arthur326 | Posted: 07-01-2022



Answer 7

based on user2124834's answer:

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

Usage (note the leading underscore), in the_module.py:

@module_property
def _thing():
    return 'hello'

Then:

import the_module

print(the_module.thing)  # prints 'hello'

I use a dict instead of nested function in original solution. That may be more efficient when use the decorator many times in one module.

Answered by: Sarah764 | Posted: 07-01-2022



Similar questions

python - Can modules have properties?

This question already has answers here:


Properties in Python Modules

I have a module for handling basic geometry features like angles and regular polygons. The portion that's giving me issues is here: # geometry.py _angle_mode = 'd' @property def angle_mode(): return _angle_mode @angle_mode.setter def angle_mode(value): if value != 'd' or value != 'r': raise ValueError("ValueError: invalid mode: '{0}'".format(mode)) _angle_mode = value class angle: ...


How to copy all properties of an object to another object, in Python?

Is there a library method to copy all the properties between two (already present) instances of the same class, in Python? I mean, something like Apache Commons' PropertyUtilsBean.copyProperties()


php - Is it possible to implement properties in languages other than C#?

During a bout of C# and WPF recently, I got to like C#'s properties: public double length_inches { get { return length_metres * 39.0; } set { length_metres = value/39.0; } } Noticing, of course, that length_metres may change from being a field to a property, and the code need not care. WPF can also bind UI elements to object properties very happily. When I first learnt abou...


python - DBus interface properties

How do I get the list of available DBus interface properties? I am writing a script that would be tracking specific type of usb devices connections. A way to distinguish the connections to be tracked from all usb connections I guess is to check the properties of signals' interfaces DBus is sending on a usb connection. I'd like to get the list of all such properties to chose the relevant. My code is:


Does Python have properties?

So something like: vector3.Length that's in fact a function call that calculates the length of the vector, not a variable.


c++ - Python Properties & Swig

I am attempting to create python bindings for some C++ code using swig. I seem have run into a problem trying to create python properties from some accessor functions I have for methods like the following: class Player { public: void entity(Entity* entity); Entity* entity() const; }; I tried creating a property using the python property function but it seems that the wrapper classes sw...


python - Create properties using lambda getter and setter

I have something like this: class X(): def __init__(self): self.__name = None def _process_value(self, value): # do something pass def get_name(self): return self.__name def set_name(self, value): self.__name = self._process_value(value) name = property(get_name, set_name) Can I replace get_name and set_name...


Does using properties on an old-style python class cause problems

Pretty simple question. I've seen it mentioned in many places that using properties on an old-style class shouldn't work, but apparently Qt classes (through PyQt4) aren't new-style and there are properties on a few of them in the code I'm working with (and as far as I know the code isn't showing any sorts of problems) I did run across a pyqtProperty function, but I can't seem to find any documentation about it. W...


python - How to define properties in __init__

I whish to define properties in a class from a member function. Below is some test code showing how I would like this to work. However I don't get the expected behaviour. class Basket(object): def __init__(self): # add all the properties for p in self.PropNames(): setattr(self, p, property(lambda : p) ) def PropNames(self): # The names of all the properties return ['Apple', 'Pear...


python - Do properties work on Django model fields?

I think the best way to ask this question is with some code... can I do this: class MyModel(models.Model): foo = models.CharField(max_length = 20) bar = models.CharField(max_length = 20) def get_foo(self): if self.bar: return self.bar else: return self.foo def set_foo(self, input): self.foo = input foo = property(g...


winapi - How do I Access File Properties on Windows Vista with Python?

The question is as simple as in the title, how do I access windows file properties like date-modifed, and more specifically tags, with Python? For a program I'm doing, I need to get lists of all the tags on various files in a particular folder, and I'm not sure on how to do this. I have the win32 module, but I don't see what I need. Thanks guys, for the quick responses, however, the main stat I need from the files ...






Still can't find your answer? Check out these communities...



PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python



top