property

property是一个装饰器函数,可以将一个方法伪装成属性,调用的时候可以不用加()。@property被装饰的方法,是不能传参数的,因为它伪装成属性了。

装饰器的使用:在要装饰的函数、方法、类上面一行加上 @装饰器名字

装饰器的分类:

  •   装饰函数
  •   装饰方法:property
  •   装饰类

例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:

过轻:低于18.5

正常:18.5-23.9

过重:24-27

肥胖:28-32

非常肥胖, 高于32

  体质指数(BMI)=体重(kg)÷身高^2(m)

  EX:70kg÷(1.75×1.75)=22.86

查看我的BMI

  1. class Person(object):
  2. def __init__(self,name,weight,height):
  3. self.name = name
  4. self.__weight = weight
  5. self.__height = height
  6.  
  7. @property
  8. def bmi(self):
  9. return self.__weight / self.__height **2
  10.  
  11. p = Person('xiao',65,1.75)
  12. print(p.bmi) # 21.224489795918366
  13. print(Person.__dict__)                   
  14. print(p.__dict__)                      # 在对象里面,并没有被当成属性
  15.  
  16. '''
  17. 执行输出:
  18. 21.224489795918366
  19. {'__module__': '__main__', '__init__': <function Person.__init__ at 0x000002221B1EC048>, 'bmi': <property object at 0x000002221B1E8C78>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
  20.  
  21. {'name': 'xiao', '_Person__weight': 65, '_Person__height': 1.75}
  22. '''

被property装饰的bmi仍然是一个方法,存在Person.__dict__。对象的.__dict__中不会存储这个属性.

下面的代码,也可以实现上面的效果

  1. class Person(object):
  2. def __init__(self,name,weight,height):
  3. self.name = name
  4. self.__weight = weight
  5. self.__height = height
  6. #self.bmi = self.__weight / self.__height **2
  7. #self.bmi = cal_BMI() 
  8.  
  9. # 但是计算的逻辑,不能放到init里面。初始化,不要做计算

举例:

  1. class Person(object):
  2. def __init__(self,name,weight,height):
  3. self.name = name
  4. self.__weight = weight
  5. self.__height = height
  6. self.bmi = self.__weight / self.__height **2
  7.  
  8. p = Person('xiao',65,1.75)
  9. print(p.bmi)
  10. p._Person__weight = 70 # 1周之后,增加体重
  11. print(p.bmi)
  12.  
  13. '''
  14. 执行输出:
  15. 21.224489795918366
  16. 21.224489795918366
  17. '''

执行结果是一样的,体重增加了,但是bmi指数没有变动。因为__init__初始化之后,就不会再变动了。

在__init__里面,属性名不能和方法名重复

  1. class Person(object):
  2. def __init__(self,name,weight,height,bmi):
  3. self.name = name
  4. self.__weight = weight
  5. self.__height = height
  6. self.bmi = bmi
  7.  
  8. def bmi(self):
  9. return self.__weight / self.__height **2
  10.  
  11. a = Person('xiao',65,1.75)
  12. print(a.bmi()) # TypeError: __init__() missing 1 required positional argument: 'bmi'

那么bmi是否可以修改呢?

  1. class Person(object):
  2. def __init__(self,name,weight,height):
  3. self.name = name
  4. self.__weight = weight
  5. self.__height = height
  6.  
  7. @property
  8. def bmi(self):
  9. return self.__weight / self.__height **2
  10.  
  11. p = Person('xiao',65,1.75)
  12. p.bmi = 2 # AttributeError: can't set attribute

但是它的name属性是可以改变的。

  1. class Person:
  2. def __init__(self,name):
  3. self.name = name
  4.  
  5. p = Person('Tony')
  6. print(p.name) # Tony
  7. p.name = 'John'
  8. p.name = 123

那么上面这2个例子,和直接定义name属性有什么区别?

  1. class Person:
  2. def __init__(self,name):
  3. self.__name = name # 私有的属性
  4.  
  5. @property
  6. def name(self):
  7. return self.__name
  8.  
  9. def set_name(self,new_name):
  10. if type(new_name) is str:
  11. self.__name = new_name
  12.  
  13. else: # 通过if判断,就可以保护属性的类型,必须是字符串
  14. print('您提供的姓名数据类型不合法')
  15.  
  16. p = Person('Tony')
  17. print(p.name)
  18. p.set_name('John')
  19. print(p.name)
  20. p.set_name(123)
  21.  
  22. '''
  23. 执行输出:
  24. Tony
  25. John
  26. 您提供的姓名数据类型不合法
  27. '''

