admin管理员组

文章数量:1414924

I have setup a custom __getattr__() method on a module which works perfectly when called from outside the module. However, it doesn't work from within the module itself (since that uses the globals() dict which doesn't redirect to __getattr__ on failure). How would I work around that?

I was thinking about encapsulating the globals() into a custom dict where I can then modify the __getitem__ method, but that seems very dirty and error prone. Is there another way to do that?

MWE:

def __getattr__(name):
    f'custom {name}'

print(a)  # should print 'custom a', but raises NameError

I have setup a custom __getattr__() method on a module which works perfectly when called from outside the module. However, it doesn't work from within the module itself (since that uses the globals() dict which doesn't redirect to __getattr__ on failure). How would I work around that?

I was thinking about encapsulating the globals() into a custom dict where I can then modify the __getitem__ method, but that seems very dirty and error prone. Is there another way to do that?

MWE:

def __getattr__(name):
    f'custom {name}'

print(a)  # should print 'custom a', but raises NameError
Share Improve this question asked Feb 23 at 11:55 XoriunXoriun 16810 bronze badges 7
  • Where is the definition of a? – OldBoy Commented Feb 23 at 12:00
  • 2 You can just make the module import itself. Like, if the module is named foo, write import foo; print(foo.a). (Feel free to turn this into an answer, I don't want to earn any more rep.) – Aran-Fey Commented Feb 23 at 12:38
  • 2 a does not get an attribute. __getattr__() is use if you retrieve an attribute of an object via the . operator. – Klaus D. Commented Feb 23 at 12:53
  • @Aran-Fey I want to avoid going through all my code and adding foo. everywhere, do you see a way around that? – Xoriun Commented Feb 23 at 13:27
  • 2 There are no hooks for ordinary name lookup: it's a fundamental operation in Python. – chepner Commented Feb 23 at 21:28
 |  Show 2 more comments

1 Answer 1

Reset to default 1

When a name is looked up in the global namespace, it is first looked up in the the globals() dict, and then the __builtins__ dict if that fails, so to customize the behavior of a missing name, you really want to customize the __builtins__ dict rather than the globals() dict.

But the __builtins__ dict of the current running frame can't be reassigned, so one workaround would be to re-execute the current frame's code object after customizing __builtins__ with a dict with a __missing__ method defined to delegate access to the module's __getattr__ function.

To avoid triggering the __builtins__ customization logics upon re-execution, you can check if __builtins__ is of the custom dict class before deciding whether to run the original intended code instead.

This approach works both as a main program and as an imported module:

if type(__builtins__).__name__ == '_DefaultDict':
    # your original code here
    def __getattr__(name):
        return f'custom {name}'

    print(a)
else:
    import sys
    import builtins

    class _DefaultDict(dict):
        def __missing__(self, name):
            return __getattr__(name)

    __builtins__ = _DefaultDict(vars(builtins))
    del _DefaultDict # avoid namespace pollution
    exec(sys._getframe(0).f_code, globals())

This outputs:

custom a

Demo: https://ideone/ThEpv6

本文标签: pythonuse getattr within the same module or consoleStack Overflow