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'>
要点总结
- 方法是通过描述符协议自动绑定的函数
- bound method绑定实例,unbound method(Python 3)就是普通函数
- **描述符
__get__**根据访问方式返回不同对象 - **
@classmethod绑定类,@staticmethod**返回原函数 - **
super()**按MRO顺序查找下一个类的方法
📝 发现内容有误?点击此处直接编辑