1、前言

Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题苦恼的朋友提供一个思考问题的参考,由于个人能力有限,文中如有笔误、逻辑错误甚至概念性错误,还请提出并指正。本文所有测试代码使用Python 3.4版本

注:本文为自己整理和原创,如有转载,请注明出处。

2、什么是描述符

Python 2.2 引进了 Python 描述符,同时还引进了一些新的样式类,但是它们并没有得到广泛使用。Python 描述符是一种创建托管属性的方法。描述符具有诸多优点,诸如:保护属性不受修改、属性类型检查和自动更新某个依赖属性的值等。

说的通俗一点,从表现形式来看,一个类如果实现了__get__,__set__,__del__方法(三个方法不一定要全部都实现),并且该类的实例对象通常是另一个类的类属性,那么这个类就是一个描述符。__get__,__set__,__del__的具体声明如下:

__get__(self, instance, owner)
      __set__(self, instance, value)
      __delete__(self, instance)

其中:
      __get__ 用于访问属性。它返回属性的值,或者在所请求的属性不存在的情况下出现 AttributeError 异常。类似于javabean中的get。
      __set__ 将在属性分配操作中调用。不会返回任何内容。类似于javabean中的set。
      __delete__ 控制删除操作。不会返回内容。

注意:

只实现__get__方法的对象是非数据描述符,意味着在初始化之后它们只能被读取。而同时实现__get__和__set__的对象是数据描述符,意味着这种属性是可读写的。

3、为什么需要描述符

因为Python是一个动态类型解释性语言,不像C/C++等静态编译型语言,数据类型在编译时便可以进行验证,而Python中必须添加额外的类型检查逻辑代码才能做到这一点,这就是描述符的初衷。比如,有一个测试类Test,其具有一个类属性name。

  1. class Test(object):
  2. name = None

正常情况下,name的值(其实应该是对象,name是引用)都应该是字符串,但是因为Python是动态类型语言,即使执行Test.name = 3,解释器也不会有任何异常。当然可以想到解决办法,就是提供一个get,set方法来统一读写name,读写前添加安全验证逻辑。代码如下:

  1. class test(object):
  2. name = None
  3. @classmethod
  4. def get_name(cls):
  5. return cls.name
  6. @classmethod
  7. def set_name(cls,val):
  8. if isinstance(val,str):
  9. cls.name = val
  10. else:
  11. raise TypeError("Must be an string")

虽然以上代码勉强可以实现对属性赋值的类型检查,但是会导致类型定义的臃肿和逻辑的混乱。从OOP思想来看,只有属性自己最清楚自己的类型,而不是它所在的类,因此如果能将类型检查的逻辑根植于属性内部,那么就可以完美的解决这个问题,而描述符就是这样的利器。

为name属性定义一个(数据)描述符类,其实现了__get__和__set__方法,代码如下:

  1. class name_des(object):
  2. def __init__(self):
  3. self.__name = None
  4. def __get__(self, instance, owner):
  5. print('call __get__')
  6. return self.__name
  7. def __set__(self, instance, value):
  8. print('call __set__')
  9. if isinstance(value,str):
  10. self.__name = value
  11. else:
  12. raise TypeError("Must be an string")

测试类如下:

  1. class test(object):
  2. name = name_des()

测试代码及输出结果如下:

  1. >>> t = test()
  2. >>> t.name
  3. call __get__
  4. >>> t.name = 3
  5. call __set__
  6. Traceback (most recent call last):
  7. File "<pyshell#99>", line 1, in <module>
  8. t.name = 3
  9. File "<pyshell#94>", line 12, in __set__
  10. raise TypeError("Must be an string")
  11. TypeError: Must be an string
  12. >>> t.name = 'my name is chenyang'
  13. call __set__
  14. >>> t.name
  15. call __get__
  16. 'my name is chenyang'
  17. >>>

