The Python Programming Language Notes

 

刚看完Python手册中的Tutorial部分,以类C程序员的角度观察了一下Python,文中涉及的所有Python与Java,C++不同的地方都只有一个原因:Python不是Java或C++,就像bitflying说的:“However, *DO* we really need another dynamic language just like JAVA? As a scripting language, it's important for users to keep it simple, clean and easy to use. ”

See Also: http://blog.csdn.net/chelsea/category/20285.aspx

 

一. 语法

1. 按名称调用,不是按类型调用

Python在一切可能的地方弱化了类型:不能/无需声明函数返回值类型,不能/无需声明函数参数类型,对名称的查找/检查在运行时进行;这样的机制带来了几个后果:

  • 天生具有多态,virtual失去意义,因为不存在使用基类指针指向子类的情况

  • 类型退化为组织代码、保存状态的一种方式,“继承”退化为一种普通的代码复用的机制

大部分情况下子类实际上将父类当作一个普通的类库来调用,而接口或者抽象基类几乎没有存在的理由;虽然Template Method之类的模式依然起作用,但在构建Framework方面,“继承”已不再重要如昔

一个例子就是for迭代,只需按约定提供__iter__()返回定义有next()的对象,即可用于for value in XXX 形式的迭代语句中

一个例外是Exception的继承,是因为try/except是少见的根据类型而不是名称来工作的,构建一个Exception的继承体系对于异常处理来说还是很方便的

 

 

2. Tuple Anywhere

牵强一点,Tuple几乎是传统的DTO(数据传输对象)模式在语言级别的直接支持;当然,这只是Tuple众多应用之一

Tuple是Python基本数据结构之一,其思想十分简单,就是用一个单一的量来代表一堆任意数据松散的集合,其显而易见的一个用处就是在一次函数调用中返回多个值;

在非单根结构,强类型的语言如C++中,是很难实现能够容纳任意多元素的Tuple的,标准库提供了一个退化的Tuple,std::pair<F,S>,只能容纳两个元素,boostloki提供了能够容纳最多两位数元素的Tuple,绝大多数情况下也够用了;

或曰:在像Java这样的单根语言中,Tuple不就是Object[]吗?是的,如果Tuple仅限于容纳多个元素,它就是只读的Object[]

然而,Python为Tuple配备了Automated Tuple Packing & Automated Sequence Unpacking (自动化的打包/拆包),使Tuple获得了及其方便和强大的表达能力 

  • 自动打包带来的表达能力举例:函数调用传递任意的参数个数,不需要你显式的把多个参数像其它语言那样弄到一个数组或集合里,你只需要把多个参数一个接一个的传递给函数即可,解释器会将参数自动打包为Tuple;C++的可变参数在这种以强类型著称的语言中显得那样格格不如,在各种Effective条款中被推荐避免使用;JavaSE 5.0 开始提供了类似的功能,虚拟机会将参数打包为数组 

  • 自动拆包带来的表达能力举例:同时获得Dictionary的Key和Value:for key, value in dict.iteritems(): 你不再需要像其它语言那样在循环体里再分别获得key和value 

  • 自动打包结合自动拆包带来的表达能力举例:不使用临时变量交换两个变量的值:a, b = b, a

更多的时候,Tuple为其它的Python语法提供背后的支持

 

 

3. Decorator:面向属性编程

Python也有类似.Net的Attribute,Java的Annotation的机制,叫做Decorator,不过目前只能用于修饰function或method,不能用来修饰class;这类机制的用处不必多说,主要是用于正交的分离职责,最终各种运行时,平台,或者工具会将各种分离的职责合并成一个功能完整的可执行体;PEP 318给出的例子有:

  • 通用的参数和返回值检查

  • 通用的额外属性的注入

  • 通用的线程安全的保护

  • 类的singleton标记

  • ...

 

 

4. 无可比拟的参数机制

1. Pass object reference by value, hehe 

2. 任意参数,避免了对参数个数的依赖,全面超越Unary,Binary的表达能力

允许用一个参数占位符来表达运行时的多个参数,使Python可以写出不依赖参数个数的通用算法,将函数、过程、谓词区分为零元、一元、二元已经不像在其它语言中那样迫不得已

比如说通用算法map,C++提供了map的一元形式:map(unary_functor, begin, end),那么如何使map适用于二元算子呢?通常有两种做法:

  • 一种是添加map的二元形式(在Java实现的Apache Common Functor中就是这么做的),局限在于三元、四元呢?

  • 一种是使用适配器,将二元函数适配为一元,如bind1st, bind2nd等,当参数不固定时要写出专用的适配器

Python使你可以写出map的通用形式:

def map(oper, *sqs): #simulate standard map()
    return [ oper( *[(sq[i]) for sq in sqs] ) for i in range(len(sqs[0]))]

或者,使用内置的zip函数,更紧凑一点:

def map(oper, *sqs): #another way to simulate standard map()
    return [ oper( *tuple ) for tuple in zip(*sqs) ]

是的,这就是任意元都通吃的map,只有一行代码而已;我没有看过Python源代码,不知道真正的map是如何实现的

测试一下:

def testUnaryMap(self):
    self.assertEquals([1,2,3], map(math.ceil,   [0.6, 1.3, 2.4]) )

def testBinaryMap(self):
    self.assertEquals([64,25,6], map(math.pow,   [4,5,6],     [3,2,1]) )
 

......
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
 

3. Keyword参数,避免了位置依赖,极大提高可读性,降低错误倾向

