翻译TIPatterns--多个编程语言(Multiple languages)-1

类别:Java 点击:0 评论:0 推荐:

多个编程语言Multiple languages-1
    
    本章我们讨论跨越语言边界所带来的好处。通常来说,对于某个问题的解决,使用多于一种的编程语言比死抱住一门语言不放,会更便利。本章你将会看到,对于某种语言而言非常困难或者棘手的问题,如果用另外一种语言就可能很容易并且很快被解决掉。如果能够把多种语言结合起来,你就可以更快更节约的创造产品。
    上面这个想法最直接的应用就是被称为Interpreter的设计模式,它可以给你的程序添加一个解释语言(interpreted language),从而允许最终用户很容易的定制一个解决方案。对于Java来说,实现上述想法最简单和最强大的方式就是通过Jython,它是用Java的纯字节码写的一个Python语言的实现。
    Interpreter解决的是一个特定的问题——为用户创建一个脚本语言。但是有时候暂时转到另外一种语言解决你所面临的问题的某一方面会更简单和快捷。并不是说要你创建一个解释器,你只需要使用另外一种语言写一些代码就可以了。再次重申一下,Jython是这方面很好的一个例子,而CORBA也允许你跨越语言边界。


Interpreter模式的动机Interpreter motivation
    
    如果程序的用户在运行时刻需要更大的灵活性,例如,为了通过创建脚本来描述所期望的系统行为,你就可以使用Interpreter设计模式。这种情况下,你(需要)创建一个语言解释器并把它嵌入到你的程序中。
    别忘了,每种设计模式都允许一个或多个可以变化的因素,所以,重要的是首先要知道哪个因素是变化的。有些时候是你的程序的最终用户(而不是程序的编写人员)在他们配置程序的某些方面的时候需要更大的灵活性。也就是说,他们需要做一些简单的编程工作。Interpreter模式通过添加一个语言解释器提供了这种灵活性。
    问题是,开发你自己的语言并为它构建一个解释器是一项耗时的工作,而且它会分散你开发应用程序的精力。你必须问问自己是想要写完应用程序呢还是要创造一门新的语言。最好的解决方案就是代码重用:嵌入一个已经构建好并且调试过的解释器。Python语言可以被免费的嵌入以盈利为目的的应用程序,而不需要签署任何许可协议和支付版税,也不必遵循任何的附加条件。基本上说,使用Python的时候你不受任何限制。
    Python是一门非常容易学习的语言,它的逻辑性很强,便于读写,它支持函数和对象,有一大堆可用的库,几乎可以在所有平台上运行。你可以到www.Python.org下载Python并在那里找到更多的信息。
    为了解决与Java相关的问题,我们会着眼于Python的一个特殊版本Jython。它是完全由Java的字节码生成的,所以要把它并入你的应用程序非常简单,而且它和Java一样有很好的可移植性。它和Java之间有一个非常干净的接口:Java可以调用Python类,Python也可以调用Java类。
    Python从一开始就是用类来设计的,它是一门真正的纯面向对象的语言(C++和Java都以不同的方式违反了纯面向对象的原则)。由于Python是递增的(scales up),所以即使你创建非常大的程序也不会失去对其代码的控制。
    要安装Python,访问www.Python.org按那些链接和说明做就可以了。要安装Jython,访问 http://jython.sourceforge.net. 下载下来的是一个.class文件,你用Java执行它的时候它会启动一个安装程序。你还需要把jython.jar添加到你的CLASSPATH。你可以访问
http://www.bruceeckel.com/TIPatterns/Building-Code.html.找到更进一步的安装说明。