从打印的输出信息可以看到,当使用实例访问name属性(即执行t.name)时,便会调用描述符的__get__方法(注意__get__中添加的打印语句)。当使用实例对name属性进行赋值操作时(即t.name = 'my name is chenyang.'),从打印出的'call set'可以看到描述符的__set__方法被调用。熟悉Python的都知道,如果name是一个普通类属性(即不是数据描述符),那么执行t.name = 'my name is chenyang.'时,将动态产生一个实例属性,再次执行t.name读取属性时,此时读取的属性为实例属性,而不是之前的类属性(这涉及到一个属性查找优先级的问题,下文会提到)。

至此,可以发现描述符的作用和优势,以弥补Python动态类型的缺点。

4、属性查找的优先级  

当使用实例对象访问属性时,都会调用__getattribute__内建函数,__getattribute__查找属性的优先级如下:

1、类属性
2、数据描述符
3、实例属性
4、非数据描述符
5、__getattr__()

由于__getattribute__是实例查找属性的入口,因此有必要探究其实现过程,其逻辑伪代码(带注释说明)如下:

  1. __getattribute__伪代码:
  2. __getattribute__(property) logic:
  3. #先在类(包括父类、祖先类)的__dict__属性中查找描述符
  4. descripter = find first descripter in class and bases's dict(property)
  5. if descripter:#如果找到属性并且是数据描述符,就直接调用该数据描述符的__get__方法并将结果返回
  6. return descripter.__get__(instance, instance.__class__)
  7. else:#如果没有找到或者不是数据描述符,就去实例的__dict__属性中查找属性,如果找到了就直接返回这个属性值
  8. if value in instance.__dict__
  9. return value
  10. #程序执行到这里,说明没有数据描述符和实例属性,则在类(父类、祖先类)的__dict__属性中查找非数据描述符
  11. value = find first value in class and bases's dict(property)
  12. if value is a function:#如果找到了并且这个属性是一个函数,就返回绑定后的函数
  13. return bounded function(value)
  14. else:#否则就直接返回这个属性值
  15. return value
  16. #程序执行到这里说明没有找到该属性,引发异常,__getattr__函数会被调用
  17. raise AttributeNotFundedException

同样的,当对属性进行赋值操作的时候,内建函数__setattr__也会被调用,其伪代码如下:

  1. __setattr__伪代码:
  2. __setattr__(property, value)logic:
  3. #先在类(包括父类、祖先类)的__dict__属性中查找描述符
  4. descripter = find first descripter in class and bases's dict(property)
  5. if descripter:#如果找到了且是数据描述符,就调用描述符的__set__方法
  6. descripter.__set__(instance, value)
  7. else:#否则就是给实例属性赋值
  8. instance.__dict__[property] = value

记住__getattribute__查找属性的优先级顺序,并且理解__getattribute__、__setattr__的实现逻辑(还包括__getattr__的调用时机)后,就可以很容易搞懂为什么有些类属性无法被实例属性覆盖(隐藏)、通过实例访问一个属性的时候到底访问的是类属性还是实例属性,为此,我专门写了一个综合测试实例,代码见本文最后。

5、装饰器

如果想在不修改源代码的基础上扩充现有函数和类的功能,装饰器是一个不错的选择(类还可以通过派生的方式),下面分别介绍函数和类的装饰器。

函数装饰器:

假设有如下函数:

  1. class myfun():
  2. print('myfun called.')

如果想在不修改myfun函数源码的前提下,使之调用前后打印'before called'和'after called',则可以定义一个简单的函数装饰器,如下:

  1. def myecho(fun):
  2. def return_fun():
  3. print('before called.')
  4. fun()
  5. print('after called.')
  6. return return_fun

使用装饰器对myfun函数就行功能增强:

  1. @myecho
  2. def myfun():
  3. print('myfun called.')

调用myfun(执行myfun()相当于myecho(fun)()),得到如下输出:

  1. before called.
  2. myfun called.
  3. after called.

装饰器可以带参数,比如定义一个日志功能的装饰器,代码如下:

  1. def log(header,footer):#相当于在无参装饰器外套一层参数
  2. def log_to_return(fun):#这里接受被装饰的函数
  3. def return_fun(*args,**kargs):
  4. print(header)
  5. fun(*args,**kargs)
  6. print(footer)
  7. return return_fun
  8. return log_to_return