@property可以将python定义的函数“当做”属性访问,有时候setter/deleter也是需要的。

  • 只有@property表示只读。

  • 同时有@property和@x.setter表示可读可写。

  • 同时有@property和@x.setter和@x.deleter表示可读可写可删除。

 

新式类中具有三种访问方式,分别将三个方法定义为对同一个属性:获取、修改、删除

  1. class Foo:
  2. @property
  3. def AAA(self):
  4. print('get的时候运行我啊')
  5.  
  6. @AAA.setter
  7. def AAA(self,value):
  8. print('set的时候运行我啊')
  9.  
  10. @AAA.deleter
  11. def AAA(self):
  12. print('delete的时候运行我啊')
  13.  
  14. #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
  15. f1=Foo()
  16. f1.AAA
  17. f1.AAA='aaa'
  18. del f1.AAA
  19.  
  20. '''
  21. 执行输出:
  22. get的时候运行我啊
  23. set的时候运行我啊
  24. delete的时候运行我啊
  25. '''

或者:

  1. class Foo:
  2. def get_AAA(self):
  3. print('get的时候运行我啊')
  4.  
  5. def set_AAA(self,value):
  6. print('set的时候运行我啊')
  7.  
  8. def delete_AAA(self):
  9. print('delete的时候运行我啊')
  10. AAA=property(get_AAA,set_AAA,delete_AAA) # 内置property三个参数与get,set,delete一一对应
  11.  
  12. f1=Foo()
  13. f1.AAA
  14. f1.AAA='aaa'
  15. del f1.AAA
  16.  
  17. '''
  18. 执行输出:
  19. get的时候运行我啊
  20. set的时候运行我啊
  21. delete的时候运行我啊
  22. '''

商品实例

  1. class Goods(object):
  2.  
  3. def __init__(self):
  4. self.original_price = 100 # 原价
  5. self.discount = 0.8 # 折扣
  6.  
  7. @property
  8. def price(self): # 计算折后价格
  9. new_price = self.original_price * self.discount
  10. return new_price
  11.  
  12. @price.setter
  13. def price(self, value):
  14. self.original_price = value
  15.  
  16. @price.deleter
  17. def price(self, value):
  18. del self.original_price
  19.  
  20. obj = Goods()
  21. print(obj.price) # 获取商品价格 80.0
  22.  
  23. obj.price = 200 # 修改商品原价
  24. print(obj.price) # 获取商品价格 160.0
  25.  
  26. # del obj.price # 删除商品原价 TypeError: price() missing 1 required positional argument: 'value'
  1. class Person:
  2. def __init__(self,name):
  3. self.__name = name # 私有的属性
  4.  
  5. @property
  6. def name(self):
  7. return self.__name
  8.  
  9. @name.setter
  10. def name(self,new_name):
  11. print('---',new_name)
  12.  
  13. p = Person('Tony')
  14. p.name = 'John' # 修改name属性
  15.  
  16. '''
  17. 执行输出:
  18. --- John
  19. '''

上面的代码,3个name必须是相同的。三位一体。@name.settet有且并只有一个参数

  1. class Person:
  2. def __init__(self,name):
  3. self.__name = name # 私有的属性
  4.  
  5. @property
  6. def name(self):
  7. return self.__name
  8.  
  9. @name.setter
  10. def name(self,new_name):
  11. print('---',new_name)
  12.  
  13. p = Person('Tony')
  14. print(p.name)
  15.  
  16. p.name = 'John' # 修改name属性
  17. print(p.name)
  18.  
  19. '''
  20. 执行输出:
  21. Tony
  22. --- John
  23. Tony
  24. '''

从结果上来看,并没有改变Tony的值那么如何改变呢?看下面的代码

  1. class Person:
  2. def __init__(self,name):
  3. self.__name = name # 私有的属性
  4.  
  5. @property
  6. def name(self):
  7. return self.__name
  8.  
  9. @name.setter
  10. def name(self,new_name):
  11. self.__name = new_name # 更改__name属性
  12.  
  13. p = Person('Tony')
  14. print(p.name)
  15.  
  16. p.name = 'John' # 修改name属性
  17. print(p.name)
  18.  
  19. '''
  20. 执行输出:
  21. Tony
  22. John
  23. '''

