自己实现python中的静态方法类方法以及属性装饰器

自己实现python中的静态方法类方法以及属性装饰器


"""
描述器练习,本练习是为了加固对python描述器的了解

下文中有些内容为了方便我直接写成了A,B类。
A代表装饰器类
B代表功能类

本文中实现了类的静态方法,类方法,已经属性装饰器。如下是各种方法的实例思路。

静态方法:
我们在调用静态方法时不论使用实例调用,还是类调用都没有注入效果。
1、fn = Staticmethod(fn),一个类的属性等于另一个类的实例,此模式会触发描述器。
2、在装饰类中定义变量变量fn
3、在__get__魔术方法中返回fn

类方法:
我们在调用类方法时不论使用实例调用,还是类调用都会将类作为第一参数注入。
1、fn = Classmethod(fn),一个类的属性等于另一个类的实例,此模式会触发描述器。
2、在装饰器类中定义变量收集fn
3、因为类方法是有注入效果的,所以我们返回的函数需要是fn(cls)这样的,但是fn的其他参数不变
4、这个场景我想到了偏函数,使用偏函数固定fn的第一参数为cls,cls可以通过__get__魔术方法的owner获取


属性装饰器:
属性装饰器是这几个例子中最难的一个,我试了好久也没思路,准备看下源码咋写的,然后就是pass,pass,pass。后来捋了一下思路准备搞出来了。
1、name = Property(name),name = name.setter(name),name = name.deleter(name),三个等价式先写出来。
2、描述器的触发动作是B类中的属性等于A实例,所以三个等价式对应函数的返回值都需要是A实例
3、理清楚这一点后,我们就需要整理下三个函数实现的效果
4、属性获取装饰器,只是返回查询的值,所以大概率我们只需要使用__get__方法就能实现。实现过程中我们需要注意实例调用还是类调用,区分下即可。
5、属性修改装饰器,这里个人觉得是利用到了零个python的特性,赋值及定义。Person类的属性name并未修改,通过等价式向装饰器类传入了一个叫name
    的方法,这个方法和属性获取装饰器中栓传入的函数同名,但是内容已经不一样了。这时候我们需要在装饰器类中定义一个变量接收这个函数,这里我觉得用到了
    闭包,原先那个name定义的函数已经消亡了,只是装饰器类中重新定义的变量记住了它。
6、使用B.x = xxx这种方式触发__set__魔术方法,在A类的set方法中使用保存的B类用于修改的方法,这个地方有点绕,简单点就是使用set魔术方法真正
    触发属性的重新赋值,这里所需的self(功能类实例)和value都可以获取,直接带入到函数中即可。
7、属性删除装饰器和属性修改装饰器的实现逻辑是一样的,如果不太懂就参考属性修改装饰器再看一遍。

备注:功能类中要求被属性装饰器装饰的属性要名称一样,我觉得这个应该是为了方便操作,即便是不写一样的名字,你记住那个是查的,那个是改的应该也行。

下面就直接上源码了。本人也在学习中,有不对的请指正,有不懂的朋友可以直接问我,大家相互交流。
"""

from functools import partial

class Staticmethod:

    def __init__(self, fn):
        # print('in init !!!!')
        # fn等于,这个方法就是普通的方法
        # print(1,fn)
        self.fn = fn

    def __get__(self, instance, owner):
        # print(2,self,instance,owner)
        # 根据下下文中的等价式show = Staticmethod(show) = ,
        # 不管类调还是实例调都恒等于这个值,没有注入效果。
        return self.fn

    def __repr__(self):
        return ''


class Classmethod:

    def __init__(self, fn):
        # 同上fn等于display方法,我们不做处理它就是普通方法,注入效果需要我们自己实现
        self.fn = fn

    def __get__(self, instance, owner):
        # print(self,instance,owner)
        # self.fn(cls,x) = display(cls,x),此处我们使用偏函数把cls固定下来,然后把固定好的方法返回
        return partial(self.fn, owner)

    def __repr__(self):
        return ''


class Property:

    def __init__(self, fn):
        self.fn = fn

    def __get__(self, instance, owner):
        # print(1,self, instance, owner)
        # property的调用方式是实例.属性直接返回结果,所以这里需要返回的结果是实例调用后的结果。fn是一个实例方法,需要传入self
        if instance:
            return self.fn(instance)
        return self

    def __set__(self, instance, value):
        # print(2,self,instance,value)
        self.setfn(instance,value)

    def Setter(self,setfn):
        # print(3,setfn)
        self.setfn = setfn
        return self

    def Deleter(self,delfn):
        # print(4,delfn)
        self.delfn = delfn
        return self

    def __delete__(self, instance):
        # print(5,self,instance)
        self.delfn(instance)

class Person:

    def __init__(self, name):
        self.__name = name

    # 先写出等价式show = Staticmethod(show),触发描述器。show等于Staticmethod的__get__方法返回值。这里的self就是一个参数,不带有注入效果
    @Staticmethod
    def show(self, x):
        print('show {} {}'.format(self, x))

    # 先写等价式display = Classmethod(display),触发描述器。dispaly等于classmethod的__get__方法返回值。
    @Classmethod
    def display(cls, x):
        print('display {} {}'.format(cls, x))

    '''先写等价式name = Property(name),触发描述器,name等于Property的__get__方法返回值。此时的name通过Person类和实例调用返回的值是不一样的,
    #常规逻辑下应该类因该返回类的方法,实例应该返回实例的绑定方法。但这个我们可以通过A类的get方法控制,类调用我们返回Property的一个实例,
    实例调用我们直接返回方法调用后的结果,效果等同于直接访问属性。
    '''
    @Property
    def name(self):
        return self.__name

    '''先写等价式name = name.setter(name),__get__,__set__魔术方法的触发条件都是判断B类中的属性等于A实例。所以name.setter(name)的返回值
    一定要是一个Property的实例才行。这一步的主要目的是把name对应的方法送到Property的类中,后续采用B.x = xxx的方式赋值时,就会触发A类的
    __set__方法,__set__方法再调用已经保存的B类方法进行赋值'''
    @name.Setter
    def name(self,value):
        self.__name = value

    #同Setter方法
    @name.Deleter
    def name(self):
        del self.__name

    def __repr__(self):
        return ''


Person.show(1, 2)
Person('laokoo').show(3, 4)
Person.display(1)
Person('laokoo').display(2)
laokoo = Person('laokoo')
print(Person.name)
print(laokoo.name)
print(laokoo.__dict__)
laokoo.name = 'jack'
print(laokoo.name)
print(laokoo.__dict__)
del laokoo.name
print(laokoo.__dict__)
自己实现python中的静态方法类方法以及属性装饰器

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章