使用有参函数装饰器对say函数进行功能增强:

  1. @log('日志输出开始','结束日志输出')
  2. def say(message):
  3. print(message)

执行say('my name is chenyang.'),输出结果如下:

  1. 日志输出开始
  2. my name is chenyang.
  3. 结束日志输出

类装饰器:

类装饰器和函数装饰器原理相似,带参数的类装饰器示例代码如下:

  1. def drinkable(message):
  2. def drinkable_to_return(cls):
  3. def drink(self):
  4. print('i can drink',message)
  5. cls.drink = drink #类属性也可以动态修改
  6. return cls
  7. return drinkable_to_return

测试类:

  1. @drinkable('water')
  2. class test(object):
  3. pass

执行测试:

  1. t = test()
  2. t.drink()

结果如下:

  1. i can drink water

6、自定义staticmethod和classmethod

一旦了解了描述符和装饰器的基本知识,自定义staticmethod和classmethod就变得非常容易,以下提供参考代码:

  1. #定义一个非数据描述符
  2. class myStaticObject(object):
  3. def __init__(self,fun):
  4. self.fun = fun
  5. def __get__(self,instance,owner):
  6. print('call myStaticObject __get__')
  7. return self.fun
  8. #无参的函数装饰器,返回的是非数据描述符对象
  9. def my_static_method(fun):
  10. return myStaticObject(fun)
  11. #定义一个非数据描述符
  12. class myClassObject(object):
  13. def __init__(self,fun):
  14. self.fun = fun
  15. def __get__(self,instance,owner):
  16. print('call myClassObject __get__')
  17. def class_method(*args,**kargs):
  18. return self.fun(owner,*args,**kargs)
  19. return class_method
  20. #无参的函数装饰器,返回的是非数据描述符对象
  21. def my_class_method(fun):
  22. return myClassObject(fun)

测试类如下:

  1. class test(object):
  2. @my_static_method
  3. def my_static_fun():
  4. print('my_static_fun')
  5. @my_class_method
  6. def my_class_fun(cls):
  7. print('my_class_fun')

测试代码:

  1. >>> test.my_static_fun()
  2. call myStaticObject __get__
  3. my_static_fun
  4. >>> test.my_class_fun()
  5. call myClassObject __get__
  6. my_class_fun
  7. >>>

7、property

本文前面提到过使用定义类的方式使用描述符,但是如果每次为了一个属性都单独定义一个类,有时将变得得不偿失。为此,python提供了一个轻量级的数据描述符协议函数Property(),其使用装饰器的模式,可以将类方法当成属性来访问。它的标准定义是:

      property(fget=None,fset=None,fdel=None,doc=None) 

前面3个参数都是未绑定的方法,所以它们事实上可以是任意的类成员函数,分别对应于数据描述符的中的__get__,__set__,__del__方法,所以它们之间会有一个内部的与数据描述符的映射。

    property有两种使用方式,一种是函数模式,一种是装饰器模式。

函数模式代码如下:

  1. class test(object):
  2. def __init__(self):
  3. self._x = None
  4. def getx(self):
  5. print("get x")
  6. return self._x
  7. def setx(self, value):
  8. print("set x")
  9. self._x = value
  10. def delx(self):
  11. print("del x")
  12. del self._x
  13. x = property(getx, setx, delx, "I'm the 'x' property.")

如果要使用property函数,首先定义class的时候必须是object的子类(新式类)。通过property的定义,当获取成员x的值时,就会调用getx函数,当给成员x赋值时,就会调用setx函数,当删除x时,就会调用delx函数。使用属性的好处就是因为在调用函数,可以做一些检查。如果没有严格的要求,直接使用实例属性可能更方便。

此处省略测试代码。

装饰器模式代码如下:

  1. class test(object):
  2.  def __init__(self):
  3.   self.__x=None
  4.  
  5.  @property
  6.  def x(self):
  7.   return self.__x
  8.  @x.setter
  9.  def x(self,value):
  10.   self.__x=value
  11.  @x.deleter
  12.  def x(self):
  13.   del self.__x

