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

class Person(object):
    def __init__(self,name,weight,height):
        self.name = name
        self.__weight = weight
        self.__height = height

    @property
    def bmi(self):
        return self.__weight / self.__height **2

p = Person('xiao',65,1.75)
print(p.bmi)                                        # 21.224489795918366
print(Person.__dict__)                   
print(p.__dict__)                      # 在对象里面,并没有被当成属性

'''
执行输出:
21.224489795918366
{'__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}

{'name': 'xiao', '_Person__weight': 65, '_Person__height': 1.75}
'''

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

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

class Person(object):
    def __init__(self,name,weight,height):
        self.name = name
        self.__weight = weight
        self.__height = height
        #self.bmi = self.__weight / self.__height **2
        #self.bmi = cal_BMI()  

# 但是计算的逻辑,不能放到init里面。初始化,不要做计算

举例:

class Person(object):
    def __init__(self,name,weight,height):
        self.name = name
        self.__weight = weight
        self.__height = height
        self.bmi = self.__weight / self.__height **2

p = Person('xiao',65,1.75)
print(p.bmi)
p._Person__weight = 70                                  # 1周之后,增加体重
print(p.bmi)

'''
执行输出:
21.224489795918366
21.224489795918366
'''

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

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

class Person(object):
    def __init__(self,name,weight,height,bmi):
        self.name = name
        self.__weight = weight
        self.__height = height
        self.bmi = bmi

    def bmi(self):
        return self.__weight / self.__height **2

a = Person('xiao',65,1.75)
print(a.bmi())                      # TypeError: __init__() missing 1 required positional argument: 'bmi'

那么bmi是否可以修改呢?

class Person(object):
    def __init__(self,name,weight,height):
        self.name = name
        self.__weight = weight
        self.__height = height

    @property
    def bmi(self):
        return self.__weight / self.__height **2

p = Person('xiao',65,1.75)
p.bmi = 2    # AttributeError: can't set attribute

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

class Person:
    def __init__(self,name):
        self.name = name

p = Person('Tony')
print(p.name)           # Tony
p.name = 'John'
p.name = 123

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

class Person:
    def __init__(self,name):
        self.__name = name  # 私有的属性

    @property
    def name(self):
        return self.__name

    def set_name(self,new_name):
        if type(new_name) is str:
            self.__name = new_name

        else:    # 通过if判断,就可以保护属性的类型,必须是字符串
            print('您提供的姓名数据类型不合法')

p = Person('Tony')
print(p.name)
p.set_name('John')
print(p.name)
p.set_name(123)

'''
执行输出:
Tony
John
您提供的姓名数据类型不合法
'''            

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

  • 只有@property表示只读。

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

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

 

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

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

'''
执行输出:
get的时候运行我啊
set的时候运行我啊
delete的时候运行我啊
'''

或者:

class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA)        # 内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

'''
执行输出:
get的时候运行我啊
set的时候运行我啊
delete的时候运行我啊
'''

商品实例

class Goods(object):

    def __init__(self):
        self.original_price = 100           # 原价
        self.discount = 0.8                 # 折扣

    @property
    def price(self):                        # 计算折后价格
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self, value):
        del self.original_price

obj = Goods()
print(obj.price)    # 获取商品价格 80.0

obj.price = 200     # 修改商品原价
print(obj.price)    # 获取商品价格 160.0

# del obj.price     # 删除商品原价 TypeError: price() missing 1 required positional argument: 'value'
class Person:
    def __init__(self,name):
        self.__name = name      # 私有的属性

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,new_name):
        print('---',new_name)

p = Person('Tony')
p.name = 'John'                 # 修改name属性

'''
执行输出:
--- John
'''

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

class Person:
    def __init__(self,name):
        self.__name = name  # 私有的属性

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,new_name):
        print('---',new_name)

p = Person('Tony')
print(p.name)

p.name = 'John'     # 修改name属性
print(p.name)

'''
执行输出:
Tony
--- John
Tony
'''

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

class Person:
    def __init__(self,name):
        self.__name = name                  # 私有的属性

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,new_name):
        self.__name = new_name              # 更改__name属性

p = Person('Tony')
print(p.name)

p.name = 'John'     # 修改name属性
print(p.name)

'''
执行输出:
Tony
John
'''

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

