Python 类的魔力特性之一:property

如果你有一个 Person 类(其中3个属性:first_name、second_name、full_name):

class Person(object):
	def __init__(self, first_name, second_name):
		self.first_name = first_name
		self.second_name = second_name
		self.full_name = first_name + ' ' + second_name
现在来实例化这个类:

jim = Person('Jim', 'Hancs')

现在可以输出实例 jim 的 first_name、second_name、full_name 3个属性:

print(jim.first_name)		# Jim
print(jim.second_name)		# Hancs
print(jim.full_name)		# Jim Hancs

现在你可以任意给这三个属性任意赋值,并输出:

jim.first_name = 'Tom'
jim.second_name = 'Lily'
jim.full_name = 'Pig'

print(jim.first_name)		# Tom
print(jim.second_name)		# Lily
print(jim.full_name)		# Pig

这显然是不科学的,首先一个人的全名怎么不等于他的姓氏+名字呢?其次一个人的名字可以乱起,但姓氏也可以乱改吗,这不是乱认爹吗?^_^
那咱们第一步就不让他去乱认爹,一出生就让他只跟一个爹(姓氏只能初始化,不能更改值)^_^

 

class Person(object):
	def __init__(self, first_name, second_name):
		self.__first_name = first_name    # 我们用私有成员变量 __first_name 来存储姓氏,
                                          # 由于用户不能直接访问类的私有成员变量,姓氏就被隐藏起来了,
                                          # 只能根据类提供的接口来对其进行操作(访问,赋值,删除)
		self.second_name = second_name
		self.full_name = first_name + ' ' + second_name

	def get_first_name(self):
		return self.__first_name

	first_name = property(get_first_name)    # property 接收三个参数,参数为函数名,
                                             # 当访问 first_name 时,调用第一个参数
                                             # 当给 first_name 赋值时调用第二个参数的函数
                                             # 当删除 first_name 变量时,调用第三个参数的函数
                                             # 也可以用关键字参数

现在可以输出三个值,可以给后两个属性任意赋值,不能给 first_name 赋值:

 
 

jim = Person('Jim', 'Hancs')

print(jim.first_name)	  # Jim(当访问first_name时会找到property()中找到第一个参数的函数并调用)
print(jim.second_name)	  # Hancs
print(jim.full_name)	  # Jim Hancs

jim.second_name = 'Lily'
jim.full_name = 'Pig'
jim.first_name = 'Tom'    # 报错:AttributeError: can't set attribute
                          # 因为我们给 property() 传第二个参数,它找不到对应的函数去调用

第二步就让他的全名只能等于姓氏+名字:

class Person(object):
	def __init__(self, first_name, second_name):
		self.__first_name = first_name
		self.second_name = second_name
		self.full_name = first_name + ' ' + second_name

	def get_first_name(self):
		return self.__first_name

	def get_full_name(self):
		return self.__first_name + ' ' + self.second_name

	def set_full_name(self, name):
		if ' ' not in name:
			print('请用空格隔开 first_name 和 second_name')
			return
		first_name, second_name = name.split(' ')
		if first_name != self.__first_name:
			print('可你不能乱认爹哦~,请用你原来的姓氏!')
			return
		self.second_name = second_name

	first_name = property(get_first_name)
	full_name = property(get_full_name, set_full_name)

测试:

 
 
 

jim = Person('Jim', 'Hancs')

print(jim.first_name)		# Jim      (其实是调用了 jim.get_first_name())
print(jim.second_name)		# Hancs
print(jim.full_name)		# Jim Hancs(其实是调用了 jim.get_full_name())

jim.second_name = 'Lily'
jim.first_name = 'Tom'		# 报错:AttributeError: can't set attribute 
                            # 因为 property() 中没有第二个参数可调用
jim.full_name = 'Pig'		# 输出错误信息:请用空格隔开 first_name 和 second_name
jim.full_name = 'Tom pig'	# 输出错误信息:可你不能乱认爹哦~,请用你原来的姓氏!
jim.full_name = 'Jim cat'	# 执行成功(其实是调用了 jim.set_full_name('jim cat'))

此时,我们只能访问 first_name 的值,却不能给 first_name 赋值,因为 property() 中没有第二个参数。访问 full_name 时,直接访问变量和调用 get_full_name() 效果是一样的。给 full_name 赋值时,直接赋值和调用 set_full_name() 效果也是一样的。这样变量的操作就变得可控,不会被外部随意更改,比如你可以控制名字赋值时长度不能超过20个字符等等,你只要在赋值函数里实现即可。还有一个好处就是,假如你在一个团队项目负责的是这个Person类的实现,别人用你的类去实现功能,假如有一天你心血来潮想优化一下算法,或者改变一下内部 full_name 的实现方式,又或者,你想给函数名换个名字,你改完之后就要通知所有用到你这个类的人去全部更改他们调用的地方,假如又过了两天你又心血来潮,又改了改,你又要让所有用到你这个类的人全部更改,这样下去总有一天你会被他们打死的。而property就不同了,你内部随便怎么改,只要你返回的结果和操作时的检查条件不变,别人就根本不用关心你内部是怎么变化的。不过这样一来,我们的类好像有点乱,我们怎么用更优雅的方式把内部get函数和set函数隐藏呢?接下来我们给它用更 Pythonic 的形式优雅地书写:

