You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Python实例方法添加代码块装饰器并访问/使用实例变量?

Let's break down what's going wrong here and fix it step by step.

First, the core issue: When you replace a.put with the wrap function returned by Decorate, you're swapping a bound method (the original a.put, which automatically carries self=a) with a plain function. When you call a.put('it is'), Python won't automatically pass a as the first argument to wrap—instead, the string 'it is' becomes that first parameter, which is why you see AttributeError: 'str' object has no attribute 'name': your self variable in the wrapper is actually the string you passed.

Solution 1: Fix the Decorator to Work with Bound Methods

The original a.put is a bound method, and its __self__ attribute directly references the instance a. We can use this to pass the instance to our before/after functions, and re-bind the wrapped function to the instance so it behaves like a normal method:

def Decorate(func, before=None, after=None):
    # Extract the instance from the original bound method
    instance = func.__self__
    
    def wrap(*args, **kwargs):
        if before:
            # Pass the instance to the before function
            before(instance)
        # Call the original bound method (no need to manually pass self)
        result = func(*args, **kwargs)
        if after:
            after(instance)
        return result
    
    # Re-bind the wrap function to the instance to maintain method behavior
    return wrap.__get__(instance, type(instance))

class Test():
    def __init__(self, name):
        self.name = name
    def put(self, prefix):
        print(prefix, self.name)
    def some_method(self):
        print(f"Calling instance method from {self.name}")

# Define before/after functions that accept the instance
def Before(instance):
    print('before')
    print(f"Instance name: {instance.name}")
    instance.some_method()

def After(instance):
    print('after')

a = Test('me')
a.put = Decorate(a.put, Before, After)
a.put('it is')

Running this will output:

before
Instance name: me
Calling instance method from me
it is me
after

Solution 2: Make Before/After Feel Like Instance Methods

If you prefer before and after to use self directly (like regular methods), we can wrap them to automatically inject the instance:

def Decorate(func, before=None, after=None):
    instance = func.__self__
    
    # Wrap before/after to pass the instance as self
    def wrapped_before():
        if before:
            before(instance)
    def wrapped_after():
        if after:
            after(instance)
    
    def wrap(*args, **kwargs):
        wrapped_before()
        result = func(*args, **kwargs)
        wrapped_after()
        return result
    
    return wrap.__get__(instance, type(instance))

# Now before uses self just like an instance method
def Before(self):
    print('before')
    print(f"Instance name: {self.name}")
    self.some_method()

# Usage remains the same
a.put = Decorate(a.put, Before, After)

Why Your addMethod Works

Your addMethod uses method.__get__(self)—this is exactly how Python creates bound methods. The __get__ method of a function returns a bound method object that automatically passes the instance as the first (self) argument when called. We use the same logic in Solution 1 with wrap.__get__(instance, type(instance)).

Advanced: Robust Class-Based Decorator

For more flexibility, you can implement a decorator class that handles binding automatically via the descriptor protocol:

class InstanceMethodDecorator:
    def __init__(self, func, before=None, after=None):
        self.func = func
        self.before = before
        self.after = after

    def __call__(self, *args, **kwargs):
        instance = self.func.__self__
        if self.before:
            self.before(instance)
        result = self.func(*args, **kwargs)
        if self.after:
            self.after(instance)
        return result

    def __get__(self, instance, owner):
        # Return a decorator bound to the current instance when accessed
        if instance is None:
            return self
        bound_func = self.func.__get__(instance, owner)
        return InstanceMethodDecorator(bound_func, self.before, self.after)

def Decorate(func, before=None, after=None):
    return InstanceMethodDecorator(func, before, after)

This version works both for decorating individual instance methods (your use case) and for decorating methods at the class level, while keeping each instance's decoration independent.

内容的提问来源于stack exchange,提问作者Ulrich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 04:47:37