站长资源脚本专栏
Perl 与 Python 之间的一些异同整理
Perl 是 Practical Extraction and Report Language 的简称,由 1987 年 Larry Wall 创建,最初的目的是为了在 UNIX 上方便处理报表,经过长期的发展已经成为一种全功能的程序设计语言,当前最新的版本为 Perl5.14.1,Perl 作为一种自由而强大的编程语言,其中心思想是: There's More Than One Way To Do It。(不只一种方法來做这件事 ),即「 Tim Toady 」。作为一种“胶水型”语言,它具有强大的正则表达式和模式匹配功能以及灵活的数据结构,如动态数组、Hash 等,在语法规则上借鉴了 C/C++、Basic、Pascal 等语言,其不足之处在于存在一些冗余语法,代码的可读性较差。
Python 是一种基于面向对象的解析性交互式的开源编程语言,它起源于 1989 年末由 CWI(阿姆斯特丹国家数学和计算机科学研究所)的研究员 Guido van Rossum 创立,1991 年初公开发行,其开源式的发行方式促进了 Python 的较快发展,目前已经形成了一个强大的社区力量。Python 开发者的哲学是“用一种方法,最好是只有一种方法来做一件事”。Python 具有简单易学、代码规范、语法简单、可移植性强、支持多平台、类库丰富等优点。
Perl 和 Python 都是开源的,但其哲学理念却刚好相反,因此常被人们将这两种语言放在一起进行比较。下面的篇章将从基本数据类型、控制流、函数、面向对象、文本处理等方面对这两种语言进行简单的比较和鉴别。
Perl 与 Python 的基本数据类型
脚本语言支持多种数据类型,变量无需事先申明,类型根据值来动态确定,一个变量在程序中可以根据上下文环境的不同存储不同类型的值。
Perl 支持的基本数据类型包括:标量、数组、哈希。在定义的时分别用 $、@、% 表示。
标量 (scalar):标量是 Perl 中最简单的数据类型,大多数标量由数字或字符串组成。其中数字类型如整数、浮点数等,字符串有单引号和双引号内两种形式,对长度没有限制。两者的区别在于在单引号内 \n 不代表换行,而代表反斜线和 n 这两个字符,双引号内字符串则可以通过反斜线进行转义。字符串的操作符有 . 拼接操作符和 x 重复操作符等。
数组 (Arrays):数组用 @ 定义,如 my @array=("a","b","c","d"); 访问数组的元素用 $array[1]。在 perl 中,数组也可以当做堆栈来处理,支持的操作符包括 pop 和 push,shft 和 unshift。两组操作的区别在于前者对数组的尾部进行处理,而 shift 和 unshift 则针对数组的头部进行处理。pop 得到的是数组的最后一个元素,如 pop(@array)= d,如果数组为空,则返回 undef。而 shift(@array)=a。
哈希:也称作关联数组,是根据关键码值 (Key value) 而直接进行访问的数据结构。用 % 定义,如 %my_hash=(“key1”=>”23”,”name”=>”zhang”,”age”=>”24”),其中键以字符串表示,Hash 可以是任意大小。
与 hash 相关的函数有:
keys:返回 hash 的键列表 my @keylist = keys %hash
value:返回值列表 my @valuelist = values %hash
each:用两个元素的列表返回键值对。
复制代码 代码如下:
while(($key,$value)= each %hash)
{
print “$key =>$value\n”;
}
Python 支持五种基本数据类型:数字 (Numbers)、字符串 (String)、列表 (List)、元组 (Tuple) 和字典 (Dictionary)。其中数字和字符串和 perl 中的标量对应,列表和数组对应,元组可以看做是不可变的列表,字典和 hash 对应。
数字 (Numbers):Python 支持五种基本数字类型,分别为 int( 有符号整数 ) 、long( 长整数 ) 、bool( 布尔值 ) 、float( 浮点数 ) 、complex( 复数 )。
字符串 (String):Python 与 Perl 一样也支持单引号和双引号字符串,但与 Perl 不同,转义字符在单引号中也会起作用。同时 python 还支持三引号字符串,它允许一个字符串跨多行,字符串中可以包含换行符,制表符以及其他特殊字符。三引号字符串常用于注释或者形成文档。字符串支持成员操作符 in,not in,连接操作符 + 以及重复操作符 *。Python 字符串可以当做 list,支持切片操作符 [],[:] 和反向索引。如下:
如 aString=”abcd”;则 aString[0] 的值为 a,aString[1:3]=bc,反向索引 aString[-1]=d
列表 (List):Pyhon 中的列表与 Perl 中的数组相对应。列表的定义使用 []. 如 li = ["a", "b", "mpilgrim", "z", "example"] ,支持动态增加和删除元素以及切片操作。
增加元素可以使用 li.append(“test”),li.insert(2,”new”) 以及 li.extend([“f”,”ggf”])
删除元素使用 li.remove(“f”) 和 li.pop() 。但需要注意的是 remove 仅删除首次出现的,而 pop 会删除 list 最后的一个元素,然后返回删除的元素的值。
元组 (Tuple):元组和列表非常相似,但用()表示,并且元组是不可变的。
字典 (Dictionary):字典跟 Perl 中的 hash 一样,定义了键值对之间一对一的关系,变量可以任意取名,Python 会在内部记录其数据类型。定义一个字典:D={“name”:”Jon”,”Family”:”SH”}, 字典中的 key 是不能重复的,并且大小写敏感,同时字典中的元素是无序的。字典也支持增删操作,往字典中添加元素 D[“age”]=23, 删除元素 del D['name'],如果需要删除所有元素可以使用 D.clear(), 或者 del D 删除整个字典。
Perl 与 Python 的控制结构
在控制结果方面,Perl 较 Python 丰富,除了支持传统的 if 、while 、for 控制结构,还支持 until 、unless 、foreach 等,Python 的控制结构相对要少一些,但已经能够满足语言的要求。本节对这些控制结构进行详细比较。
If 控制结构:
Perl 与 Python 都支持 if 、if-else 、if-else if- else 三种结构,两者在语法上基本类似,但与 Python 不同的是 Perl 中没有 boolean 类型,零和空表示 False,其余表示 True,而 Python 中除了''、""、0、()、[]、{}、None 为 False 之外,其他的都是 True。同时 Python 直接用缩进表示 block 块。
表 1. if 控制结构
true_statement;
} if expression:
if_suite
if-else if (expression) {
true_statement;
} if expression:
if_suite
else:
else_suite If-else if-else if (expression_A) {
A_true_statement;
} elseif (expression_B) {
B_true_statement;
} else {
false_statement;
} if expression1:
if_suite
elif expression2:
elif_suite
else:
else_suite
Perl 中还支持 unless 条件控制语句,基本语法如下:
unless (expression) {
stmt_1; }
unless 和 if 不同之处在于当条件表达式的值为假的时候才执行,同时 unless 后面还能跟 else 语句。如:
复制代码 代码如下:
unless($mon =~/^Feb/){
print “This month has at least thirty days.\n”;
}else{
print “Do you see what's going on here?\n”;
}
循环控制结构:
For 循环:
Perl 中的 for 循环除了支持传统的 for 循环,即 for ( 表达式 1; 表达式 2; 表达式 3) ,还支持 foreach 语句,基本语法为:
复制代码 代码如下:
foreach $i (@aList) {
stmt_1;
}
python 不支持传统的 for 循环,但是提供了强大的循环结构可以遍历序列成员,同时 for 循环后面也可以接 else 语句,基本语法如下:
for inter_var in iterable:
suite_to_repeat
else:
else_statement
while 循环
Perl 循环控制结果还支持 while 和 do-while 以及 until 形式,until 与 while 结构相似,区别在于 unitl 会在条件为假的时候重复执行。until 语法如下:
复制代码 代码如下:
until(expression)
{
statement;
}
而 python 只支持 while 形式,但 python 可以在 while 后面接 else 语句。语法如下:
While condition:
statements
else:
statements
循环控制符
Perl 有三个循环控制操作符,分别为 Last 、next 、redo。
last:立即终止循环,类似于 c 中的 break。在多层循环中,只对 last 所在的当前循环块有效;
next:立刻结束当前这次迭代;
redo:将控制返回本循环的顶端,不经过条件测试也不会进去下一次迭代循环,而是重新执行本循环块。它与 next 最大的区别在于 next 会正常继续下一次迭代,而 redo 会重新执行本次迭代。
Python 也有三个循环控制操作符,分别为 break 、continue 、pass 语句。
break:与 C 中的 break 类似;
continue:continue 语句并不会退出循环结构,而是立即结束本次循环,重新开始下一轮循环,也就是说,跳过循环体中在 continue 语句之后的所有语句,继续下一轮循环;
pass:一般作为占位符或者创建占位程序,pass 语句不会执行任何操作。
Perl 与 Python 的函数
Perl 和 Python 都支持函数,可以传递参数以及在程序中对函数进行调用等。下面从函数的定义,调用,返回值以及参数传递等方面对这两者进行比较。
表 2. Perl 与 Python 函数比较
- 基本语法:
statement;
[return value]
}
- 基本语法:
def functionName(arg1,arg2,[...]):
statement
[return value]
- 内嵌函数:
Python 支持内嵌函数 , 其方法是在外部函数的定义体内定义函数 , 但整个函数体都在外部函数的作用域之内 .
def outfun():
def innerfun():
Print “inner fun test”
print “out fun test”
如下例必须使用 &chomp 调用:
sub chomp{
print “it is my chomp\n”;
}
- 直接采用 函数名(参数 1,参数 2.....)
- 函数名(参数名 1= 值,参数名 2= 值 .......)
- 在子程序调用的后面加上被括号圈引的列表表达式即可,所有的参数都会自动存储为 @_ 中,其中第一个参数为 $_[0], 第二个参数存储 $_[1]。
- 传递引用,在参数前加入 \ 表示为引用
- 按照参数声明的关键字顺序直接传递;
- 通过关键字参数 testFun(par1=“2”,par2=“45”)
- 默认参数:
> Python 在传递参数的时候支持默认参数,规则是所有的位置参数必须出现在任何一个默认参数之前,如
def fun(arg1,defarg1=”var1”,defarg2=”12”), 如果在调用的时候没有给出参数值,则会使用默认值
- 变长参数:
一种方法是利用非关键字可变长参数 , 可变长的参数元组必须在位置和默认参数之后 , 带元组的函数语法如下:
def function_name([formal_args,]*vargs_tuple):
function_body
其中 * 之后的形参将作为元组传递给函数。
另外一种方法是利用关键字变量参数,区别是在函数的参数变量里使用 **。
def dicVarArgs(arg1,arg2=”default”,**theRest):
print 'formal arg1:', arg1
print 'formal arg2:', arg2
for eachXtrArg in theRest.keys():
print 'Xtra arg %s: %s' % \(eachXtrArg, str(theRest[eachXtrArg]))
Perl 程序把变量和子程序的名称存贮到符号表中,Perl 的符号表中名字的集合就称为 Perl 包 (package)。定义语法为:package mypack;每个符号表有其自己的一组变量、子程序名,各组名字是不相关的,因此可以在不同的 Perl 包中使用相同的变量名,而代表的是不同的变量。Perl 模块有两种来源,一种是随 Perl 发行版本一同打包的,另外就是用 CPAN 中下载的。Perl 模块和包的概念并不清晰,两者有时可以混用。在程序中使用模块的操作称为导入模块;导入模块关键字 use;如:use ModuleName;模块被导入后,其中的子程序和变量就可以直接使用了;要取消一个已经导入了的模块,可以使用关键字 no;如:no ModuleName。
一个 .py 文件就是一个 python 模块。把一堆相关的 python 模块放在一个目录下,再加上一个 __init__.py 文件就构成了一个 python 包。在 Python 另一个程序中导入模块用 import module 或者 from module import *,两者的区别在于:import module 会导入 module 这个模块里的所有标识,但是这些标识现在都在 module 名字空间下。from module import * 也会导入 module 中所有标识,但是标识放到在当前名字空间里。
导入模块或包按下面顺序进行路径查找:
1. 当前目录
2. 环境变量 PYTHONPATH 所指的目录列表 3.python 解释器的安装目录
Perl 与 Python 中的 OOP
在 Perl 中,类是 Perl 包,含有提供对象方法的类,而方法是 Perl 的子程序,类名是其第一个参数,对象是对类中数据项的引用。在 Perl 中创建一个新类,首先要创建一个包,扩展名为 .pm, 在创建 perl 包的时候程序的最后一个必须为"1;";否则该包不会被 Perl 处理。
清单 1. 创建 perl 的类和对象
复制代码 代码如下:
package person;
use strict;
sub new {
my $class = shift();
print("CLASS = $class\n");
my $self = {};
$self->{"name"} = shift();
$self->{"sex"} = shift();
bless $self, $class;
return $self;
}
1;
[html]
其中 new() 方法是对象的构造函数,是创建该类的对象实例必须被调用的,它返回该对象的引用。将类名与引用相结合称为”bless”一个对象,其语法为:bless YeReference [,classname]
YeReference 是对被”祝福”的对象的引用,classname 是可选项,指定对象获取方法的包名,其缺省值为当前包名。也可以通过函数 bless 来声明一个构造函数。
[code]
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
创建一个对象可以直接使用 new 关键字。$object = new Person( "mohand", "sam", 345);
Perl 类中的方法就 Perl 的子函数,规定第一个参数为对象或者被引用的包,分为静态方法和虚方法。 虚方法通常首先把第一个参数 shift 到变量 self 或 this 中,然后将该值作普通的引用使用。一是通过该对象的引用 ( 虚方法 ),一是直接使用类名 ( 静态方法 )。如上例中如果类 Person 中有 getContactList 则可以直接使用 $object->getContactList() 来调用该方法。
Perl 支持重载,当两个不同的类中含有相同的方法名称的时候,可以用 :: 操作符指定使用哪个类中的方法。
$mess = Qava::grind("whole","lotta","bags");
Qava::grind($mess, "whole","lotta","bags");
由于 Perl 采用了简单的、基于引用的垃圾回收系统。Perl 跟踪对象的链接数目,当某对象的最后一个应用释放到内存池时,该对象就自动销毁。因此一般不需要定义类的析构函数。
Perl 通过数组 @ISA 支持继承。
package Employee;
use Person;
use strict;
our @ISA = qw(Person); # inherits from Person
当子类继承父类的时候,继承了父类的所有方法,但子类也可以覆盖父类的方法。如加入 Employee 想覆盖父类的 getFirstName:
复制代码 代码如下:
#!/usr/bin/perl
package Employee;
use Person;
use strict;
our @ISA = qw(Person); # inherits from Person
# Override helper function
sub getFirstName {
my( $self ) = @_;
# This is child class function.
print "This is child class helper function\n";
return $self->{_firstName};
}
1;
调用直接使用 $firstName = $object->getFirstName(); 如果要调用父类的 getFirstName,则可以使用 $object->Person::getFirstName();
在 Python 中创建一个类的基本语法为 :
class className(bases):
classBody
参数 base 可以是一个单继承或者多继承的父类,object 是所有类的父类,位于类继承结构的最上层。类的构造函数为 __init__(),其中构造函数中 self 会作为第一个默认的参数。而类的析构函数则是 __del__(),访问类的方法和属性可以直接使用 . 访问符。
Python 不支持纯虚函数或抽象方法,并且声明和定义没有本质区别。一般或者 Python 类的属性可以通过 __dict__ 或者 dict()访问。常见属性有 __name__ ,__doc__,__base__,__dict__。Python 中创建一个类的实例,不需要关键之 new,直接使用类名 () 即可。如 c=myclass()。
Python 不仅仅支持单继承和多继承,同时还支持方法的覆盖 .
复制代码 代码如下:
class P(object):
def foo(self):
print 'Hi, I am P-foo()'
> p = P()
> p.foo()
Hi, I am P-foo()
现在创建 C 类 , 继承于 P
复制代码 代码如下:
class C(P):
def foo(self):
print 'Hi, I am C-foo()'
> c = C()
> c.foo()
Hi, I am C-foo()
当从一个带构造器 __init()_ 的类派生,如果在子类中覆盖了 __init__(),当子类被实例化时,基类的 __init__() 方法不会被自动调用。如果必须调用基类的构造方法,可以使用父类名 .__init__(self) 方法或者 super( 子类名,self).__init__()。 如
复制代码 代码如下:
def __init__(self):
super(C, self).__init__()
print "calling C's constructor"
Python 类和实例支持一些内建函数,如
Issubclass(sub,sup):判断一个类是另一个类的子类或子孙类;
isinstance(obj1,obj2):判定一个对象是否是另一个给定类的实例;
Perl 与 Python 的正则表达式
正则表达式是 perl 比较突出的一大特色,perl 中正则表达式有三种形式:
匹配:m/<regexp>/ (还可以简写为 /<regexp>;/ ,略去 m) 替换:s/<pattern>/<replacement>/ ,为了语法的简化用 /<pattern>/<replacement>/ 表示,略去 s
转换:tr/<charClass>/<substituteClass>/ 这种形式包含一系列的字符— /<charClass> —同时把它们替换为 <substituteClass>。
表 3. Perl 常用匹配模式
abc 匹配含有 abc 的字符串 匹配 a 或者 b 或者 c 等 (pattern) () 这个符号会记住所找寻到的字符串,是一个很实用的语法。第一个 () 内所找到的字符串变成 $1 这个变量或是 \1 变量,第二个 () 内所找到的字符串变成 $2 这个变量或是 \2 变量,以此类推下去。 b(\d)c 表示匹配的任何数字将存放与 $1 变量中
Python 语言本身不支持正则表达式,依赖 re 模块(python1.5 版本被引入)支持正则表达式。有搜索和匹配两种方法完成匹配模式。re 模块常用的函数和方法有 complie、match、search、find 与 findall 等。在利用 re 进行匹配之前,模式必须被编译成 regex 对象。
表 4. Python 常用匹配模式
Perl 与 Python 中的线程
线程是一个单一的执行流程,它是所有程序执行过程中最小的控制单位,即能被 CPU 所调度的最小任务单元。在 Perl 中一个线程的生命周期包括创建,运行与退出这三个阶段。线程的运行过程与普通函数的执行类似,但新建线程的执行与当前线程的执行是并行的。
在 Perl 中创建线程有两种方法:
清单 2. 使用 threads 包的 create() 方法
复制代码 代码如下:
use threads;
sub say_hello
{
printf("Hello thread! @_.\n");
return( rand(10) );
}
my $t1 = threads->create( \&say_hello, "param1", "param2" );
清单 3. 使用 async{} 块创建线程
复制代码 代码如下:
#!/usr/bin/perl
use threads;
my $t4 = async{
printf("Hello thread!\n");
};
对于线程的执行控制,有两种方式,一种是 join(),一种是 detach()。所谓 join() 就是在主线程中等待子线程的执行返回值,然后再继续执行后续代码,而在调用线程的 join() 方法之前,子线程与主线程的执行是分开的。而 detach() 则是告诉解释器主线程不关心子线程的执行结果,所以该子线程在完成任务之后就是自动退出,同时释放自己所占有的资源,而不用主线程再操心。
Perl 默认任何数据结构都不是共享的,任何新创建的线程都有当前数据的私有拷贝。如果要共享数据,必须使用 threads::shard 进行显示声明。
如:
复制代码 代码如下:
my $var :shared = 0; # use :share tag to define
my @array :shared = (); # use :share tag to define
my %hash = ();
share(%hash); # use share() funtion to define
同时 Perl 线程还支持锁机制,可以使用 lock 方法实现线程间共享数据的锁机制。Perl 中的 Thread::Semaphore 包为线程提供了信号量的支持,Thread::Queue 包为线程提供了线程安全的队列支持。更多使用读者可以自行查阅相关文档。
Python 提供了几个用于多线程编程的模块,包括 thread, threading 和 Queue 等。thread 和 threading 模块允许程序员创建和管理线程。thread 模块提供了基本的线程和锁的支持,而 threading 提供了更高级别,功能更强的线程管理的功能。Queue 模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
Python 的线程创建也有两种方式,一是利用 thread 模块的 start_new_thread() 函数来产生新线程。
复制代码 代码如下:
import time
import thread
def timer(no, interval):
cnt = 0
while cnt<10:
print 'Thread:(%d) Time:%s/n'%(no, time.ctime())
time.sleep(interval)
cnt+=1
thread.exit_thread()
def test(): #Use thread.start_new_thread() to create 2 new threads
thread.start_new_thread(timer, (1,1))
thread.start_new_thread(timer, (2,2))
if __name__=='__main__':
test()
另一种是创建 threading.Thread 的子类来包装一个线程对象。
复制代码 代码如下:
class timer(threading.Thread): # derived from the class threading.Thread
def __init__(self, num, interval):
threading.Thread.__init__(self)
self.thread_num = num
self.interval = interval
self.thread_stop = False
def run(self): #Overwrite run() method, put what you want the thread do here
while not self.thread_stop:
print 'Thread Object(%d), Time:%s/n' %(self.thread_num, time.ctime())
time.sleep(self.interval)
def stop(self):
self.thread_stop = True
Python 线程中也提供同步机制,可以利用 thrading 模块的 threading.RLock 和 hreading.Condition 可以分别实现锁机制和条件变量。
其中 acquire() 和 release() 方法分别获取和释放锁。
复制代码 代码如下:
def run(self):
global x
lock.acquire()
for i in range(3):
x = x + 1
time.sleep(2)
print x
lock.release()
更多关于线程的内容,读者可查阅相关文档。
总结
本文从 Perl 和 Python 的起源,基本数据类型、控制结构、函数、包与模块、面向对象、正则表达式以及线程等方面进行了比较,从而给需要同时掌握这两种脚本语言的开发人员一定参考,以便更好的理解与应用。
参考资料 学习
- 在 developerWorks Linux 专区寻找为 Linux 开发人员(包括 Linux 新手入门)准备的更多参考资料,查阅我们 最受欢迎的文章和教程。
- 在 developerWorks 上查阅所有 Linux 技巧和 Linux 教程。
- 随时关注 developerWorks 技术活动和 网络广播。
- 参考 Tutorial Perl官方文档关注更多 Perl 动态。
- 参考 Perl Document了解 Perl 基本语法。
- 参考 Tutorial Python查看 Python 的官方文档。