class Person(object):
	def __init__(self, first_name, second_name):
		self.__first_name = first_name
		self.second_name = second_name
		self.full_name = first_name + ' ' + second_name

	@property
	def first_name():
		def fget(self):
			return self.__first_name
		return locals()
	
	@property
	def full_name():
		def fget(self):
			return self.__first_name + ' ' + self.second_name

		def fset(self, name):
			if ' ' not in name:
				print('请用空格隔开 first_name 和 second_name')
				return
			first_name, second_name = name.split(' ')
			if first_name != self.__first_name:
				print('可你不能乱认爹哦~,请用你原来的姓氏!')
				return
			self.second_name = second_name
		return locals()

这样由于get函数和set函数是在函数内部定义的函数,当超出它的父函数作用域时,函数名变量就已经不存在了,
所以不能再用get函数和set函数来获取变量的值或给变量赋值,
这时对 first_name 和 full_name 的访问和赋值操作和其他变量行为是一样,只是你完全可以对他的访问和赋值操作进行掌控(这是《Python核心编程》里介绍的优雅书写方式,但我这样写测试是报语法错误的,可能是那本书本班比较老了吧)。

我查官方文档应该是这么写的:

class Person(object):
	def __init__(self, first_name, second_name):
		self.__first_name = first_name
		self.second_name = second_name
		self.full_name = first_name + ' ' + second_name

	@property
	def first_name(self):
		return self.__first_name
	
	@property
	def full_name(self):
		return self.__first_name + ' ' + self.second_name

	@full_name.setter
	def full_name(self, name):
		if ' ' not in name:
			print('请用空格隔开 first_name 和 second_name')
			return
		first_name, second_name = name.split(' ')
		if first_name != self.__first_name:
			print('可你不能乱认爹哦~,请用你原来的姓氏!')
			return
		self.second_name = second_name

这样能达到同样的效果,first_name 和 full_name 的行为和普通变量一样,只是变得更可操控,是不是很完美呢,这一切皆来源于 property()。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码下载:完整代码,可直接运行 ;运行版本:2022a或2019b或2014a;若运行有问题,可私信博主; **仿真咨询 1 各智能优化算法改进及应用** 生产调度、经济调度、装配线调度、充电优化、车间调度、发车优化、水库调度、三维装箱、物流选址、货位优化、公交排班优化、充电桩布局优化、车间布局优化、集装箱船配载优化、水泵组合优化、解医疗资源分配优化、设施布局优化、可视域基站和无人机选址优化 **2 机器学习和深度学习方面** 卷积神经网络(CNN)、LSTM、支持向量机(SVM)、最小二乘支持向量机(LSSVM)、极限学习机(ELM)、核极限学习机(KELM)、BP、RBF、宽度学习、DBN、RF、RBF、DELM、XGBOOST、TCN实现风电预测、光伏预测、电池寿命预测、辐射源识别、交通流预测、负荷预测、股价预测、PM2.5浓度预测、电池健康状态预测、水体光学参数反演、NLOS信号识别、地铁停车精准预测、变压器故障诊断 **3 图像处理方面** 图像识别、图像分割、图像检测、图像隐藏、图像配准、图像拼接、图像融合、图像增强、图像压缩感知 **4 路径规划方面** 旅行商问题(TSP)、车辆路径问题(VRP、MVRP、CVRP、VRPTW等)、无人机三维路径规划、无人机协同、无人机编队、机器人路径规划、栅格地图路径规划、多式联运运输问题、车辆协同无人机路径规划、天线线性阵列分布优化、车间布局优化 **5 无人机应用方面** 无人机路径规划、无人机控制、无人机编队、无人机协同、无人机任务分配 **6 无线传感器定位及布局方面** 传感器部署优化、通信协议优化、路由优化、目标定位优化、Dv-Hop定位优化、Leach协议优化、WSN覆盖优化、组播优化、RSSI定位优化 **7 信号处理方面** 信号识别、信号加密、信号去噪、信号增强、雷达信号处理、信号水印嵌入提取、肌电信号、脑电信号、信号配时优化 **8 电力系统方面** 微电网优化、无功优化、配电网重构、储能配置 **9 元胞自动机方面** 交通流 人群疏散 病毒扩散 晶体生长 **10 雷达方面** 卡尔曼滤波跟踪、航迹关联、航迹融合

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值