注意:三个函数的名字(也就是将来要访问的属性名)必须一致。

使用property可以非常容易的实现属性的读写控制,如果想要属性只读,则只需要提供getter方法,如下:

  1. 1 class test(object):
  2. 2  def __init__(self):
  3. 3   self.__x=None
  4. 4  
  5. 5  @property
  6. 6  def x(self):
  7. 7   return self.__x

前文说过,只实现get函数的描述符是非数据描述符,根据属性查找的优先级,非属性优先级是可以被实例属性覆盖(隐藏)的,但是执行如下代码:

  1. >>> t=test()
  2. >>> t.x
  3. >>> t.x = 3
  4. Traceback (most recent call last):
  5. File "<pyshell#39>", line 1, in <module>
  6. t.x = 3
  7. AttributeError: can't set attribute

从错误信息中可以看出,执行t.x=3的时候并不是动态产生一个实例属性,也就是说x并不是非数据描述符,那么原因是什么呢?其实原因就在property,虽然表面上看属性x只设置了get方法,但是其实property是一个同时实现了__get__,__set__,__del__方法的类(是一个数据描述符),因此,使用property生成的属性其实是一个数据描述符!

      使用python模拟的property代码如下,可以看到,上面的“AttributeError: can't set attribute”异常其实是在property中的__set__函数中引发的,因为用户没有设置fset(为None):

  1. class Property(object):
  2. "Emulate PyProperty_Type() in Objects/descrobject.c"
  3.  
  4. def __init__(self, fget=None, fset=None, fdel=None, doc=None):
  5. self.fget = fget
  6. self.fset = fset
  7. self.fdel = fdel
  8. if doc is None and fget is not None:
  9. doc = fget.__doc__
  10. self.__doc__ = doc
  11.  
  12. def __get__(self, obj, objtype=None):
  13. if obj is None:
  14. return self
  15. if self.fget is None:
  16. raise AttributeError("unreadable attribute")
  17. return self.fget(obj)
  18.  
  19. def __set__(self, obj, value):
  20. if self.fset is None:
  21. raise AttributeError("can't set attribute")
  22. self.fset(obj, value)
  23.  
  24. def __delete__(self, obj):
  25. if self.fdel is None:
  26. raise AttributeError("can't delete attribute")
  27. self.fdel(obj)
  28.  
  29. def getter(self, fget):
  30. return type(self)(fget, self.fset, self.fdel, self.__doc__)
  31. def setter(self, fset):
  32. return type(self)(self.fget, fset, self.fdel, self.__doc__)
  33. def deleter(self, fdel):
  34. return type(self)(self.fget, self.fset, fdel, self.__doc__)

