描述器 Descriptors


导论

老子曰:一生二,二生三,三生万物,佛家思想认为,世界万物相生相克,有上必有下,有苦必有甜。大家都在描述一个什么问题?世界生态的构成和对象之间的相互作用。
面向对象又是什么?面向对象是哲人们为实现机器语言编程的一种哲学思想,通过面向对象思想模拟世界万物的生态。
现实世界可以抽象为不同种类的组成,不同种类下又有一个个独立的个体对象组成,每个在现实生活中的个体不一定完全相同,但是每个个体在生态环境中各司其职。
计算机语言又是为解决人类需求而发明的工具,只要是人类发明,必然会继承人类千百年来自然选择适则生存下对世界万物的认知,因此面向对象就是通过不同的出发点模拟抽象这个世界,计算机语言中设计不同的种类和对象都是通过模拟世界的分类重构计算机生态下的现实世界,因此现实生活中的每一类就是计算机生态环境下的每一类,现实生活中的每一个对象就是计算机生态环境下的每一个对象。这就是为什么在编程时,一切皆对象,代码才美观的原因

外包公司即描述器

生活中的外包公司组成,公司即是企业类实例化的一个对象,外包公司的人又是人类中职业为程序员的群体中的某些对象。这些群体继承了外包公司的属性和程序员类的属性,所以这个群体中的每个对象就是多态继承下类的对象。
工作在外包公司众所周知:

  • 企业根据你会的方法和属性特点调度你时,你得按照企业的分配的任务即时完成工作,相当于提供python魔术中的__get__功能。可以理解只有当企业的名义调度你时,你得通过__get__功能对外提供你得能力,如企业下得某一为很随便的一个对象调度你时,不对外提供__get__服务,否则显得你太便宜,不值钱。
class A:
    def __init__(self):
        self.a1 = "a1"

    def __get__(self, instance, owner):
        print("A.__get__{}{}{}".format(self,instance,owner))

class B:
    x = A()

    def __init__(self):
        pass

print("-"*20)
print(B.x) #instance会显示为None,因为时类调用
b = B()
print(b.x)
print("-"*20)
运行结果:
--------------------
A.__get__<__main__.A object at 0x0000018AC1261710>None<class '__main__.B'>
None
A.__get__<__main__.A object at 0x0000018AC1261710><__main__.B object at 0x0000018AC1261F28><class '__main__.B'>
None
--------------------
只有类属性才会触发__get__魔术
  • 企业需求是需要一个会写python代码的工程师,因此从外包公司调了一位python工程师,但是企业在__init__初始化招聘时,再次招聘一个会写pyhon代码的工程师,此时外包公司的python工程师肯定不愿意,如果招聘进来的话,可是正编员工,自己的饭碗就不稳定了,因此外包工程师调用自己的__set__方法进行限制。
class A:
    def __init__(self):
        self.a1 = "a1"

    def __get__(self, instance, owner):
        print("A.__get__{}{}{}".format(self,instance,owner))
        return self

    def __set__(self, instance, value):
        print("A.__set__{}{}{}".format(self,instance,value))
        self.data = value

class B:
    x = A()
    #类B中已经存在x属性,实例化时实例中又有self.x属性,因此会触发
    #描述器中的__set__属性,因此就屏蔽调实例中的self.x属性,对外为类B中的B.x属性
    def __init__(self):
        self.x = "b.x"
        self.y = "b.y"

print("-"*20)
b = B()
print(b.x)

描述器概念

Python中,一个类实现了__get____set____delete__三个方法中的任何一个方法,就是描述器。
如果仅实现了__get__就是非数据描述符non-data descriptor
同时实现了__get____set__就是数据描述符data descriptor
如果一个类的类属性设置为描述器,那么它被称为owner属主。

描述器对dict的影响

描述器为数据描述器,有__set__属性

  • 当一个类用到了描述器,那么必然时类属性的某一值为描述器,但是实例再次设置同类一样的属性时,则触发描述器中set方法,类字典中加载此值,实例字典则不加载此值。
class A:
    def __init__(self):
        pass
    def __set__():
        pass
    def __get__():
        pass
class B:
    x = A()
    def __init__(self)
        self.x = "任意值" #和类属性冲突,触发描述器set
b = B()
b.x = "任意值" #同样也会触发描述器set。
总结:只要实例属性和类属性为描述器的属性字符相同时,则触发set函数

非数据描述器,仅有__get__属性

  • 当描述器为非数据描述器时,只有通过访问类属性时触发get。

Python中得描述器

描述器在python中应用非常广泛,Python的方法(包括staticmethod()和classmethod())都实现为非数据描述器。因此,实例可以重新定义和覆盖方法。这允许单个实例获取与同一类的其他实例不同的行为

@staticmethod描述器实现

系统中得staticmethond是如何实现的呢?

class StaticMethod:
    def __init__(self,fn):
        self.fn = fn
    def __get__(self, instance, owner):
        return self.fn

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

    @StaticMethod
    def pint():
        print("Test ok")
A.pint()

@classmethod描述器实现

系统中的classmethod实现过程

from functools import partial
class ClassMethod:
    def __init__(self,fn):
        self.fn = fn
    def __get__(self, instance, owner):
        return partial(self.fn,owner)

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

    @ClassMethod
    def pint(cls): #pint = ClassMEthod(pint)==partial(self.fn owner)
        print("Test ok")
A.pint()

描述器验证实例参数传入是否正确

入侵式代码描述器

class CheckData: #创建校验数据的合法性的类
    def __init__(self,key):
        self.key = key
    def __set__(self, instance, value):
        pass
    def __get__(self, instance, owner):
        pass

class A:
    name = CheckData("name") #一个实例验证一个事
    age  = CheckData("age")  #专项人才,一个干好一个事就OK了
    def __init__(self,name:str,age:int):
        self.name = name
        self.age = age

非入侵式代码描述器

class CheckData:
    def __init__(self,key):
        self.key = key
    def __set__(self, instance, value):
        pass
    def __get__(self, instance, owner):
        pass

class AddCheck:
    pass #动态添加name和age属性

@AddCheck
class A:
    def __init__(self,name:str,age:int):
        self.name = name
        self.age = age

猜你喜欢

转载自blog.csdn.net/xuexiaoyaani/article/details/80314116