但是这样,不能保证修改的数据类型是固定的

  1. class Person:
  2. def __init__(self,name):
  3. self.__name = name # 私有的属性
  4.  
  5. @property
  6. def name(self):
  7. return self.__name
  8.  
  9. @name.setter
  10. def name(self,new_name):
  11. if type(new_name) is str:
  12. self.__name = new_name
  13.  
  14. else:
  15. print('您提供的姓名数据类型不合法')
  16.  
  17. p = Person('Tony')
  18. print(p.name)
  19.  
  20. p.name = 'John' # 修改name属性
  21. print(p.name)
  22.  
  23. p.name = 123 # 不合法
  24. print(p.name)
  25.  
  26. '''
  27. 执行输出:
  28. Tony
  29. John
  30. 您提供的姓名数据类型不合法
  31. John
  32. '''

非法类型,不允许修改,这样就可以保护属性的类型

方法伪装成的属性删除操作

  1. class Person:
  2. def __init__(self,name):
  3. self.__name = name # 私有的属性
  4.  
  5. @property
  6. def name(self):
  7. return self.__name
  8.  
  9. p = Person('alex')
  10. print(p.name)
  11. del p.name # AttributeError: can't delete attribute
  12.  
  13. # 为什么不能删除?因为name被@property伪装了,此时name是只读的。

那么如何删除呢?看下面的代码

  1. class Person:
  2. def __init__(self,name):
  3. self.__name = name # 私有的属性
  4.  
  5. @property
  6. def name(self):
  7. return self.__name
  8.  
  9. @name.deleter
  10. def name(self):
  11. del self.__name
  12.  
  13. p = Person('Tony')
  14. print(p.name)
  15.  
  16. del p.name
  17. print(p.__dict__) # 查看属性
  18.  
  19. '''
  20. 执行输出:
  21. Tony
  22. {}
  23. '''

p对象返回的是空字典,说明删除成功了!

3个装饰器的重要程度

  • @property****
  • @name.setter   ***
  • @name.deleter  *

再讲一个列子:商品的 折扣  我想看折后价

  1. class Goods:
  2. def __init__(self,name,origin_price,discount):
  3. self.name = name
  4. self.__price = origin_price # 原价
  5. self.__discount = discount # 折扣价
  6.  
  7. @property
  8. def price(self):
  9. return self.__price * self.__discount
  10.  
  11. apple = Goods('apple',5,0.8)
  12. print(apple.price) # 4.0

修改苹果的原价

  1. class Goods:
  2. def __init__(self,name,origin_price,discount):
  3. self.name = name
  4. self.__price = origin_price # 原价
  5. self.__discount = discount # 折扣价
  6.  
  7. @property
  8. def price(self): # 价格
  9. return self.__price * self.__discount
  10.  
  11. @price.setter
  12. def price(self,new_price):
  13. if type(new_price) is int or type(new_price) is float:
  14. self.__price = new_price
  15.  
  16. apple = Goods('apple',5,0.8)
  17. print(apple.price) # 4.0
  18.  
  19. apple.price = 8 # # 修改苹果的原价
  20. print(apple.price) # 6.4

被property装饰的方法,不能修改,只能查看

圆形类,有半径,面积,周长。要求:将方法伪装成属性,方法中一般涉及的都是一些计算过程

  1. from math import pi
  2. class Circle: # 圆形
  3. def __init__(self, r):
  4. self.r = r
  5.  
  6. @property
  7. def area(self): # 面积
  8. return pi * self.r ** 2
  9.  
  10. @property
  11. def perimeter(self): # 周长
  12. return pi * self.r * 2
  13.  
  14. c = Circle(10)
  15. print(c.area)
  16. print(c.perimeter)
  17.  
  18. c.r =15 # 修改半径
  19. print(c.area)
  20. print(c.perimeter)
  21.  
  22. '''
  23. 执行输出:
  24. 314.1592653589793
  25. 62.83185307179586
  26. 706.8583470577034
  27. 94.24777960769379
  28. '''

总结:

  • @property --> func 将方法伪装成属性,只观看的事儿

  • @func.setter --> func 对伪装的属性进行赋值的时候调用, 一般情况下用来做修改

  • @func.deleter --> func 在执行del 对象.func的时候调用,一般情况下用来做删除.基本不用

property的作用

  • 将一些需要随着一部分属性的变化而变化的值的计算过程 从方法 伪装成属性

  • 将私有的属性保护起来,让修改的部分增加一些约束,来提高程序的稳定性和数据的安全性

在一个类加载的过程中,会先加载这个类的名字,包括被property装饰的。