class Person:
    def __init__(self,name):
        self.__name = name  # 私有的属性

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,new_name):
        if type(new_name) is str:
            self.__name = new_name

        else:
            print('您提供的姓名数据类型不合法')

p = Person('Tony')
print(p.name)

p.name = 'John'     # 修改name属性
print(p.name)

p.name = 123        # 不合法
print(p.name)

'''
执行输出:
Tony
John
您提供的姓名数据类型不合法
John
'''

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

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

class Person:
    def __init__(self,name):
        self.__name = name  # 私有的属性

    @property
    def name(self):
        return self.__name

p = Person('alex')
print(p.name)
del p.name    # AttributeError: can't delete attribute

# 为什么不能删除?因为name被@property伪装了,此时name是只读的。    

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

class Person:
    def __init__(self,name):
        self.__name = name  # 私有的属性

    @property
    def name(self):
        return self.__name

    @name.deleter
    def name(self):
        del self.__name

p = Person('Tony')
print(p.name)

del p.name
print(p.__dict__)  # 查看属性

'''
执行输出:
Tony
{}
'''

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

3个装饰器的重要程度

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

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

class Goods:
    def __init__(self,name,origin_price,discount):
        self.name = name
        self.__price = origin_price                 # 原价
        self.__discount = discount                  # 折扣价

    @property
    def price(self):
        return self.__price * self.__discount

apple = Goods('apple',5,0.8)
print(apple.price)                          # 4.0

修改苹果的原价

class Goods:
    def __init__(self,name,origin_price,discount):
        self.name = name
        self.__price = origin_price  # 原价
        self.__discount = discount  # 折扣价

    @property
    def price(self):  # 价格
        return self.__price * self.__discount

    @price.setter
    def price(self,new_price):
        if type(new_price) is int or type(new_price) is float:
            self.__price = new_price

apple = Goods('apple',5,0.8)
print(apple.price)              # 4.0

apple.price = 8                 # # 修改苹果的原价
print(apple.price)              # 6.4

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

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

from math import pi
class Circle:  # 圆形
    def __init__(self, r):
        self.r = r

    @property
    def area(self):  # 面积
        return pi * self.r ** 2

    @property
    def perimeter(self):  # 周长
        return pi * self.r * 2

c = Circle(10)
print(c.area)
print(c.perimeter)

c.r =15                 # 修改半径
print(c.area)
print(c.perimeter)

'''
执行输出:
314.1592653589793
62.83185307179586
706.8583470577034
94.24777960769379
'''

总结:

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

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

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

property的作用

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

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

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

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

最新文章

  1. Android 中关于Fragment嵌套Fragment的问题
  2. 线程和线程池的理解与java简单例子
  3. zookeeper系列之通信模型(转)
  4. CentOS下Apache开启Gzip网页压缩功能
  5. 对于System.Net.Http的学习(二)——使用 HttpClient 进行连接
  6. Android Studio 查看密钥库证书指纹SHA1
  7. 传递引用类型参数的两种方式(转自 MSDN)
  8. Mysql对用户操作加审计功能——高级版
  9. dotnet use regex two samples
  10. 腾讯企鹅智酷100多张PPT:移动时代创业黄金法则
  11. Eclipse搭建Android5.0应用开发环境 “ndk-build”:launchingfailed问题解决
  12. HTTP - 摘要认证
  13. AngularJS(6)-选择框Select
  14. Java List 用法代码分析 非常详细
  15. bmp to jpg
  16. Android(java)学习笔记118:类继承的注意事项
  17. ASP.NET MVC3细嚼慢咽---(3)Razor视图语法
  18. tortoisegit 保存用户名密码
  19. 从iReport至Jaspersoft Studio
  20. 有关opacity或RGBA设置颜色值及元素的透明值

热门文章

  1. 【2019年07月22日】A股最便宜的股票
  2. Python【每日一问】38
  3. Shell~echo -e 颜色输出
  4. R与金钱游戏:均线黄金交叉2
  5. 如何解决RIP的问题
  6. 《 .NET并发编程实战》实战习题集 - 3 - CRUD项目中使用FP
  7. 【JVM】jstat命令详解---JVM的统计监测工具
  8. request.setAttribute()的用法
  9. Blend 设置通明窗体
  10. TCP/UDP协议(二)