鱼C论坛

 找回密码
 立即注册
查看: 2176|回复: 1

[学习笔记] 【第040讲心得】【使用@property和使用__slot__】

[复制链接]
发表于 2018-12-26 10:05:37 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 heidern0612 于 2018-12-26 19:49 编辑

写心得的过程都是自我思考的过程,借鉴了很多论坛前辈和互联网大佬的经验,仓促间难免出错,如有错误,恳请指出,感激不尽。


网上扒了一大堆关于@property的文章,算是有点小感悟,趁热打铁写下来。

(此文主要借鉴了廖雪峰大神blog中关于@property一章节:戳我前进)。

以及CSDN里关于@property的一篇文章:戳我前进




主要写几个自己认知过程中的重点:


1、@property的作用:


   A、将类方法转换为只读属性;

   B、重新实现一个属性的setter和getter方法;


2、@property的简单使用介绍:

如果在一个类中要设置和获取一个成员变量的话,正常的写法应该是以下这种经典的写法。

  1. class Student(object):
  2.    
  3.     __slots__ = ('__name', '__age', '__score')
  4.    
  5.     def __init__(self, name, age, score = 0):
  6.         self.__name = name
  7.         self.__age = age
  8.         self.__score = score
  9.         
  10.    
  11.     def getscore(self):    # 获取score
  12.         return self.__score
  13.    
  14.    
  15.     def setscore(self, score):   # 设置score value
  16.         if not isinstance(score, int):
  17.             raise ValueError('Score必须是int类型!')
  18.         if score < 0 or score > 100:
  19.             raise ValueError('Score值必须在0=<score<=100')
  20.         self.__score = score
复制代码


在实际的应用过程中,如果要get/set score就得如下这种写法:

  1. stu = Student('Wuli', 28)
  2. stu.setscore(88)
  3. stu.getscore() = 88
复制代码


这样这样写本没有什么错, 但是鉴于在实际的码code的过程中,getfuncname/setfuncname实在是太普通了。

我们可能希望get/set一个值时有更简单的方法(如下),像设置成员变量一样去设置一个变量,又可以检查类型参数,如下:

  1. #设置成员变量
  2. stu.score = 100
  3. #获取成员变量值
  4. stu.score
  5. # 报错:
  6. AttributeError    Traceback (most recent call last)
  7. <ipython-input-9-fb8376649a7b> in <module>()
  8. ----> 1 stu.__score = 88
  9. AttributeError: 'Student' object has no attribute '__score'
复制代码


还真可以,因了python有@property 装饰器。我们对前面的代码稍稍做一些修改。

  1. class Student(object):
  2.    
  3.     __slots__ = ('__name', '__age', '__score')
  4.    
  5.     def __init__(self, name, age, score = 0):
  6.         self.__name = name
  7.         self.__age = age
  8.         self.__score = score
  9.         
  10.     # 获取score
  11.     @property
  12.     def score(self):
  13.         return self.__score
  14.    
  15.     # 设置score value
  16.     @score.setter
  17.     def score(self, score):
  18.         if not isinstance(score, int):
  19.             raise ValueError('Score必须是int类型!')
  20.         if score < 0 or score > 100:
  21.             raise ValueError('Score值必须在0=<score<=100')
  22.         self.__score = score
复制代码



注意一下第1段代码与第2段代码之间的差异, get/set的函数名都变成一样了。

但是上了分别多了一个@property/@score.setter.

@property 加了这个装饰器的funcname相当于getfuncname()

@score.setter 加了这个装饰器的funcname相当于setfuncname


自此,你就可以像耍成员变量一样的去耍它们了。

  1. In [15]: stu = Student('wuli2', 28)
  2. In [16]: stu.score = 88
  3. In [17]: print(stu.score)
  4. 88
复制代码



3、@property如何将类方法定义成只读?

如下,只定义getter方法,不定义setter方法就是一个只读属性:

  1. class Student(object):

  2.     @property
  3.     def birth(self):

  4.         return self._birth

  5.     @birth.setter
  6.     def birth(self, value):
  7.         self._birth = value

  8.     @property
  9.     def age(self):
  10.         return 2015 - self._birth
复制代码
  

这里的self.age实例化后属性就无法进行修改或设置。




                               
登录/注册后可看大图

                               
登录/注册后可看大图


上述关于介绍@property的代码中出现了:

  1. __slots__ = ('__name', '__age', '__score')
复制代码



这个__slot__是个什么意思呢?


slots的作用一:阻止在实例化类时为实例分配dict,节省内存空间。

python中新模式的class,即从object继承下来的类有一个变量是__slots__。

在默认情况下每个类都会有一个dict,通过__dict__访问,这个dict维护了这个实例的所有属性,

例如:

  1. class Base(object):
  2.     v = 1
  3.     def __init__(self):
  4.           pass

  5. b = Base()
  6. print (b.__dict__)

  7. b.x = 2
  8. print (b.__dict__)
复制代码


可见:实例的dict只保持实例的变量,对于类的属性是不保存的,类的属性包括变量和函数。

由于每次实例化一个类都要分配一个新的dict,因此存在空间的浪费,因此有了slots。

当定义了slots后,slots中定义的变量变成了类的描述符,类的实例只能拥有这些个变量,而不在有dict,因此也就不能在增加新的变量。

如果你实在不能理解上面一大坨,你可以简单的理解为:当类被大量实例化的时候,__slot__可以不为实例化分配字典,节省了内存空间。



slots的作用二:限制该class能添加的属性。

  1. class Student(object):
  2.       __slots__ = ('name', 'age')    # 用tuple定义允许绑定的属性名称
  3.       

  4. s= Student ()

  5. s.name = "John"
  6. s.age = 30
  7. s.score = 35
复制代码


如以上代码,如果不添加最后一句s.score = 35,代码是可以正常运行的。

但如果添加了最后一句,就会抛出个AttributeError的错误,提示你Student类没有score属性。



另外,需要注意的是,,__slots__定义的属性仅对当前类起作用,对继承的子类是不起作用的。

除非在子类中也定义__slots__,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__。


如下代码运行是没有错误的。

  1. class StudentA(object):
  2.       __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称


  3. class StudentB(StudentA):
  4.       __slots__ = ('score')
  5.   
  6.   
  7.       
  8. s= StudentB ()

  9. s.name = "海顿上校"
  10. s.age = 30
  11. s.score = 35
复制代码





本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2019-4-20 22:26:46 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-30 04:53

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表