7、综合测试实例

      以下测试代码,结合了前文的知识点和测试代码,集中测试了描述符、property、装饰器等。并且重写了内建函数__getattribute__、__setattr__、__getattr__,增加了打印语句用以测试这些内建函数的调用时机。每一条测试结构都在相应的测试语句下用多行注释括起来。

  1. #带参数函数装饰器
  2. def log(header,footer):#相当于在无参装饰器外套一层参数
  3. def log_to_return(fun):#这里接受被装饰的函数
  4. def return_fun(*args,**kargs):
  5. print(header)
  6. fun(*args,**kargs)
  7. print(footer)
  8. return return_fun
  9. return log_to_return
  10.  
  11. #带参数类型装饰器
  12. def flyable(message):
  13. def flyable_to_return(cls):
  14. def fly(self):
  15. print(message)
  16. cls.fly = fly #类属性也可以动态修改
  17. return cls
  18. return flyable_to_return
  19.  
  20. #say(meaasge) ==> log(parms)(say)(message)
  21. @log('日志输出开始','结束日志输出')
  22. def say(message):
  23. print(message)
  24.  
  25. #定义一个非数据描述符
  26. class myStaticObject(object):
  27. def __init__(self,fun):
  28. self.fun = fun
  29. def __get__(self,instance,owner):
  30. print('call myStaticObject __get__')
  31. return self.fun
  32. #无参的函数装饰器,返回的是非数据描述符对象
  33. def my_static_method(fun):
  34. return myStaticObject(fun)
  35. #定义一个非数据描述符
  36. class myClassObject(object):
  37. def __init__(self,fun):
  38. self.fun = fun
  39. def __get__(self,instance,owner):
  40. print('call myClassObject __get__')
  41. def class_method(*args,**kargs):
  42. return self.fun(owner,*args,**kargs)
  43. return class_method
  44. #无参的函数装饰器,返回的是非数据描述符对象
  45. def my_class_method(fun):
  46. return myClassObject(fun)
  47.  
  48. #非数据描述符
  49. class des1(object):
  50. def __init__(self,name=None):
  51. self.__name = name
  52. def __get__(self,obj,typ=None):
  53. print('call des1.__get__')
  54. return self.__name
  55. #数据描述符
  56. class des2(object):
  57. def __init__(self,name=None):
  58. self.__name = name
  59. def __get__(self,obj,typ=None):
  60. print('call des2.__get__')
  61. return self.__name
  62. def __set__(self,obj,val):
  63. print('call des2.__set__,val is %s' % (val))
  64. self.__name = val
  65. #测试类
  66. @flyable("这是一个测试类")
  67. class test(object):
  68. def __init__(self,name='test',age=0,sex='man'):
  69. self.__name = name
  70. self.__age = age
  71. self.__sex = sex
  72. #---------------------覆盖默认的内建方法
  73. def __getattribute__(self, name):
  74. print("start call __getattribute__")
  75. return super(test, self).__getattribute__(name)
  76. def __setattr__(self, name, value):
  77. print("before __setattr__")
  78. super(test, self).__setattr__(name, value)
  79. print("after __setattr__")
  80. def __getattr__(self,attr):
  81. print("start call __getattr__")
  82. return attr
  83. #此处可以使用getattr()内建函数对包装对象进行授权
  84. def __str__(self):
  85. return str('name is %s,age is %d,sex is %s' % (self.__name,self.__age,self.__sex))
  86. __repr__ = __str__
  87. #-----------------------
  88. d1 = des1('chenyang') #非数据描述符,可以被实例属性覆盖
  89. d2 = des2('pengmingyao') #数据描述符,不能被实例属性覆盖
  90. def d3(self): #普通函数,为了验证函数(包括函数、静态/类方法)都是非数据描述符,可悲实例属性覆盖
  91. print('i am a function')
  92. #------------------------
  93. def get_name(self):
  94. print('call test.get_name')
  95. return self.__name
  96. def set_name(self,val):
  97. print('call test.set_name')
  98. self.__name = val
  99. name_proxy = property(get_name,set_name)#数据描述符,不能被实例属性覆盖,property本身就是一个描述符类
  100.  
  101. def get_age(self):
  102. print('call test.get_age')
  103. return self.__age
  104. age_proxy = property(get_age) #非数据描述符,但是也不能被实例属性覆盖
  105. #----------------------
  106. @property
  107. def sex_proxy(self):
  108. print("call get sex")
  109. return self.__sex
  110. @sex_proxy.setter #如果没有setter装饰,那么sex_proxy也是只读的,实例属性也无法覆盖,同property
  111. def sex_proxy(self,val):
  112. print("call set sex")
  113. self.__sex = val
  114. #---------------------
  115. @my_static_method #相当于my_static_fun = my_static_method(my_static_fun) 就是非数据描述符
  116. def my_static_fun():
  117. print('my_static_fun')
  118. @my_class_method
  119. def my_class_fun(cls):
  120. print('my_class_fun')
  121.  
  122. #主函数
  123. if __name__ == "__main__":
  124. say("函数装饰器测试")
  125. '''
  126. 日志输出开始
  127. 函数装饰器测试
  128. 结束日志输出
  129. '''
  130. t=test( ) #创建测试类的实例对象
  131. '''
  132. before __setattr__
  133. after __setattr__
  134. before __setattr__
  135. after __setattr__
  136. before __setattr__
  137. after __setattr__
  138. '''
  139. print(str(t)) #验证__str__内建函数
  140. '''
  141. start call __getattribute__
  142. start call __getattribute__
  143. start call __getattribute__
  144. name is test,age is 0,sex is man
  145. '''
  146. print(repr(t))#验证__repr__内建函数
  147. '''
  148. start call __getattribute__
  149. start call __getattribute__
  150. start call __getattribute__
  151. name is test,age is 0,sex is man
  152. '''
  153. t.fly() #验证类装饰器
  154. '''
  155. start call __getattribute__
  156. 这是一个测试类
  157. '''
  158. t.my_static_fun()#验证自定义静态方法
  159. '''
  160. start call __getattribute__
  161. call myStaticObject __get__
  162. my_static_fun
  163. '''
  164. t.my_class_fun()#验证自定义类方法
  165. '''
  166. start call __getattribute__
  167. call myClassObject __get__
  168. my_class_fun
  169. '''
  170. #以下为属性获取
  171. t.d1
  172. '''
  173. start call __getattribute__
  174. call des1.__get__
  175. '''
  176. t.d2
  177. '''
  178. start call __getattribute__
  179. call des2.__get__
  180. '''
  181. t.d3()
  182. '''
  183. start call __getattribute__
  184. i am a function
  185. '''
  186. t.name_proxy
  187. '''
  188. start call __getattribute__
  189. call test.get_name
  190. start call __getattribute__
  191. '''
  192. t.age_proxy
  193. '''
  194. start call __getattribute__
  195. call test.get_age
  196. start call __getattribute__
  197. '''
  198. t.sex_proxy
  199. '''
  200. start call __getattribute__
  201. call get sex
  202. start call __getattribute__
  203. '''
  204. t.xyz #测试访问不存在的属性,会调用__getattr__
  205. '''
  206. start call __getattribute__
  207. start call __getattr__
  208. '''
  209. #测试属性写
  210. t.d1 = 3 #由于类属性d1是非数据描述符,因此这里将动态产生实例属性d1
  211. '''
  212. before __setattr__
  213. after __setattr__
  214. '''
  215. t.d1 #由于实例属性的优先级比非数据描述符优先级高,因此此处访问的是实例属性
  216. '''
  217. start call __getattribute__
  218. '''
  219. t.d2 = 'modefied'
  220. '''
  221. before __setattr__
  222. call des2.__set__,val is modefied
  223. after __setattr__
  224. '''
  225. t.d2
  226. '''
  227. start call __getattribute__
  228. call des2.__get__
  229. '''
  230. t.d3 = 'not a function'
  231. '''
  232. before __setattr__
  233. after __setattr__
  234. '''
  235. t.d3 #因为函数是非数据描述符,因此被实例属性覆盖
  236. '''
  237. start call __getattribute__
  238. '''
  239. t.name_proxy = 'modified'
  240. '''
  241. before __setattr__
  242. call test.set_name
  243. before __setattr__
  244. after __setattr__
  245. after __setattr__
  246. '''
  247. t.sex_proxy = 'women'
  248. '''
  249. before __setattr__
  250. call set sex
  251. before __setattr__
  252. after __setattr__
  253. after __setattr__
  254. '''
  255. t.age_proxy = 3 #age_proxy是只读的
  256. '''
  257. before __setattr__
  258. Traceback (most recent call last):
  259. File "test.py", line 191, in <module>
  260. t.age_proxy = 3
  261. File "test.py", line 121, in __setattr__
  262. super(test, self).__setattr__(name, value)
  263. AttributeError: can't set attribute
  264. '''

