Python Cookbook-6.8 避免属性读写的冗余代码

任务

你的类会用到某些 property 实例,而 getter 或者 setter 都是一些千篇一律的获取或者设置实例属性的代码。你希望只用指定属性名,而不用写那些非常相似的代码。

解决方案

需要一个工厂函数,用它来处理那些 getter 或 setter 的参数是字符串的情况,并将正确的参数封装到一个函数中,然后将其余的工作委托给Python内建的property:

def xproperty(fget,fset,fdel = None,doc = None):
	if isinstance(fget,str):
		attr_name = fget
		def fget(obj):return getattr(obj,attr_name)
	elif isinstance(fset,str):
		attr_name = fset
		def fset(obj,val):setattr(obj,attr_name,val)
	else:
		raise TypeError,'either fqet or fset must be a str'
	return property(fget,fset,fdel,doc)

讨论

Python 内建的 property 非常有用,但它也有一点小小的烦人之处(有 Delphi 经验的开发者能比较容易地看出这一点)。尤其是当你同时需要一个setter和一个 getter 时,其中的一个需要执行一些额外的代码,而另一个则只需简单地读取或者写入实例的属性。此时,property 需要两个函数作为它的参数。其中的一个函数就是所谓的“样板代码“(即重复的长篇大论的冗余代码,既无趣也容易滋生 bug)。
举个例子:

class Lower(object):
	def __init__(self,s=''):
		self.s = s
	def _getS(self):
		return self.s
	def _setS(self,s):
		self._s = s.lower()
	s = property(_getS,_setS)

方法_getS 就是样板代码,但你仍需要编写这些代码,因为你要将它传递给 property。使用本节的方案,可以让你的代码变得更简洁,同时丝毫不改变原意:

class Lower(object):
	def __init__(self,s = ''):
		self.s = s
	def setS(self,s):
		self._s = s.lower()
	s = xproperty('_s',_setS)

在这个小例子中,这点简化看起来没省掉多少代码,但是如果在一个大项目的所有代码中应用这种简化,省去的几余代码将极为可观。
本节解决方案中的工厂函数 xproperty 的实现对参数有比较严格的要求:它需要你同时传入 fet和 fset,而且其中之一必须是一个字符串。要求两者都是字符串没有什么用处如果两者都不是字符串,或者你只需要其中的一个,可以(且应当)直接使用内建的property。但用 xproperty 预先检查会更好,它既不会带来什么大的性能损失也不会造成任何功能上的损失。

你可能感兴趣的:(#Python学习,python,开发语言)