在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性,如果有就不能再在自己对象的空间中创建这个属性了

Python面向对象 | 类属性的更多相关文章

  1. Python面向对象—类属性和实例属性

    属性:就是属于一个对象的数据或函数元素 类有类方法.实例方法.静态方法.类数据属性(类变量)和实例数据属性(实例变量). 类属性:包括类方法和类变量,可以通过类或实例来访问,只能通过类来修改. 实例属 ...

  2. python 面向对象类成员(字段 方法 属性)

    一.字段 字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同, 普通字段属于对象 静态字段属于类 class Province: # 静态字段 countr ...

  3. Python面向对象-类、实例的绑定属性、绑定方法和__slots__

    绑定属性 从之前的文章中,我们知道python是动态语言——实例可以绑定任意属性. 那如果实例绑定的属性和类的属性名一样的话,会是什么情况呢? >>> class Student(o ...

  4. python面向对象——类

    from:http://www.runoob.com/python3/python3-class.html Python3 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在P ...

  5. python面向对象(类和对象及三大特性)

    类和对象是什么 创建类 新式类 和 经典类 面向对象三大特性 继承 封装 多态   面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  6. Python面向对象之类属性类方法静态方法

    类的结构 实例 使用面向对象开发时,第一步是设计类: 当使用 类名() 创建对象时,会自动执行以下操作: 1.为对象在内存中分配空间--创建对象: 2.为对象的属性 设置初始值--初始化方法(init ...

  7. ~~核心编程(二):面向对象——类&属性~~

    进击のpython 类&属性 虽然我们上一part写了一个面向对象的程序:人狗大战 但是如果在面向对象来看 你这些的就不够规范 你既然选择用面向对象的思想来写 那你就要符合人家的定义规范和操作 ...

  8. Python——面向对象(类)的基础疑难点

    相信用Python写def函数大家都信手拈来了,但Python作为面向对象的编程语言,怎么能浪费呢? 那问题来了.什么是类呢?什么是实例?什么是对象?方法是什么??属性又是什么???继承?封装?多态? ...

  9. python 添加类属性

    类属性必须赋值. 创建类属性 类是模板,而实例则是根据类创建的对象. 绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有 ...

随机推荐

  1. golang strings常用函数

    package main import ( "fmt" "strings" ) func main() { s1 := " aBc" s2 ...

  2. Python连载27-log日志

    一.log 1.推荐网站:https://www.cnblogs.com/yyds/p/6901864.html 该网站为日志处理logging模块简介 2.logging模块提供模块级别的函数记录日 ...

  3. SpringBoot第八篇:整合MyBatis-Generator

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10894278.html 版权声明:本文为博主原创文章,转载请附上博文链接! 注意:本章有大量代码 ...

  4. mybatis解决字段名和实体属性不相同

    两种方法: 1.在xml文件里面使用别名 2.使用resultMap标签

  5. CSS3幸运大转盘最简单的写法

    点击开始 直接css动画 如果你要自己控制转到哪里 那就多写几个class 根据不同角度 运行不同的class..<pre>.zhuandong{ animation: zhuandong ...

  6. 微信小程序练习笔记(更新中。。。)

    微信小程序练习笔记 微信小程序的练习笔记,用来整理思路的,文档持续更新中... 案例一:实现行的删除和增加操作  test.js // 当我们在特定方法中创建对象或者定义变量给与初始值的时候,它是局部 ...

  7. 【问题记录】ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

    一.问题描述 环境:MySQL 8.0 + Windows 由于密码错误或者其他原因导致无法连上MySQL服务,如下图: 二.解决方案 解决该问题的具体步骤如下: 1.关闭MySQL服务 以管理员权限 ...

  8. EF Core反向导航属性解决多对一关系

    多对一是一种很常见的关系,例如:一个班级有一个学生集合属性,同时,班级有班长.语文课代表.数学课代表等单个学生属性,如果定义2个实体类,班级SchoolClass和学生Student,那么,班级Sch ...

  9. C# while循环

    一.简介 只要给定条件为true,C#的while循环语句会循环重新执行一个目标的语句. 二.语法 C# while的语法: while(循环条件) { 循环体: } 三.执行过程 程序运行到whil ...

  10. CountDownEvent 信号类来等待直到一定数量的操作完成

    当主程序启动时,创建一个 CountDownEvent 类的实例,在其构造函数中指定个数操作完成发出信号,当前为2个操作完成会发出信号. /// <summary> /// 创建 Coun ...