python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解的更多相关文章

  1. Python 描述符(descriptor) 杂记

    转自:https://blog.tonyseek.com/post/notes-about-python-descriptor/ Python 引入的“描述符”(descriptor)语法特性真的很黄 ...

  2. python描述符descriptor(一)

    Python 描述符是一种创建托管属性的方法.每当一个属性被查询时,一个动作就会发生.这个动作默认是get,set或者delete.不过,有时候某个应用可能会有 更多的需求,需要你设计一些更复杂的动作 ...

  3. python描述符 descriptor

    descriptor 在python中,如果一个新式类定义了__get__, __set__, __delete__方法中的一个或者多个,那么称之为descriptor.descriptor通常用来改 ...

  4. python描述符和属性查找

    python描述符 定义 一般说来,描述符是一种访问对象属性时候的绑定行为,如果这个对象属性定义了__get__(),__set__(), and __delete__()一种或者几种,那么就称之为描 ...

  5. Python 描述符 (descriptor)

    1.什么是描述符? 描述符是Python新式类的关键点之一,它为对象属性提供强大的API,你可以认为描述符是表示对象属性的一个代理.当需要属性时,可根据你遇到的情况,通过描述符进行访问他(摘自Pyth ...

  6. python描述符descriptor(二)

    python内置的描述符 python有些内置的描述符对象,property.staticmethod.classmethod,python实现如下: class Property(object): ...

  7. Python描述符 (descriptor) 详解

    1.什么是描述符? python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问.这些方法有 __get__(), __set__(), 和__delete__().如 ...

  8. 学习python第十三天,函数5 装饰器decorator

    定义:装饰器本质是函数,(装饰其他函数)就是为其他函数添加附加功能原则:1.不能修改被装饰的函数的源代码 2.不能修改装饰的函数的调用方式 实现装饰器知识储备1函数即变量2.高阶函数,满足2个条件之一 ...

  9. Python学习第四十天函数的装饰器用法

    在软件开发的过程中,要遵循软件的一些原则封装的,不改变原有的代码的基础增加一些需求,python提供了装饰器来扩展函数功能,下面说说函数装饰器用法 def debug(func):      def ...

随机推荐

  1. PHP的openssl加密扩展使用小结

    h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...

  2. 多重共线性的解决方法之——岭回归与LASSO

          多元线性回归模型 的最小二乘估计结果为 如果存在较强的共线性,即 中各列向量之间存在较强的相关性,会导致的从而引起对角线上的 值很大 并且不一样的样本也会导致参数估计值变化非常大.即参数估 ...

  3. wordpress-4.4.1 数据库表结构解析

    wordpress-4.4.1.zip 安装包  SQL结构 : wp_commentmeta  :文章评论额外信息表. CREATE TABLE IF NOT EXISTS `wp_commentm ...

  4. STM32 KEIL不能输入仿真引脚端口error 65: access violation at 0x40021000 : no 'read' permission

    使用MDK自己创建一个STM32F103ZE核的项目 加入源码后编译,正常,在线仿真单步执行出现如下问题 error 65: access violation at 0x40021000 : no ' ...

  5. css3实现循环执行动画,且动画每次都有延迟

    一.最终效果 需求:gift图片的小动画每隔2s执行一次. 需求就一句话,我们看一下实现过程. 二.实现过程 1.网页结构 <!DOCTYPE html> <html lang=&q ...

  6. 洛谷P1962 斐波那契数列 || P1349 广义斐波那契数列[矩阵乘法]

    P1962 斐波那契数列 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数 ...

  7. ArrayList,Vector,LinkedList

    在java.util包中定义的类集框架其核心的组成接口有如下:·Collection接口:负责保存单值的最大父接口 |-List子接口:允许保存重复元素,数据的保存顺序就是数据的增加顺序: |-Set ...

  8. Struts2中的EasyUI

    Struts2中的EasyUI 一.easy UI是类似于jQuery UI的插件库,它提供了丰富的各种常用插件:tree.datagrid... tree插件: 语法:$(selector).tre ...

  9. flex align-content中的描述的“多根轴线的对齐方式”中的“多根轴线”到底是什么

    flex 有两条轴线,根据flex-flow 设置的来判断的,水平为主轴的话,那么值为row,垂直为主轴的话那么为column: 其中设置align-items 和 align-content都是来设 ...

  10. C# 窗体缩放的时候同步改变控件的大小和字体

    最新在写个小程序,需要窗体填满各种尺寸的显示器,同时需要同步缩放控件的大小.于是就写了个类,简单的调用一下即可解决问题. 这个类可以同步缩放控件的位置,宽度高度,字体大小. 使用的时候在FormLoa ...