全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-19 10 分钟 ✍️ juanwangdev

Python方法调用机制

Python方法调用基于描述符协议实现自动绑定,理解这一机制有助于深入掌握面向对象编程。

方法与函数的区别

Python
class Demo:
    def method(self):
        return "instance method"

    @classmethod
    def class_method(cls):
        return "class method"

    @staticmethod
    def static_method():
        return "static method"

d = Demo()

# 类型分析
print(f"类访问实例方法: {type(Demo.method)}")          # function
print(f"实例访问实例方法: {type(d.method)}")           # method (bound)
print(f"类访问类方法: {type(Demo.class_method)}")      # method (bound to class)
print(f"类访问静态方法: {type(Demo.static_method)}")   # function

Bound Method

Python
class Counter:
    def __init__(self, start=0):
        self.value = start

    def increment(self):
        self.value += 1
        return self.value

c = Counter(10)

# 方法绑定
bound_method = c.increment
print(f"类型: {type(bound_method)}")  # <class 'method'>

# bound method 存储了实例引用
print(f"自身: {bound_method.__self__}")       # <Counter object>
print(f"函数: {bound_method.__func__}")       # <function Counter.increment>

# 调用等价
bound_method()  # 等价于 Counter.increment(c)
Python
# 手动绑定
import types

def greet(self, name):
    return f"Hello, {name}! I'm {self.name}"

class Person:
    def __init__(self, name):
        self.name = name

p = Person("Alice")

# 手动创建bound method
bound_greet = types.MethodType(greet, p)
print(bound_greet("Bob"))  # Hello, Bob! I'm Alice

Unbound Method

Python
class Demo:
    def method(self):
        return "method called"

# Python 3 中,通过类访问实例方法得到的是普通函数
print(type(Demo.method))  # <class 'function'>

# 必须显式传入实例
d = Demo()
Demo.method(d)            # 正确
Demo.method()              # TypeError: missing required argument 'self'

注意:Python 2 中存在"unbound method"类型,Python 3 移除了这一概念,直接返回函数。

描述符协议

方法绑定通过描述符协议实现:

Python
class MethodDescriptor:
    "模拟方法描述符"

    def __init__(self, func):
        self.func = func

    def __get__(self, obj, owner):
        if obj is None:
            # 通过类访问:返回原函数
            return self.func
        else:
            # 通过实例访问:返回bound method
            return types.MethodType(self.func, obj)

class MyClass:
    @MethodDescriptor
    def my_method(self):
        return "custom descriptor method"

m = MyClass()
print(f"类访问: {type(MyClass.my_method)}")  # <class 'function'>
print(f"实例访问: {type(m.my_method)}")       # <class 'method'>
Python
# 函数对象本身就是描述符
def example():
    pass

print(hasattr(example, '__get__'))  # True

# 函数的 __get__ 方法实现绑定逻辑
class Test:
    def method(self):
        pass

t = Test()
bound = Test.method.__get__(t, Test)
print(type(bound))  # <class 'method'>

类方法与静态方法

Python
class Demo:
    count = 0

    def __init__(self):
        Demo.count += 1

    @classmethod
    def get_count(cls):
        return cls.count

    @staticmethod
    def utility():
        return "static utility"

# classmethod 描述符行为
print(type(Demo.get_count))      # <class 'method'> (bound to class)
print(type(Demo().get_count))    # <class 'method'> (bound to class)

# staticmethod 描述符行为
print(type(Demo.utility))        # <class 'function'>
print(type(Demo().utility))      # <class 'function'>
Python
# 手动实现 classmethod
class ClassMethodDescriptor:
    def __init__(self, func):
        self.func = func

    def __get__(self, obj, owner):
        if owner is None:
            owner = type(obj)
        return types.MethodType(self.func, owner)

class MyCounter:
    total = 0

    @ClassMethodDescriptor
    def increment(cls):
        cls.total += 1
        return cls.total

MyCounter.increment()  # 1
MyCounter.increment()  # 2

描述符调用链

Python
class TraceAccess:
    "追踪属性访问的描述符"

    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, owner):
        if obj is None:
            print(f"  通过类 {owner.__name__} 访问 {self.name}")
            return self
        print(f"  通过实例 {obj} 访问 {self.name}")
        return f"value of {self.name}"

    def __set__(self, obj, value):
        print(f"  设置 {obj}{self.name} = {value}")
        obj.__dict__[self.name] = value

class Demo:
    attr = TraceAccess()

print("类访问:")
Demo.attr

print("\n实例访问:")
d = Demo()
d.attr

print("\n设置属性:")
d.attr = "new value"

方法解析顺序

Python
class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):
    pass

d = D()
print(d.method())      # "B"
print(D.__mro__)       # (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

# 方法查找路径
for cls in D.__mro__:
    if 'method' in cls.__dict__:
        print(f"找到方法于: {cls.__name__}")
        break

super() 的工作原理

Python
class A:
    def method(self):
        return "A.method"

class B(A):
    def method(self):
        # super() 返回代理对象,按MRO查找下一个类
        return f"B.method -> {super().method()}"

class C(A):
    def method(self):
        return "C.method"

class D(B, C):
    def method(self):
        return f"D.method -> {super().method()}"

d = D()
print(d.method())  # D.method -> B.method -> A.method
# 注意:C.method 未被调用,因为 MRO 中 B 后面是 C,但 A 在 B 的 MRO 中

# super() 的本质
print(type(super(D, d)))  # <class 'super'>

要点总结

  1. 方法是通过描述符协议自动绑定的函数
  2. bound method绑定实例,unbound method(Python 3)就是普通函数
  3. **描述符__get__**根据访问方式返回不同对象
  4. **@classmethod绑定类,@staticmethod**返回原函数
  5. **super()**按MRO顺序查找下一个类的方法

📝 发现内容有误?点击此处直接编辑

← 上一篇 Python属性查找链
下一篇 → Python解释器启动流程
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库