容易消化(有例子)解释类中的__get__ & __set__ & __call__,什么是所有者,实例?
在投入工作之前,要记住以下几点:
__get__ __set__ are called descriptors of the class to work/save their internal attributes namely: __name__ (name of class/owner class), variables - __dict__ etc. I will explain what is an owner later
Descriptors are used in design patterers more commonly, for example, with decorators (to abstract things out). You can consider it's more often used in software architecture design to make things less redundant and more readable (seems ironical). Thus abiding SOLID and DRY principles.
If you are not designing software that should abide by SOLID and DRY principles, you probably don't need them, but it's always wise to understand them.
1. 考虑下面的代码:
class Method:
def __init__(self, name):
self.name = name
def __call__(self, instance, arg1, arg2):
print(f"{self.name}: {instance} called with {arg1} and {arg2}")
class MyClass:
method = Method("Internal call")
instance = MyClass()
instance.method("first", "second")
# Prints:TypeError: __call__() missing 1 required positional argument: 'arg2'
当实例。方法("first", "second")被调用,__call__方法从method类被调用(调用方法使类对象像函数一样可调用-每当类实例被调用__call__时被初始化),并分配以下参数:instance: "first", arg1: "second",最后一个arg2被忽略,这将打印错误:
2. 如何解决?
由于__call__将instance作为第一个参数(instance, arg1, arg2),但是什么实例?
实例是调用描述符类(Method)的主类(MyClass)的实例。instance = MyClass()是实例那么谁是所有者呢?然而,在我们的描述符类(method)中没有方法将它识别为实例。这就是我们需要__get__方法的地方。再次考虑下面的代码:
from types import MethodType
class Method:
def __init__(self, name):
self.name = name
def __call__(self, instance, arg1, arg2):
print(f"{self.name}: {instance} called with {arg1} and {arg2}")
def __set__(self, instance, value):
self.value = value
instance.__dict__["method"] = value
def __get__(self, instance, owner):
if instance is None:
return self
print (instance, owner)
return MethodType(self, instance)
class MyClass:
method = Method("Internal call")
instance = MyClass()
instance.method("first", "second")
# Prints: Internal call: <__main__.MyClass object at 0x7fb7dd989690> called with first and second
根据文档,先忘掉set吧:
__get__“调用来获取所有者类的属性(类属性访问)或该类的实例的属性(实例属性访问)。”
如果你这样做:
打印:< __main__。MyClass对象0x7fb7dd9eab90> <class '__main__。MyClass的>
这意味着instance: MyClass的对象,即instance
Owner是MyClass本身
3.__set__解释:
__set__用于设置类__dict__对象中的某个值(假设使用命令行)。用于设置set的内部值的命令是:instance.descriptor = 'value' #,在这种情况下,descriptor是method
(实例。__dict__["method"] = value在代码中只是更新描述符的__dict__对象)
所以请执行:instance。方法= 'value'现在要检查在__set__方法中是否设置了value = 'value',我们可以访问descriptor方法的__dict__对象。
做的事:
instance.method。__dict__打印:{“_name”:“内部电话”,“价值”:“价值”}
或者你可以使用vars(instance.method)检查__dict__值
打印:{'name': '内部调用','value': 'value'}
我希望事情现在都清楚了:)