其实这个描述符我在工作中基本没杂用到过,今天看一段视频看到了,就随便把以前的记录整理了一下。
前面已经开了许多笔记,不知道再哪个笔记下面记录合适,那就新开一个主题进行记录。
Python的描述符,很多人可能觉的很高级,很多人可能觉的没用过。但可以很准确的告诉你,
在类中定义了函数就是一个非覆盖型的描述符,因为描述符的定义中,只要定义了__get__,或者__set__,或者__delete__就属于描述符。
你可以查看一下函数的属性当中,肯定携带有__get__属性,属于当他定义在类中就属于一个描述符。
再往绕的地方讲,函数本身也是一个类的实例,只不过这个类定义了__call__方法,描述符也史一个实例,只要定义了前面所说的三个魔法方法中的任意一个。
class Descriptor: # 这个比__init__好用,都是在初始化的时候执行 # 这个能获取到调用的类owner信息,已经类属性名name def __set_name__(self, owner, name): print(f'name: {owner.__name__}.{name}') self.name = f'__{name}__' def __get__(self, instance, owner): print(f'get: {instance}, {owner}') return getattr(instance, self.name, None) # setattr与.取值的效果一样,都会激活父类的__setattr__ def __set__(self, instance, value): print(f'set {instance}, {value}') setattr(instance, self.name, value) def __delete__(self, instance): print(f'del: {instance}') raise AttributeError('Delete is disabled')
这个是我从自己的另外一个帖子里面找到的关于描述符中参数的定义情况。
这里先说明里面参数的一些意义,instance为实例,也就是类中的self,owner也就是类本身。
就这个__get__里面有两个参数,当你通过实例或类调用该属性的时候,里面的参数返回是不同的,当类调用的时候,里面的instance是None
当实例调用的时候,里面就是实例,所以这里就完全可以通过__get__内的一些判断来自动传入self,也就是所谓的实例的方法
# 模拟函数内部的__get__逻辑 def __get__(self, instance, owner): if isinstacne: retnrn functools.partial(self, instance) else return self
从__set__与__del__的参数可以看出来当出现赋值或者输出属性的时候,会优先调用该对象,如果该描述符定义的话。
当一个描述符称为类的属性,确实发生了很多奇妙的化学反映
经过我的测试,当我们给实例通过.的方式进行属性查看或者设置的时候,如果这是一个描述符,也部影响__getattrbute__或者__getattr__,__setattr__的实例方法调用,只不过当调用返回的时候,他会判断该属性是否为描述符,也就是否在类中已经定义了该描述符,如果定义了该描述符会走继续走该描述符的__set__方法里面对属性进行操作,如果没有的话,会直接会实例的属性进行赋值,也就是对实例的属性空间__dict__添加属性的键值对。
上面的理解应该很透彻了,最后记录一些我对Python的类与模块的理解。
最近上下班路上听了老男孩培训机构的Python教学视频,参考那老师对Python类的说明以及自己对Python的理解。
确实OOP的设计思路很巧妙,而且每个实例的创建也非常的节约内容。其实一个实例,真正属于自己的属性真的很少,很少。
在我们自定义的实例中,一般就通过__init__来初始化一些实例的属性,其实那才是这个实例真正自己的,另外所有的都是从类中公用的。
昨天晚上大半夜的失眠就想这个问题,Python的类中用了太多的语法糖,其实完全大可不必这样,类其实就是一个命名空间,
实例也有一个自己的命名空间,但实例可以去使用类中的命名空间的内容。
那如果大量的实例进行创建,可以省下很多内存空间,因为共享的函数或者属性只要一份公用的就可以了。
所以按照这样的逻辑,你完全可以把一个模块也认为是一个类,当你使用模块内的函数或者变量的时候,也就像在使用类中的函数【方法】。
然后你可以通过字典或者列表建立一个自己想要的模型对象,放一些该对象特有的属性。
最后就说一句该培训结构老师说的很让我醍醐灌耳的一句话,其实实例也罢,类也罢,这些都是一些容器而已。
晚上回看了一下super以前转帖的内容,顺便直接调用了下函数的__get__方法,返回的method实例,其实是method类的实例。
具体的调用跟functools里面的partial很像。下面我上一些调试代码。
In [1]: from functools import partial In [2]: def show_me(self): ...: print(self) ...: In [3]: arg = 999 In [4]: my_method = show_me.__get__(arg) In [5]: my_method Out[5]: <bound method show_me of 999> In [6]: my_method_class = type(my_method) In [7]: my_method_class Out[7]: method In [8]: my_partial = partial(show_me, arg) In [9]: my_method() 999 In [10]: my_partial() 999 In [11]: my_method_class? Init signature: my_method_class(self, /, *args, **kwargs) Docstring: method(function, instance) Create a bound instance method object. Type: type Subclasses: In [12]: my_method2 = my_method_class(show_me, arg) In [13]: my_method2 Out[13]: <bound method show_me of 999> In [14]: my_method Out[14]: <bound method show_me of 999> In [15]: my_method is my_method2 Out[15]: False In [16]: my_method2() 999 In [17]: type(partial) Out[17]: type In [18]: type(my_method_class) Out[18]: type
感觉就是用了偏函数的一些原理实现的方法对象的绑定。