Python装饰器内获取函数或类方法名称的实现方案
Solution for Getting Function/Class Name in Decorator
Got it, let's tackle this problem step by step. The core challenge is to reliably distinguish between standalone functions and class methods (including instance, class, and static methods) and extract the correct name for your version variable. Here's a robust implementation that handles all cases:
Full Decorator Code
import functools def allow_disable_in_tests(func): @functools.wraps(func) def wrapper(*args, **kwargs): def get_version(): # Case 1: Bound methods (already attached to an instance or class) if hasattr(func, '__self__'): # For class methods, __self__ is the class itself if isinstance(func.__self__, type): return func.__self__.__name__ # For instance methods, get the class from the instance return func.__self__.__class__.__name__ # Case 2: Unbound methods/static methods defined in a class elif '.' in func.__qualname__: parts = func.__qualname__.split('.') # Grab the direct class name (handles nested classes too) return parts[-2] if len(parts) >= 2 else func.__name__ # Case 3: Standalone function else: return func.__name__ version = get_version() need_to_switch_off_in_tests = cache.get('switch_off_in_tests', version=version) if settings.IM_IN_TEST_MODE and need_to_switch_off_in_tests: return None return func(*args, **kwargs) return wrapper
How It Works
Let's break down each case in the get_version helper:
- Bound Methods: When the decorated function is already bound to an instance (e.g.,
my_obj.instance_method) or class (e.g.,MyClass.class_method), it has a__self__attribute. We check if__self__is a class (for class methods) or an instance (for instance methods) to get the correct class name. - Unbound Class/Static Methods: When you apply the decorator directly to a method inside a class definition (before it's bound to an instance/class), the
__qualname__attribute holds the full path likeClassName.method_name(orOuterClass.InnerClass.methodfor nested classes). We split this string to grab the direct class name. - Standalone Functions: If none of the above conditions are met, we're dealing with a standalone function, so we just use its
__name__.
Test Cases
Here's how this works with different function types:
# Standalone function → version = "my_standalone_func" @allow_disable_in_tests def my_standalone_func(): return "Hello from standalone" class MyService: # Instance method → version = "MyService" @allow_disable_in_tests def process_data(self): return "Processing data" # Class method → version = "MyService" @classmethod @allow_disable_in_tests def cleanup(cls): return "Cleaning up" # Static method → version = "MyService" @staticmethod @allow_disable_in_tests def validate_input(data): return data is not None # Nested class example → version = "NestedService" class OuterService: class NestedService: @allow_disable_in_tests def run(self): return "Running nested service"
Notes
- This relies on
__qualname__, which was added in Python 3.3. If you need to support older versions, you'd need a fallback (though most modern codebases use 3.3+ now). - For nested classes, this grabs the direct containing class name (e.g.,
NestedServiceinstead ofOuterService.NestedService). If you need the full class path, replaceparts[-2]with'.'.join(parts[:-1]).
内容的提问来源于stack exchange,提问作者Aleksei Khatkevich
相关产品推荐
相关产品推荐

