利用描述符的原理,我们完全可以自定义模拟@classmethod、@staticmethod、@property等属性
模拟@classmethod
class TestClassmethod:
    """
    使用描述符模拟@classmethod
    """
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, owner):
        print('in classmethod __get__')
        # 对原函数进行加工,最后返回该函数
        def newfunc(*args, **kwargs):
            print('函数加工处理后,返回实例的类')
            return self.func(owner, *args, **kwargs)
        return newfunc
class Movie:
    title = '唐人街探案3'
    def movie(cls):
        print('电影名称是:{}'.format(cls.title))
    mymovie = TestClassmethod(movie)
    
    # 完全等价 mymovie = TestClassmethod(movie)
    @TestClassmethod
    def movie1(cls):
        print('电影名称是:{}'.format(cls.title))
print(Movie.mymovie())
print(Movie.movie1())
################################################
in classmethod __get__
函数加工处理后,返回实例的类
电影名称是:唐人街探案3
None
in classmethod __get__
函数加工处理后,返回实例的类
电影名称是:唐人街探案3
None模拟@staticmethod
staticmethod与classmethod不同的是,classmethod需要传入一个类的引用作为参数,为staticmethod不用
class TestStaticmethod:
    """
    使用描述符模拟@staticmethod
    """
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, owner):
        print('in staticmethod __get__')
        print('owner------------------', owner)
        # 对原函数进行加工,最后返回该函数
        def newfunc(*args, **kwargs):
            print('函数加工处理后,返回实例的类')
            return self.func( *args, **kwargs) # 此处不能加owner参数,静态方法么有自身对象self、也没有自身类cls
        return newfunc
class Movie:
    title = '唐人街探案3'
    def movie(cls):
        print('电影名称是:{}'.format(cls.title))
    mymovie = TestClassmethod(movie)
    @TestClassmethod
    def movie1(cls):
        print('电影名称是:{}'.format(cls.title))
    @TestStaticmethod
    def moive2(): # 此处pycharm会报错但是不会影响执行,静态方法不需要自身对象self、也不需要自身类cls参数
        print('最近没有电影上映------------------------')
print(Movie.moive2())
###################################################################
in staticmethod __get__
owner------------------ <class 'test_classmethod.Movie'>
函数加工处理后,返回实例的类
最近没有电影上映------------------------
None@Property
Python 内置的 property 函数可以说是最著名的描述符之一,几乎所有讲述描述符的文章都会拿它做例子。
property 是用 C 实现的,不过这里有一份等价的 Python 实现:
class TestProperty:
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        self.__doc__ = doc
    def __get__(self, obj, objtype=None):
        print('in __get__')
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError
        return self.fget(obj)
    def __set__(self, obj, value):
        print('in __set__')
        if self.fset is None:
            raise AttributeError
        self.fset(obj, value)
    def __delete__(self, obj):
        print('in __delete__')
        if self.fdel is None:
            raise AttributeError
        self.fdel(obj)
    def getter(self, fget):
        print('in getter')
        return type(self)(fget, self.fset, self.fdel, self.__doc__)
    def setter(self, fset):
        print('in setter')
        return type(self)(self.fget, fset, self.fdel, self.__doc__)
    def deleter(self, fdel):
        print('in deleter')
        return type(self)(self.fget, self.fset, fdel, self.__doc__)
class Student:
    def __init__(self, name, math):
        self.name = name
        self.math = math
    @TestProperty
    def math(self):
        return self._math
    @math.setter
    def math(self, value):
        if 0 <= value <= 100:
            self._math = value
        else:
            raise ValueError('value must be in [0,100]')
s = Student('zhangsan', 100)
print(s.math)函数 math 作为位置参数被赋给 Property.__init__() 的 fget,得到新的 math 已经不是个函数而是个完整实现了 __get__() 方法的描述器实例了。
@math.setter 的用法略有不同。它实际上是利用上面定义的描述器实例 math 的 setter 方法,重新创建了新的实例。这时变量 math 再次被更新,指向了一个完整实现 __get__() 和 __set__() 方法的新描述器。传入 setter 方法的函数名必须是 math。
 与一般的属性访问不同,s.math 访问的已经不是简单的属性,而是相当于 math.__get__(s),可以调用各种复杂方法对属性作检查、包装 。






 
 


Comments | NOTHING