在前面学习访问权限
一文中,我们设计了一个SmartPhone类,
# 定义一个SmartPhone类
class SmartPhone(object):
__os = "" # 私有属性:系统
__type = "" # 私有属性:品牌
__memory = "4GB" # 私有属性:内存大小 默认单位GB
# 构造方法
def __init__(self, os, type, memory):
self.__os = os
self.__type = type
self.__memory = memory
def print_phone_info(self):
print('os:', self.__os, '\n品牌:', self.__type, '\n内存大小:', self.__memory)
# 对外界提供获取__os的值的方法
def get_os(self):
return self.__os
# 对外界提供设置__os的值的方法
def set_os(self, os):
self.__os = os
# 对外界提供获取__type的值的方法
def get_type(self):
return self.__type
# 对外界提供设置__type的值的方法
def set_type(self, types):
self.__type = types
# 对外界提供获取__memory的值的方法
def get_memory(self):
return self.__memory
# 对外界提供设置__memory的值的方法
def set_memory(self, memory):
self.__memory = memory
如果你观察的足够仔细,你会发现,例子中我们属性只有3个,增加了3对get\set
方法,也就是6个方法。我们已经知道,当把属性设置为私有属性时,通过添加对应的get
方法获取其值,通过添加对应的set
方法设置值,还可以通过set
方法做数据的有效性校验、过滤等操作。
那么,如果属性非常多时,我们上面的做法又会显得非常麻烦,这有没有好的解决方案呢?答案是有的。python中给我们提供了一些关键词@property
,专门帮我们解决上面的问题,我们今天就详细学习这些知识。
@property
@property是Python内置的一种装饰器。(什么是装饰器,暂时可以理解为给函数动态加上功能的一种语法)。@property的内部实现是比较复杂的。现在我们先学会如何使用,等以后有了一定的分析源码的能力,再回过头分析其内部实现。
从今天以后,在Python编程中,你一看到@property
,第一反应就是:@property专门负责把一个方法变成同名属性的。
# 定义一个Car类
class Car(object):
@property
def price(self):
print("调用了:@property修饰的price方法")
return self.__price
@price.setter
def price(self, price):
if price < 0:
price = 0
print("调用了: @price.setter修饰的price方法")
self.__price = price
解释:上面我们用@property
修饰了方法price(self):
,此时@property
会把方法price
直接变成同名属性。当我们获取price
属性的值时,实际上就是调用的此方法。
此时,@property
本身又自动创建了另一个装饰器@price.setter
,负责把一个方法变成属性赋值,即:price的setter方法。于是,@price.setter
修饰的price
其实是set方法
,当我们给price
赋值时,实际上就是调用的此方法。
@price.setter
def price(self, price):
if price < 0:
price = 0
print("调用了: @price.setter修饰的price方法")
self.__price = price
接下来验证上面的解析:
# 创建一个Car类型的对象
c = Car()
c.price = -100000 # 故意设置为负值,
print("小汽车的价格:price = ", c.price)
运行结果:
调用了: @price.setter修饰的price方法
调用了:@property修饰的price方法
小汽车的价格:price = 0
可见,我们给price设置非法
的数值时,会被@price.setter
修饰的price
方法,即:set方法
过滤处理。我们设置一个合法的价格,重新运行:
调用了: @price.setter修饰的price方法
调用了:@property修饰的price方法
小汽车的价格:price = 100000
@property补充
1.我们还可以定义只读属性,即只定义getter方法,不定义setter方法就是一个只读属性。
例如:我们给上面的Car
类,新增一个轮胎个数的只读属性:
# 定义一个Car类
class Car(object):
@property
def price(self):
print("调用了:@property修饰的price方法")
return self.__price
@price.setter
def price(self, price):
if price < 0:
price = 0
print("调用了: @price.setter修饰的price方法")
self.__price = price
@property
def tire_count(self):
return 4
读取该轮胎个数属性,用下面代码进行验证:
# 创建一个Car类型的对象c2
c2 = Car()
print("轮胎个数:", c2.tire_count)
运行结果:
轮胎个数: 4
读取成功。我们尝试设置轮胎个数为5,代码如下:
# 创建一个Car类型的对象c2
c2 = Car()
print("轮胎个数:", c2.tire_count)
c2.tire_count = 5
print("更改后,轮胎个数:", c2.tire_count)
运行结果如下:
轮胎个数: 4
Traceback (most recent call last):
File "F:/python_projects/oop/23SmartPhone.py", line 78, in <module>
c2.tire_count = 5
AttributeError: can't set attribute
会报错:AttributeError:can't set attribute
,
如果你也在用PyChar编辑器,细心的你可以发现:当我们给一个只读属性赋值时,编辑器会提示:属性
tire_count
不能被赋值。
2.@property修饰的方法名可以和@property自动生成的装饰器修饰的方法名必须相同。
上面例子中,我们采用了相同的方法名price
。下面我们尝试修改@price.setter
修饰的方法名为:price_test
:
# 定义一个Car类
class Car(object):
@property
def price(self):
print("调用了:@property修饰的price方法")
return self.__price
@price.setter
def price_test(self, price):
if price < 0:
price = 0
print("调用了: @price.setter修饰的price方法")
self.__price = price
# 创建一个Car类型的对象
c = Car()
c.price = 100000
print("小汽车的价格:price = ", c.price)
运行结果:
Traceback (most recent call last):
File "F:/python_projects/oop/23SmartPhone.py", line 71, in <module>
c.price = 100000
AttributeError: can't set attribute
这样尝试修饰的方法一个为:price
,一个为price_test
让方法名不同后,再给price
赋值时,就会报错AttributeError: can't set attribute
。并且编译器会提示:
小结
@property
广泛应用在Python类的定义中非常常见。这即可以让调用者写出简短的代码,同时又起到了对参数进行必要的检查,这样程序运行时就减少了出错的可能性,这一节知识务必熟练掌握。
更多了解,可关注公众号:人人懂编程
微信公众号:人人懂编程
网友评论