Python中Callable对象缺失__module__属性的场景有哪些?
__module__ Great question! Your decorator works right now because most common callables (like regular functions, methods, lambdas) come with a __module__ attribute by default. But PyCharm's static analysis is flagging a valid edge case—not every object that implements __call__ is guaranteed to have __module__. Let's break down the scenarios where this might happen:
Custom callable class instances (without explicit
__module__)
When you define a class with a__call__method, the class itself gets a__module__attribute automatically. But if you dynamically create a class (e.g., withtype()) without specifying the__module__parameter, or manually delete the class's__module__attribute, its instances will lack this property. For example:# Dynamically create a callable class, then remove its __module__ DynamicCallable = type('DynamicCallable', (), {'__call__': lambda self: "hello"}) del DynamicCallable.__module__ obj = DynamicCallable() obj.__module__ # Raises AttributeError!Callable objects with manually deleted
__module__
It's rare, but someone could intentionally strip the__module__attribute from a function or callable. For example:def my_function(): return 42 del my_function.__module__ # This callable now has no __module__ attributeEdge cases in dynamic code execution
If you create a callable usingexecorevalin a stripped-down namespace that doesn't populate module metadata, the resulting callable might not have__module__. While this is uncommon in production code, it's a valid scenario static analysis tools account for.Third-party or C-extension callables
A small number of third-party libraries (especially those wrapping C code) might implement callable objects that don't expose a__module__attribute. Most well-maintained libraries will include this, but it's not a strict requirement for all callables.
How to Make Your Decorator Robust
To handle these edge cases safely, use getattr() to retrieve __module__ with a fallback value:
import logging from typing import Callable from functools import wraps def function_logging(fn: Callable) -> Callable: # Fallback to '__main__' if __module__ is missing fn_module = getattr(fn, '__module__', '__main__') fn_logger = logging.getLogger(f"{fn_module}.{fn.__name__}") @wraps(fn) def wrapper(*args, **kwargs): fn_logger.info("Args: {}".format(args)) fn_logger.info("Kwargs: {}".format(kwargs)) result = fn(*args, **kwargs) fn_logger.info("Return: {}".format(result)) return result return wrapper
This ensures your logger still gets a valid name even when __module__ isn't present.
内容的提问来源于stack exchange,提问作者Harald Husum