Python概览
    
    为了让你能够上手,下面是关于Python的一个简短介绍,这个介绍是针对有经验的程序员的(如果你正在读这本书那你必须得是个有经验的程序员)。你可以参考www.Python.org上更为全面的Python文档(尤其是非常有用的那个HTML页面:Python 快速参考手册),此外,你还可以参考众多的Python书籍,比如Mark Lutz和David Aschor写的《Learning Python》(O'Reilly, 1999)。
    Python经常被说成是一种脚本语言,但是脚本语言似乎有很大的局限性,尤其是就它们所解决的问题的领域而言。与此不同,Python是一门支持脚本编程的语言。用它来写脚本确实是很棒的,你甚至会想用Python脚本替换掉你所有的批处理文件,shell脚本和一些简单的程序。但是它远远超出了一门脚本语言。
    Python被设计成这样一门语言,用它写出来的代码非常干净,而且很容易读懂。你会发现即使过了很长时间你还是很容易读懂自己写的那些代码,而且读别人的代码也是如此。这在某种程度上得归功于它简明扼要的语法,但是对于代码可读性来说很重要的一个因素是缩进——对于Python来说,作用域是通过缩进来确定的。例如:

##interpreter:if.py
response = "yes"
if response == "yes":
  print "affirmative"
  val = 1
print "continuing..."
##~

    ‘#’号表示直到该行结尾的一个注释,就像C++和Java里的‘//’注释那样。
    首先我们注意到Python的基本语法是C风格的;请注意上面的if语句。但是C语言的if语句,你必须得用括号把条件语句括起来,而在Python里这么做并不是必需的(但是如果你用了括号它也不会抱怨)。
    条件语句是以一个冒号结尾的,这意味着紧跟着的是一组缩进的语句,也就是if语句的“then”那一部分。上面的例子里,先是一个“print”语句把结果送到标准输出,接下来是给一个叫做val的变量赋值的语句。再接下来的语句就没有缩进了,所以它不是if语句的一部分。缩进可以嵌套到任何一级,就像C++或者Java里的大括号,但是和C++,Java不同的是这里没有括号该放在哪里的问题(也就没有争论)——编译器强制使所有人的代码都以同一种方式格式化,这也是Python具有很强的可读性的一个主要原因。
    通常Python每行只有一个语句(你可以通过使用分号在一行放置多个语句并把它们分开),这样标志语句结束的那个分号就没有必要了。即使看看上面那个简短的例子,你也会发现这门语言被设计的尽可能的简单,而同时又具有良好的可读性。


内置容器(Built-in containers)
   
    对于像C++和Java那样的语言,容器是以库的形式而附加的,并没有和语言集成在一起。在Python里,容器之于编程语言的重要地位(essential nature)是通过把它们构建入语言核心来使其得到承认的:链表(lists)和关联数组(即映射表,字典,哈希表)都成了基本的数据类型。这使得这门语言更加优雅。
    除此之外,for语句自动遍历链表,而不仅仅是couting through a sequence of numbers。仔细想想,这其实很有意义,因为大多数情况下你都是用一个for循环来单步遍历(step through)一个数组或容器。Python通过自动地让for语句使用一个作用在(works through)某个序列上的迭代器把for语句的这种用法标准化了。下面是一个例子:

## interpreter:list.py
list = [ 1, 3, 5, 7, 9, 11 ]
print list
list.append(13)
for x in list:
  print x
##~

    第一行代码创建了一个链表。你可以把它打印出来,打印的结果应该和你放进去的一摸一样(作为对比,回忆一下在Thinking in Java第二版里,为了把数组打印出来我们必需得创建一个特别的Array2类)。这里的链表类似于Java里的容器类——你可以往里面添加新的元素(上例中,是用append()来添加的),链表会自动调整自己的大小。For语句创建了x迭代器,它会作用到链表的每一个元素。
    你可以使用range()函数创建一个存放数字的链表,所以如果你真的需要模拟C里面的for语句,你就可以这么做。请注意,上面的代码没有任何类型声明——对象只要有名字就可以了,Python会根据你使用它们的方式推断出其类型。感觉上好像设计Python的目的就是为了让你仅在绝对必要的时候才敲一下键盘。使用Python一小段时间以后,你就会发现,对于非Python的编程语言来讲必不可少、而又让你绞尽脑汁的分号、大括号和其它额外的谓词,实际并没有描述程序的意图。


函数Functions

    在Python里创建一个函数,需要用到def关键字,接下来是函数名称和参数列表,然后是一个冒号表示函数体的开始。下面的代码把上例改写成一个函数:

## interpreter:myFunction.py
def myFunction(response):
  val = 0
  if response == "yes":
    print "affirmative"
    val = 1
  print "continuing..."
  return val
 
print myFunction("no")
print myFunction("yes")
##~

    注意到函数签名(function signature)里并没有类型信息——它只是指定了函数的名称和参数标识,但是并没有参数类型或者返回类型。Python是一种弱类型(weakly-typed)语言,也就是说它对类型信息的要求降到了最低。比如,你可以针对同一个函数传入和返回不同的类型。

## interpreter:differentReturns.py
def differentReturns(arg):
  if arg == 1:
    return "one"
  if arg == "one":
    return 1
 
print differentReturns(1)
print differentReturns("one")
##~

    对于传入某个函数的对象来说,唯一的限制是,函数的操作必须得能够应用到这个对象上,除此之外,函数并不关心对象的其它东西。上例中,同一个函数把‘+’操作符应用到了数字和字符串。

### interpreter:sum.py
def sum(arg1, arg2):
  return arg1 + arg2
 
print sum(42, 47)
print sum('spam ', "eggs")
##~

    当‘+’操作符应用到字符串的时候,它的意思是,把两个字符串连接起来(是的,Python支持操作符重载,而且它在这方面做的不错)。


字符串Strings

    上面的例子还展示了一点点Python的字符串处理功能,它在这方面是我所见过的语言里做的最好的。你可以使用单引号或者双引号来表示字符串,这一点是非常好的,因为如果你用双引号来把一个字符串引起来,你就可以在其中嵌入单引号,反过来也是如此。

## interpreter:strings.py
print "That isn't a horse"
print 'You are not a "Viking"'
print """You're just pounding two
coconut halves together."""
print '''"Oh no!" He exclaimed.
"It's the blemange!"'''
print r'c:\python\lib\utils'
##~

    请记住Python并不是根据蛇的名字来命名的,实际上它是一个Monty Python comedy troupe,所以上面的例子理论上来说都需要包括Python-esque references.
    三个双引号连用,这种语法表示把它们之间的所有东西都引起来,包括新的行。这在处理诸如产生web页面(Python 是一门非常棒的CGI语言)等工作的时候尤为有用,因为你可以简单的使用三个连续的双引号把你想要的整个页面都引起来,而不用做任何其它的编辑。
    一个字符串右边的r字符表示这个字符串是个“raw字符串”,也就是说按照字面来解释反斜杠,所以你就不用再加一个额外的反斜杠了。
    Python的字符串替换出奇的简单,因为它使用了类似于C里面printf()的替换语法,对于任何字符串(but for any string at all???).你只需要在字符串后面加上一个‘%’和用来做替换的值。

## interpreter:stringFormatting.py
val = 47
print "The number is %d" % val
val2 = 63.4
s = "val: %d, val2: %f" % (val, val2)
print s
##~

    你可以看到上面第二种情况下,如果有多于一个的参数,你可以用括号把它们括起来(这组成了一个垫片(tuple),也就是一个不能作改动的链表)。
    所有适用于printf() 的格式化操作都可以用在这里,包括对小数位的个数和对齐的控制。Python还有非常复杂的正则表达式(语法)。


类Classes

    像Python里面的其它东西一样,定义一个类只需要用到最少量的额外语法。使用class关键字(定义类),在类定义体的内部使用def创建方法(methods)。下面是一个例子:

## interpreter:SimpleClass.py
class Simple:
  def __init__(self, str):
    print "Inside the Simple constructor"
    self.s = str
  # Two methods:
  def show(self):
    print self.s
  def showMsg(self, msg):
    print msg + ':',
    self.show() # Calling another method
 
if __name__ == "__main__":
  # Create an object:
  x = Simple("constructor argument")
  x.show()
  x.showMsg("A message")
##~

    上面两个方法都把“self”作为它们的第一个参数。C++和Java的类方法都有一个隐藏的第一参数,也就是指向调用这个方法的对象的一个指针,可以通过this关键字来访问。Python方法也使用了当前对象的引用,但是当定义一个方法的时候,你必须显式的把这个引用指定为第一个参数。传统上, 这个引用被称作self,然而你也可以使用任何你想用的标示符(但是如果你不用self,很可能会让很多人感到迷惑)。如果你需要用到对象的field或者它的其它方法,那你必须在表达式里使用self。但是,当你是通过像x.show()这样调用某个对象的方法的时候,并不需要把self引用传给对象——Python已经帮你做好了。
    上面的第一个方法有它的特别之处,所有以双下划线开始并且结束的标示符都是特殊的。对于上面的情况来说,它定义了构造函数,当创建对象的的时候它会自动被调用,就像C++和Java那样。然而,在上例下半部分你会看到,对象的创建就像一次使用类名称的函数调用。Python宽松(spare)的语法会让你觉得C++或者Java里的new 关键字其实都不是必需的。
    后一部分的所有代码被一个if语句隔开(set off) 了,这个if语句检查__name__这个东西是不是等于__main__。再说一次,双下划线意味着特殊的名字。使用if的原因是因为每一个文件都有可能在另外的程序里被用作库模块(马上就会讲到模块)。在那种情况下,你只需要那些定义好了的类,而不想让文件下半部分的代码被执行。这个特殊的if语句只有当你直接运行这个文件的时候才为真,也就是说,如果你在命令行输入:
    Python SimpleClass.py
    然而,如果这个文件是被另外一个程序作为一个模块引入的,那__main__的代码就不会被执行。
    可能会让你觉得有点奇怪的是,你得在方法内部定义成员变量(fields),而不是像C++或者Java那样在方法外部定义(如果你像在C++/Java里那样定义成员变量,它们就隐含的称为静态成员变量)。要创建一个对象的成员变量,你只需要在某个方法内部(通常是在构造函数里,但也并非全部如此)——使用self关键字——给它起个名字,当运行那个方法的时候会为成员变量分配空间。在C++或Java看来,这似乎有点奇怪,因为对于C++或Java来说你必须提前决定对象需要占用多少空间,但是Python的做法已经证明这是一种非常灵活的编程方法。


继承Inheritance

    因为Python是弱类型的(weakly typed),所以它并不真正关心接口——它所关心的只是把操作应用到对象上(实际上,Java的interface关键字在Python里是个浪费)。这就是说Python的继承和C++或者Java是不同的,对后两者而言经常是通过继承简单的建立一个公共接口。而对Python来说,使用继承的唯一理由是为了继承一个实现——为了重用属于基类的代码。
    如果你打算从某个类继承下来,你必须告诉Python把那个类引入的你的新文件。Python像Java那样对命名空间(name spaces)进行强有力的(aggressively)控制,而且风格也和Java类似(尽管如此,Python 保留着它一贯的简单性)。每次创建一个文件的时候,你就隐含的创建了一个和该文件同名的模块(类似于Java里的package)。这么一来,package关键字在Python里也不需要了。当你想要使用某个模块的时候,只要用import关键字,并且给出模块的名字就可以了。Python会搜索PYTHONPATH,Java用同样的方法搜索CLASSPATH(但是由于某种原因,Python并没有像Java那样的缺陷),然后读取搜索到的文件。要引用某个模块的函数或者类,你需要给出模块名称,接着是一个句号,然后是函数名或者类名。如果你觉得给出确切的名称太麻烦,你可以用:
    #from module import name(s)
    这里,“names(s)”可以是由逗号分开的一组名称。
    你可以通过在继承类(inheriting class)后面的括号里列出被继承类的名字,让继承类继承自某个类(或者是多个类——Python支持多继承)。请注意,Simple类属于SimpleClass文件(也就是模块),通过使用import语句把它引入新的命名空间。

## interpreter:Simple2.py
from SimpleClass import Simple
 
class Simple2(Simple):
  def __init__(self, str):
    print "Inside Simple2 constructor"
    # You must explicitly call
    # the base-class constructor:
    Simple.__init__(self, str)
  def display(self):
    self.showMsg("Called from display()")
  # Overriding a base-class method
  def show(self):
    print "Overridden show() method"
    # Calling a base-class method from inside
    # the overridden method:
    Simple.show(self)
 
class Different:
  def show(self):
    print "Not derived from Simple"
 
if __name__ == "__main__":
  x = Simple2("Simple2 constructor argument")
  x.display()
  x.show()
  x.showMsg("Inside main")
  def f(obj): obj.show() # One-line definition
  f(x)
  f(Different())
##~

    Simple2继承自Simple,它在构造函数里调用基类的构造函数。在display()方法里可以把showMsg()作为self的一个方法来调用,但是当调用这个方法的基类版本的时候,就得用到覆写(overriding)了,你必须给出完整的名字(译注:类名称加方法名称)并且把self作为第一个参数传给它,就像调用基类的构造函数那样。show()方法覆写过的版本也是这么做的。
    在__main__函数里,你会看到(当运行这个程序的时候)基类的构造函数会被调用。你还会看到派生类可以调用showMsg()方法,这正是你期望通过继承所达到的效果。
    Different类也有一个叫作show()的方法,但是这个类不是由Simple类继承而来的。__main__函数里的f()方法描述了什么是弱类型(weak typing):它所关心的只是show()方法可以被应用到obj上,而对类型并没有任何其它的要求。你会看到f( )可以被同样地应用到一个继承自Simple的对象和一个不是继承自Simple的对象,这两者并没有什么区别。 如果你是个C++程序员,你应该知道C++的模板(template)功能是为了在一个强类型语言里提供弱类型支持。但在Python里你自动的就得到了与模板等效的功能——而不必学习非常麻烦的语法和语义(semantics)。

本文地址:http://com.8s8s.com/it/it11271.htm