利用描述符的原理,我们完全可以自定义模拟@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