参数不宜过多基本是普适的,但不是人人都遵守,在C++, Java中,如果参数比较多,又都是同一类型,对客户程序员来说简直就是恶梦,他要仔细核对参数的位置,不要将相同类型的参数弄颠倒了,编译器和IDE都帮不上什么忙;

另一个不便之处就是无论你的函数声明、定义时参数的名字多么具有可读性和意义明显,编译之后就只剩类型信息了

然而Python提供了期待已久的根据参数名字传递参数的特性,基本解决了前面两个问题,观察一下下面这行代码:

pathCtrl = wx.TextCtrl(self, -1, "", (30, 50), (150, 20))

你知道,或者说你记得住(30, 50),(150, 20)哪一对是位置,那一对是大小吗?再看下面这一行

pathCtrl = wx.TextCtrl(self, -1, "", pos=(30, 50), size=(150, 20))

真是清晰舒适啊

 

 

5. 语法糖衣

  • for/else, while/else: 适用于方便的指定集合中所有元素都不满足某谓词时的行为

传统代码

Python的for/else

int i = 0;

for(; i < len; i++){

    if (xxx) break;

}

if (i == len) {

    ...

}

for x in range(n):
   if n % x == 0:
      print n, 'equals', x, '*', n/x
      break
else:
   # loop fell through without finding a factor
   print n, 'is a prime number'

  • try/except/else,try/finally:适用于方便的指定“清理”行为

这两组语句不可混合使用,给出的理由是当except子句和finally子句同时存在时逻辑上无法证明先执行哪个是普适的合理的,呵呵

else子句当try块没有抛出异常时被执行

finally则用于任何原因离开try块时执行,当然,断电除外

  • lambda

在函数是一级公民的语言中,lambda是可有可无的

善用是糖衣,滥用则是毒药,主要是影响可读性和可维护性

  • String Literals

现在流行的语言几乎都提供了丰富的String处理函数,但如何方便的表达String literal, 很多语言却重视不够,尤其是对于转义字符和换行、空白的处理;

即当字符串中需要转义的字符比较多或字符串很长需要换行时,如何使字符串依然能够方便直观的书写;C# 提供了 @,Python则提供了 r""" """,更为强大和方便

输入

print r"df'h'sf/"s/"f/
/ns"

print """df'h'sf"s"f
/ns"""

输出

df'h'sf/"s/"f/
/ns

df'h'sf"s"f

s
(注意中间有个空行)

总结一下:

 

是否需要对'"至少其一转义

是否需要行尾/连接下一行

转义序列是否变成普通字面量

r

Yes

Yes

Yes

""" """

No

No

No

 

  • other

list comprehension

generator expression

yield...

 

二、运行时

1. 函数是一等公民

函数本身是一对象,称作function object

可以用变量命名,可以提供给过程作为参数,可以由过程作为结果返回,可以包含在数据结构中
 

 

2. 类本身是对象,一切数据类型都是对象

与C++不同,在那里,类定义只存在于编译时,运行时只有实例化的对象;在Python中,类定义与类实例一样,都是对象,都在运行时平等的存在;事实上,Python所有数据类型都是对象;

在类中定义的一切对象都叫做类的“属性”,包括数据和method对象等;有效的“属性”是类对象被创建时所声明的对象,但类对象和实例对象都可以动态增删“属性”,对增删之前的已经创建的实例或对象没有影响,只影响增删之后的代码

类对象和实例对象都可以动态修改“属性”,即对属性重新赋值,对修改之前和之后创建的实例都有影响

通俗一点讲就是类可以动态的随时增删成员,对成员重新赋值(包括将函数对象指向另外的函数体),因此可以在运行时动态调整、重新组装类的行为,实现诸如mixin之类灵活机制

 

 

三. Python与软件工程

1. Built-in Unit Test

标准模块中包含doctest, unittest,test等用于进行各种测试

其中doctest允许你将测试写在函数的注释中,这对于阅读代码的人理解函数的使用有很大的帮助

unittest基本类似JUnit

 

2. 访问控制

不知为何Python没有提供像C++和Java那样的访问控制,无法控制在module中定义的function和class的可见性;class中定义的function和variable倒是可以通过不少于两个下划线的前缀和不多于一个下划线的后缀标明这是class“私有”的.

在Python中文邮件列表问了一下,感觉至少有三种workaround:

 

1, 对于class中打算私有的函数定义以两个下划线开头

class SomeClass:
    def pub_func(self):
        self.__private_func()

    def __private_func(self):
        print 'impl'

SomeClass().pub_func()

SomeClass().__private_func() #AttributeError: SomeClass instance has no attribute '__private_func'
 

2,对于module中打算私有的function或class,定义到单独的module中,在package的__init__.py中定义__all__,使其不包括这个单独的module

并且不能在其它module的全局空间中import这个单独的module;例如,包结构:

util/__init__.py

     /private.py

     /pub.py

in util/__init__.py:

    __all__=["pub"]

in util/private.py

    def private_func():
        print 'private'

in util/pub.py

    def pub_func():

        import private

        private.private_func()

客户代码:

from util import *

pub.pub_func()        #ok
private.private_func() #NameError: name 'private' is not defined

但如果客户from util import private 还是可以使用private module的

 

3, 将打算私有的代码以可交互语言编写扩展,并以二进制提供

 

以上方法都不尽如人意,基本上,问题是在Python中有没有访问控制的必要?什么情况下会有?如果有必要的话应该怎么控制呢?

我不知道

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值