1. 结语
1.1. 特性有助于减少前期投入
默认情况下,Python的所有实例属性和类属性都是公开的. 如果觉得应该避免意外更新属性,可以实现特性,但是代码的其他部分没有变化.
这表明我们可以先以最简单的方式定义类,也就是使用公开属性,因为如果以后需要对读值方法和设值方法增加控制, 那就可以实现特性,这样做对一开始通过公开属性的名称(如 x 和 y)与对象交互的代码没有影响.
Java语言采用的方式则截然相反:
Java 程序员不能先定义简单的公开属性,然后在需要时再实现特性,因为Java语言没有特性.
因此,在Java中编写读值方法和设值方法是常态,就算这些方法没做什么有用的事情也得这么做, 因为API不能从简单的公开属性变成读值方法和设值方法,同时又不影响使用那些属性的代码. 而且到处都使用读值方法和设值方法是愚蠢的行为。如果想编写下面的代码:
my_object.set_foo(my_object.get_foo() + 1)
这样做就行了:
my_object.foo += 1
维基的发明人和极限编程先驱 Ward Cunningham 建议问这个问题:"做这件事最简单的方法是什么?"
意即,我们应该把焦点放在目标上.提前实现设值方法和读值方法偏离了目标.在 Python 中,我们可以先使用公开属性,然后等需要时再变成特性.
python作为动态语言,这些特性也让他非常适合敏捷开发.
1.2. 运算符重载的优缺点
James Gosling决定不让Java支持运算符重载.在那次访谈中(“The C Family of Languages: Interview with Dennis Ritchie, Bjarne Stroustrup, and James Gosling”,他说: 大约 20% 到 30% 的人觉得运算符重载是罪恶之源;有些人对运算符的重载惹怒了很多人, 因为他们使用+
做列表插入,导致生活一团糟.这类问题大都源于一个事实--世界上有成千上万个运算符, 但是只有少数几个适合重载.因此, 我们要挑选,但是有时所作的决定违背直觉.
Guido van Rossum为运算符重载采取了一种折中方式--不放任用户随意创建运算符, 如:<=>
或 :-)
,这样防止了用户对运算符的异想天开,而且能让Python解析器保持简单. 此外,Python 还禁止重载内置类型的运算符,这个限制也能增强可读性和可预知的性能.
Gosling 接着说道:
社区中约有10%的人能正确地使用和真正关心运算符重载,对这些人来说,运算符重载是极其重要的. 这部分人几乎专门处理数字,在这一领域中,为了符合人类的直觉,表示法特别重要,因为他们进入这一领域时, 直觉中已经知道`+`的意思,他们知道`“a + b”`中的`a`和`b`可以是复数、矩阵或其他合理的东西.
表示法方面的问题不能低估.下面以金融领域为例说明.在Python中,可以使用下述 公式计算复利:
interest = principal * ((1 + rate) ** periods - 1)
不管涉及什么数字类型,这种表示法都成立.因此,如果是做重要的金融工作,你要确保periods
是整数, rate``interest
和principal
是精确的数字(Python 中 decimal.Decimal
类的实例), 这样上述公式就能完好运行.
但是在Java中,如果把float
换成精度不定的BigDecimal
,就无法再使用中缀运算符, 因为中缀运算符只支持基本类型.在 Java 中,支持 BigDecimal
数字的公式要这样写:
BigDecimal interest = principal.multiply(BigDecimal.ONE.add(rate) .pow(periods).subtract(BigDecimal.ONE));
显然,使用中缀运算符的公式更易读,至少对大多数人来说如此.为了让中缀运算符表示法支持非基本类型, 运算符必须能重载.Python是门高级语言,易于使用,支持运算符重载可能就是它这些年在科学计算领域得到广泛使用的主要原因. 当然,语言不支持运算符重载也有好处.对极为重视性能和安全的低级系统语言而言,这无疑是正确的决定. 新近出现的Go语言在这方面效仿了Java,它不支持运算符重载.
但是,重载的运算符,如果使用得当,的确能让代码更易于阅读和编写.对现代的高级语言来说,这是个好功能.
1.3. 猴子补丁
猴子补丁的名声不太好.如果滥用,会导致系统难以理解和维护.
- 补丁通常与目标紧密耦合,因此很脆弱.
- 另一个问题是,打了猴子补丁的两个库可能相互牵绊,因为第二个库可能撤销了第一个库的补丁.
不过猴子补丁也有它的作用,例如可以在运行时让类实现协议.适配器设计模式通过实现全新的类解决这种问题.
为 Python 打猴子补丁不难,但是有些局限.Python 不允许为内置类型打猴子补丁.其实我觉得这是优点, 因为这样可以确保 str
对象的方法始终是那些.这一局限能减少外部库打的补丁有冲突的概率.
1.4. Python 装饰器和装饰器设计模式
Python 函数装饰器符合Gamma等人在《设计模式:可复用面向对象软件的基础》一 书中对“装饰器”模式的一般描述: “动态地给一个对象添加一些额外的职责。就扩展功能而言,装饰器模式比子类化更灵活。”
在实现层面,Python装饰器与“装饰器”设计模式不同,但是有些相似之处:
在设计模式中,Decorator 和 Component 是抽象类.
为了给具体组件添加行为,具体装饰器的实例要包装具体组件的实例.《设计模式:可复用面向对象软件的基础》一书是 这样说的:
装饰器与它所装饰的组件接口一致,因此它对使用该组件的客户透明.它将客户请求转发给该组件,并且可能在转发前后执行一些额外的操作(例如绘制一个边框)。透明性使得你可以递归嵌套多个装饰器,从而可以添加任意多的功能.(第 115页)
在 Python 中,装饰器函数相当于Decorator
的具体子类,而装饰器返回的内部函数相当于装饰器实例.返回的函数包装了被装饰的函数,这相当于“装饰器”设计模式中的组件.返回的函数是透明的,因为它接受相同的参数,符合组件的接口.返回的函数把调用转发给组件,可以在转发前后执行额外的操作.因此,前面引用那段话的最 后一句可以改成:“透明性使得你可以递归嵌套多个装饰器,从而可以添加任意多的行为.”这就是叠放装饰器的理论基础.
注意,我不是建议在 Python 程序中使用函数装饰器实现“装饰器”模式。在特定情况下确实可以这么做,但是一般来说,实现“装饰器”模式时最好使用类表示装饰器和要包装的组件。
还没有评论,来说两句吧...