前言
写这个系列文章的目的就是在每一个小小的语法上面对于Python和Ruby进行比较。注意,我并不是简单的列一列他们的好处。这个过程首先非常有趣,而且是一个学习的好途径。为了比较,我把我的Python和Ruby的学习笔记合成了一个学习笔记。通过比较两个语言里面的的长处和对方是怎么实现的,让我更加熟悉Python和Ruby的语法了。
源程序
命令行参数
Ruby:
ARGV存放了所有的命令行参数
例如:
程序名字后面的任何命令行参数都会对你的程序打开全局数组ARGV。例如,假设test.rb包含了下面程序:
ARGV.each {|arg| p arg }
用下同命令行调用它:
% ruby –w test.rb "Hello World" a1 1.6180
会产生如下输出:
"Hello World"
"a1"
"1.6180"
Python:Python里面的参数是在sys.argv这个列表里面。
例如:
import sys
for arg in sys.argv:
print arg
字符编码
Ruby中字符串中的编码依赖于其文件的字符编码,可以在文件开头设定文件编码如下:
#encoding: utf8
# -*- coding: utf-8 -*- # Specify UnicodeUTF-8 characters
Python中可以在文件中加上#-*- coding: UTF-8 -*-(除了coding:utf-8以外,其他的都是为了美观加上的)
要注意的是,在两个编程语言中,文件编码声明和文件保存的类型最好相等。
注释
Ruby和Python注释都是用#号。
标准输入输出
输入
Ruby:
gets用来读取,但是还要转换成其他的类型的数据,这个可以用系统里面的内置类型构造就可以了。
as:number =Integer(gets)
Python:
1,raw_input就是用来输入的。
例如:
raw_input("whatyour name ")
这个函数中得到的结果是字符串类型,无论用户输入的是什么类型。
2,input函数,这个函数的使用形似和raw_input一致,但是他要求用户输入的是一个合格的Python表达式,也意味着:在输入字符串的时候必须输入一个“”。这是不可接受的,单数又有一个好处是:input可以判断用户输入的值得类型,这样就可以直接输入int值而不用转换了。
3,在Python3中,把raw_input和input都合并到了同一个input函数中:也就是说现在input函数对于数值和字符串有区分了。
输出
Ruby:
puts和putc 用来输出,p是puts的简写,但是可以打印出更加有好的信息。
printf用来格式化输出
Python 3:print是一个内置函数,因此在使用的时候要加上括号。
变量
并行赋值
这个Python和Ruby比较一致:
@name, @block = name,block
a, b = 1, 2, 3, 4 # a=1,b=2
c, = 1, 2, 3, 4 # c=1
如果要看到底层的机制,那么Python是用元组来实现的,Ruby用的是列表。
通用布尔规则
在ruby中除了nil和false以外,其他的值都是true。
False None 0"" () [] {}在boolean环境中是假,其他的都是真。基本上都是Python中内置类型中表示空的值。
注意:他们虽然都表示的是False,但是他们相互之间却并不相等。
命名规则
Python中的命名方法是不加前缀的方法,因此在一个函数之内可能无法辨别使用的变量是 一个全局变量还是重新生成一个局部变量,那么这个时候就需要使用global关键字来声明这个变量是一个全局变量,如果不使用这个关键字,那么Python会默认所有引用的变量都是局部的,如果加上那么这个函数处理的就是全局的变量。
Ruby中的命名规则比较特殊(本人认为也比较有效),Ruby可以通过变量名的前缀来判断这个变量的作用域。下面是规则:
局部变量:不适用任何前缀。
实例属性:加上@
类属性 :加上@@
全局属性:$
类名 :使用驼峰法,这个是强制的
常量 :全大写,这个也是强制的。
None和nil
Ruby nil:
Python:
None 是唯一的空值。它有着自己的数据类型(NoneType)。可将 None 赋值给任何变量,但不能创建其它 NoneType 对象。所有值为 None 变量是相等的。
None和其他的值比较时都是False。但是在条件判断中None是False。
但是:None== False是False。
作用域
在动态语言中传统的控制结构中没有生成变量域。那么剩下的就只是函数,类和模块可以生成变量作用于了。
但是Ruby中要注意块中变量都是局部的。
变量屏蔽
变量屏蔽的问题就是说:局部变量屏蔽了全局变量。这个时候如果想要访问局部变量就会出现问题。
Python中提供了两个函数locals和globals用来得到局部域和全局域,用他们可以轻松访问全局域。
>>> defcombine(parameter):
print parameter +globals()['parameter']
...
>>> parameter ='berry'
>>>combine('Shrub')
Shrubberry
Ruby可以使用kernel模块中的global_variables和local_variables这两个函数来得到使用符号表示的变量列表。(在Ruby里面函数可以去掉函数调用的时候的参数)
例如:
fred = 1
for i in 1..10
# ...
end
local_variables #=> [:fred, :i]
变量生成规制
在Ruby中,只要在程序中出现了变量的初始赋值,那么这个变量就已经存在于程序中,不论这个赋值是否执行。(这个和传统的想法不同:程序只有在执行赋值时才把变量加入)。
例如下面的赋值:
n = 100 if false
这儿的赋值并没有执行,但是程序空间中已经有这样的一个变量n,这是由于没有赋值,他的值为nil。
注意:
1.对于常量,这个规则有一点不同,因为常量必须有一个初始值,如果向上面的这样,那么常量是一个未初始化状态,引用它会出错
Python不是这样的,如果这条语句没有执行,那么变量就不会被加入到这个运行时环境中。
不可变类型
Ruby里面只有一个类型Number(就是数值类型)是不可变的对象,String则是可变类型。
例如:
str = "abc"
str[1] = "d"
str # => "adc"
Python:
字符串,数值和元组是不可变的,也就是说每次他们被修改的时候其实都是创建了一个新的对象。
例如:
>>> name ='Mrs. Entity'
>>> name[2]='a'
Traceback (most recentcall last):
File "<stdin>", line 1, in<module>
TypeError: 'str' objectdoes not support item assignment
表达式
除法
Ruby:
Ruby中更加喜欢对象方法而不是全局的操作符,因此Ruby的除法其实是是在int这个对象里面实现的。但是Ruby也提供了全局的除法操作符,因此全局/,int类的/div方法都是地板除。int类的fdiv实现的是数学意义上的除法。
Python:
在新的Python中,/不是传统C语言中的地板除,而是用的完整数学意义上的法。原来的地板除则使用//来代替。
引用测试
o == nil # Is o nil?
o.nil? # Another way totest
三目表达式
Ruby是传统的C的写法:
cost = duration > 180? 0.35 : 0.25
Python是经过改进的写法:a if b else c
表达式的值
在Ruby中每一个表达式都有一个值,除了While和until没有。
甚至表达式还可以一边返回值,一边抛出异常
if n < 1
raise "argument must be > 0"
elsif n == 1
1
else
n * factorial(n-1)
end
当然,也可以赋值
a = if true then 10 else20 end
Python没有这种东西。
对象ID和对象内容
对象的内容比较的时候,Ruby和Python都是使用==来表示的,而Java则是使用equal方法来表示的,而且这个方法默认还是不工作的。可以看到Java中对对对象内容比较的需求判断失误。
在比较对象ID的时候,Python使用的是is这个操作符。而Ruby使用的是equal?这个对象方法。
另外:在Ruby中使用了另外一个比较严格的对象方法equ?:两个对象的值和类型都必须相等,因此1 == 1.0为true,而1.equ?(1.0)为false
列表解析
Python
列表解析是一种使用原来的列表来建立新的列表的方法。
>>> [x*x for xin range(10)]
[0, 1, 4, 9, 16, 25, 36,49, 64, 81]
Ruby:列表解析这个功能对于Ruby来说可以用函数式编程中的思想来解决。
例如:some_array.select{|x|x % 2 == 0 }.collect{|x| x * 3}
添加守卫
守卫就是一个if语句,用来决定这个列表值是否应该放到这个结果列表里面。
>>> [x*x for xin range(10) if x % 3 == 0]
[0, 9, 36, 81]
多个列表同时解析
>>> [(x, y) forx in range(3) for y in range(3)]
[(0, 0), (0, 1), (0, 2),(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
循环生成器
循环生成器和列表解析类似,只是他实际上产生的是一个迭代的对象。每次调用这个对象的next方法可以得到下一个列表解析中的值。
>>> g =((i+2)**2 for i in range(2,27))
>>> g.next()
16
如同列表解析一样,循环生成器也可以直接用于和列表一起工作的函数。
sum(i**2 for i inrange(10))
流程控制
Ruby:then在控制结构中是可选的,但是如果判断条件和控制快在一行时必须加上then。而Python则是一律要加上冒号表示这个for条件。
注意:其实Ruby里面所有的控制语句都是一个块,但是Ruby为了和传统的控制语句相一致,Ruby控制结构里面可以省略do这个关键字。但是在控制结构;里面不允许使用大括号。
For
Ruby中的for语句和Python中的很像。尤其是对列表迭代的时候:
for i in ['fee', 'fi','fo', 'fum']
print i, " "
end
for i in 1..3
print i, " "
end
for i inFile.open("ordinal").find_all {|line| line =~ /d$/}
print i.chomp, " "
end
但是在Python里面,可以认为有些内置的对象是一些残废,不能像Ruby那么优雅地实现对于连续数字的迭代。每一个数字迭代都需要使用range这个内置函数。
深入:动态语言中的for循环比较有疑惑型,因为会让人认为在每次迭代的时候,in后面的语句都会执行得到一个可迭代对象。其实in后面的语句是一个初始化语句,仅仅在每次for开始的时候执行一次。for执行的流程如下:在循环开始的时候,in后面的初始化条件执行一次得到迭代对象。然后每次for处理这个迭代对象就可以了。
类支持for
Ruby
只要类实现了each方法就可以支持for语句,yield的参数回传递给for中的迭代变量
class Periods
def each
yield "Classical"
yield "Jazz"
yield "Rock"
end
end
periods = Periods.new
for genre in periods
print genre, " "
end
=》sical Jazz Rock
深入:
在Python里面类似:定义一个next和__iter__方法就可以了。例如:
class ForClass:
def __init__(self,count):
self.time = count
def __iter__(self):
return self
def next(self):
if self.time > 0:
self.time -= 1
return self.time
raise StopIteration
实现这个函数的时候要注意在迭代结束的时候在最后加上StopIteration这个异常表示迭代结束。并且返回值的时候使用的是return,而不是yield。
深入:Python使用的还是比较传统的设计模式中的迭代器的模式(Ruby也可以实现类似的代码,但是Python是强制的,因为他会自动调用对象的__iter_方法,而Ruby没有)。一个迭代器对象就是一个有next方法的对象,这儿这个Python对象本身充当了迭代对象,因此必须实现next和__iter__
遍历字典
Ruby里面建议使用Hash.each方法来实现变量字典。
Python:
dict={"a":"apple","b":"banana","o":"orange"}
print"##########dict######################"
for i in dict:
print "dict[%s]=" % i,dict[i]
print"###########items#####################"
for (k,v) in dict.items():
print "dict[%s]=" % k,v
print"###########iteritems#################"
for k,v indict.iteritems():
print "dict[%s]=" % k,v
print"###########iterkeys,itervalues#######"
for k,v inzip(dict.iterkeys(),dict.itervalues()):
print "dict[%s]=" % k,v
yield
Python
Python中yield用在函数执行的时候,可以让每次函数被调用的时候仅仅执行一部分就返回一个参数。这个在for循环中非常有用,可以替代上面的实现的for类。其实有yield语句是给这个函数放回了一个迭代对象,而不是这个函数本身就有迭代的功能,这也是直接给这个函数调用next不能够迭代的原因。
例如:
>>> def test():
yield "a"
yield "b"
>>>test().next()
'a'
>>>test().next()
'a'
>>>
这段代码可以清晰地看到test()每次调用的时候都返回来一个迭代对象。
如下:
>>> test()
<generator objecttest at 0x0230C3C8>
>>>
建议
1,Ruby中的for语句都是调用对象中的each方法,因此建议不使用for而直接使用对象的each方法来代替。
If
Ruby里面:
if count > 10
puts "Try again"
elsif tries == 3
puts "You lose"
else
puts "Enter a number"
end
注意if语句中,最后一个表达式的值是最后的if的结果,因此上面的语句还可以表达成
if count>10
"Try again"
elseif tries == 3
"you lose"
else
"Enter a number"
可以把if语句写到一行上,那么必须在if后面加上then
minimum = if x < ythen x else y end
但是Python除了没有单行版的控制结构,而且else if变成了elif以外。其他都是一样的。
name = raw_input('Whatis your name? ')
ifname.endswith('Gumby'):
if name.startswith('Mr.'):
print 'Hello, Mr. Gumby'
elif name.startswith('Mrs.'):
print 'Hello, Mrs. Gumby'
else:
print 'Hello, Gumby'
else:
print 'Hello, stranger'
打断
1.打断操作符可以用在循环和迭代的块语句中
2.如果和statement modifier合用,那么可以跟简单地表示
Redo
这个只有Ruby有。
redo表示把当前的循环再次执行,但是不在计算循环变量,就好像仅仅执行一次一样。
Next
Ruby里面是next,而Python里面是Continue。他们表示的含义都是直接跳到下一次迭代。
With
Python:
with实现的是一种资源借贷的方法,如下:
with open(filename) asf:
input = f.read()
output =do_something(input)
with open(filename, 'w')as f:
f.write(output)
这个方式就是使用一个有__enter__和__exit__的对象来管理资源,__enter__来得到资源,__exit__处理这个资源的善后工作。得到这个对象的方法有两种:使用一个方法放回一个对象或者直接创建一个对象。
Ruby里面没有with语句,但是类似的效果其实已经实现了。
在Python里面使用with的原因是:1,为了简化异常处理。因为with自动处理了异常。2,简化对象使用,因为这样用户程序不用再维护它调用的对象。这个对象自动产生自动销毁。比如:上面的用open的例子里面既没有自动关闭这个File对象,也没有处理文件操作过程中的异常。其实本质的原因是一个在Python里面,一个函数是一个原子的执行的实体,外部不能介入函数的执行(这就是AOP的思想),试想想如果Python的open函数自己出来异常和对象,那么我就没法把自己的代码加在这个文件打开后和关闭前。但是Ruby通过块可以实现这样的需求,使用yield就可以。
例如:Ruby里面的open语句。
File.open(filename,"r") do |file|
while line=file.gets
puts line
end
end
with对象
实现一个类__enter__()和__exit__()方法
classcontrolled_execution:
def _enter__(self):
set things up
return thing
def __exit__(self, type, value, traceback):
tear thing down
withcontrolled_execution() as thing:
some code
在实际的运行过程中,python会首先运行enter里的代码,返回thing,作为as 后面的变量值,然后再运行with模块中的代码,最后会自动执行exit中的代码,而不管with中的代码运行结果如何。这也就是with能简化try-finally语句的原因。所以with通常用在读取文件的操作中,将文件句柄的关闭操作放在exit方法中,这样就不会因忘记释放文件句柄而产生可能出现的错误。
Contextlib
>>> fromcontextlib import contextmanager
>>> from__future__ import with_statement
>>>@contextmanager
... def context():
... print 'entering the zone'
... try:
... yield
... except Exception, e:
... print 'with an error %s'%e
... raise e
... else:
... print 'with no error'
...
>>> withcontext():
... print '----in context call------'
...
entering the zone
----in contextcall------
with no error
>>>withcontext():
print '----in context call------'
1/0
entering the zone
----in contextcall------
with an error integerdivision or modulo by zero
Traceback (most recentcall last):
File "test.py", line 16, in<module>
1/0
ZeroDivisionError:integer division or modulo by zero
这个修饰器就是把一个try/except/else拆分成几个部分,其中try是一个部分,相当于前面的with对象里面的__enter__方法,其他部分就是__exit__方法。这样的话,如果是异常发生的时候,那么也是在with最后的时候执行。