Python 中的
__call__
和
__get__
是两个特殊的方法,它们分别在不同的场景下被调用。下面详细介绍它们的用法和代码示例:
1. __call__
方法:
__call__
方法在对象被当作函数调用时被调用。- 通过实现
__call__
方法,可以让类的实例像函数一样被调用。 - 这种技术被称为"可调用对象"(Callable Objects)。
示例代码:
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self, z):
return self.x * self.y * z
obj = MyClass(2, 3)
result = obj(4) # 调用 __call__ 方法, 结果为 24
在上面的例子中,当我们创建 MyClass
的实例 obj
时,__init__
方法被调用。当我们像函数一样调用 obj(4)
时,__call__
方法被调用,并返回 2 * 3 * 4 = 24
。
2. __get__
方法:
__get__
方法在访问属性时被调用。- 它用于实现描述符协议(Descriptor Protocol),描述符协议是 Python 用于实现动态属性访问的机制。
__get__
方法接收三个参数:实例对象、所属类对象和访问属性的名称。
示例代码:
class MyDescriptor:
def __init__(self, initial_value=None):
self.value = initial_value
def __get__(self, instance, owner):
print(f"Accessing attribute: {instance}, {owner}")
return self.value
def __set__(self, instance, value):
print(f"Setting attribute: {instance}, {value}")
self.value = value
class MyClass:
x = MyDescriptor(10)
y = MyDescriptor(20)
obj = MyClass()
print(obj.x) # 输出: Accessing attribute: <__main__.MyClass object at 0x7f6a0c1c8d60>, <class '__main__.MyClass'>
# 输出: 10
obj.y = 30
print(obj.y) # 输出: Accessing attribute: <__main__.MyClass object at 0x7f6a0c1c8d60>, <class '__main__.MyClass'>
# 输出: 30
__get__
方法中的 self
、instance
和 owner
参数的区别如下:
-
self
:self
指的是当前__get__
方法所属的描述符类的实例。- 在上一个例子中,
self
就是MyDescriptor
类的实例。
-
instance
:instance
指的是访问描述符属性的对象实例。- 也就是说,当我们访问
obj.x
时,instance
就是obj
。
-
owner
:owner
指的是描述符属性所属的类。- 在上一个例子中,当我们访问
obj.x
时,owner
就是MyClass
。
总结一下:
self
是描述符类自身的实例。instance
是访问描述符属性的对象实例。owner
是描述符属性所属的类。
这三个参数都是 __get__
方法的重要组成部分,用于描述描述符的使用场景。通过这三个参数,我们可以实现更加复杂和灵活的属性访问控制逻辑。
在上面的例子中,MyDescriptor
类实现了 __get__
和 __set__
方法,用于动态地访问和设置属性。当我们访问 obj.x
和 obj.y
时,__get__
方法被调用,并打印出相关信息。当我们设置 obj.y = 30
时,__set__
方法被调用,并打印出相关信息。
总结
总之,__call__
方法用于让类的实例像函数一样被调用,而 __get__
方法则是实现描述符协议,用于动态地访问属性。这两个方法为 Python 的面向对象编程提供了更加灵活和强大的功能。