设为首页 加入收藏

TOP

Python描述符(descriptor)解密(一)
2014-11-24 00:14:56 来源: 作者: 【 】 浏览:17
Tags:Python 描述 descriptor 解密

Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解。这些特性包括列表/集合/字典推导式,属性(property)、以及装饰器(decorator)。对于大部分特性来说,这些“中级”的语言特性有着完善的文档,并且易于学习。


但是这里有个例外,那就是描述符。至少对于我来说,描述符是Python语言核心中困扰我时间最长的一个特性。这里有几点原因如下:


但是一旦你理解了之后,描述符的确还是有它的应用价值的。这篇文章告诉你描述符可以用来做什么,以及为什么应该引起你的注意。


在这里我要告诉你:从根本上讲,描述符就是可以重复使用的属性。也就是说,描述符可以让你编写这样的代码:


f = Foo()
b = f.bar
f.bar = c
del f.bar


而在解释器执行上述代码时,当发现你试图访问属性(b = f.bar)、对属性赋值(f.bar = c)或者删除一个实例变量的属性(del f.bar)时,就会去调用自定义的方法。


让我们先来解释一下为什么把对函数的调用伪装成对属性的访问是大有好处的。


想象一下你正在编写管理电影信息的代码。你最后写好的Movie类可能看上去是这样的:


class Movie(object):
def __init__(self, title, rating, runtime, budget, gross):
self.title = title
self.rating = rating
self.runtime = runtime
self.budget = budget
self.gross = gross


def profit(self):
return self.gross - self.budget


你开始在项目的其他地方使用这个类,但是之后你意识到:如果不小心给电影打了负分怎么办?你觉得这是错误的行为,希望Movie类可以阻止这个错误。 你首先想到的办法是将Movie类修改为这样:


class Movie(object):
def __init__(self, title, rating, runtime, budget, gross):
self.title = title
self.rating = rating
self.runtime = runtime
self.gross = gross
if budget < 0:
raise ValueError("Negative value not allowed: %s" % budget)
self.budget = budget


def profit(self):
return self.gross - self.budget


但这行不通。因为其他部分的代码都是直接通过Movie.budget来赋值的——这个新修改的类只会在__init__方法中捕获错误的数据,但对于已经存在的类实例就无能为力了。如果有人试着运行m.budget = -100,那么谁也没法阻止。作为一个Python程序员同时也是电影迷,你该怎么办?


幸运的是,Python的property解决了这个问题。如果你从未见过property的用法,下面是一个示例:


class Movie(object):
def __init__(self, title, rating, runtime, budget, gross):
self._budget = None


self.title = title
self.rating = rating
self.runtime = runtime
self.gross = gross
self.budget = budget


@property
def budget(self):
return self._budget


@budget.setter
def budget(self, value):
if value < 0:
raise ValueError("Negative value not allowed: %s" % value)
self._budget = value


def profit(self):
return self.gross - self.budget


m = Movie('Casablanca', 97, 102, 964000, 1300000)
print m.budget # calls m.budget(), returns result
try:
m.budget = -100 # calls budget.setter(-100), and raises ValueError
except ValueError:
print "Woops. Not allowed"


964000
Woops. Not allowed


我们用@property装饰器指定了一个getter方法,用@budget.setter装饰器指定了一个setter方法。当我们这么做时,每当有人试着访问budget属性,Python就会自动调用相应的getter/setter方法。比方说,当遇到m.budget = value这样的代码时就会自动调用budget.setter。


花点时间来欣赏一下Python这么做是多么的优雅:如果没有property,我们将不得不把所有的实例属性隐藏起来,提供大量显式的类似get_budget和set_budget方法。像这样编写类的话,使用起来就会不断的去调用这些getter/setter方法,这看起来就像臃肿的Java代码一样。更糟的是,如果我们不采用这种编码风格,直接对实例属性进行访问。那么稍后就没法以清晰的方式增加对非负数的条件检查——我们不得不重新创建set_budget方法,然后搜索整个工程中的源代码,将m.budget = value这样的代码替换为m.set_budget(value)。太蛋疼了!!


因此,property让我们将自定义的代码同变量的访问/设定联系在了一起,同时为你的类保持一个简单的访问属性的接口。干得漂亮!


对property来说,最大的缺点就是它们不能重复使用。举个例子,假设你想为rating,runtime和gross这些字段也添加非负检查。下面是修改过的新类:


class Movie(object):
def __init__(self, title, rating, runtime, budget, gross):
self._rating = None
self._runtime = None
self._budget = None
self._gross = None


self.title = title
self.rating = rating
self.runtime = runtime
self.gross = gross
self.budget = budget


#nice
@property
def budget(self):
return self._budget


@budget.setter
def budget(self, value):
if value < 0:
raise ValueError("Negative value not allowed: %s" % value)
self._budget = valu

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Java动态代理的InvocationHandler.. 下一篇如何在XCode中配置Boost库

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: