这是分两部分的教程的第二部分,旨在向您介绍Jython脚本语言。 Jython是已与Java平台无缝集成的Python实现。 Python是一种功能强大的面向对象的脚本语言,主要在UNIX环境中使用。
在本教程的第1部分中,您学习了Jython的基础知识,包括安装和设置,访问选项和文件编译,语法和数据类型,程序结构,过程语句和函数。 在第2部分中,我们将深入研究使用这种强大的脚本语言的一些更高级的方面,首先是对Jython的面向对象编程的深入介绍。 我们还将讨论任何语言对应用程序开发机制必不可少的主题,包括调试,字符串处理和文件I / O。
到您完成Jython的两部分介绍的下半部分时,您将能够在Jython中编写和实现完整的功能,类和程序。
本教程旨在逐步介绍Jython。 如果您尚未完成本教程的第1部分 ,则应在继续进行第2部分之前完成。如果不参考第1部分 ,那么概念性讨论和此处介绍的许多代码示例将很难遵循。
在本教程的后半部分,我们将介绍使用Jython编写脚本的以下方面:
要从讨论中受益,您应该熟悉至少一种过程编程语言和计算机编程的基本概念,包括命令行处理。 要充分利用Jython的功能,您还应该熟悉面向对象编程的基本概念。 为了完全理解本教程结尾处的GUI应用程序示例,您应该具有Swing GUI编程的先前经验,尽管您将能够从前面的讨论和示例中学到很多东西。 熟练掌握Java平台也将很有帮助,因为Jython运行在JVM上。 尽管这不是本教程的要求。
请注意,本教程是针对Windows系统的。 所有命令示例都将使用Windows语法。 在大多数情况下,相似的命令在UNIX系统上执行相同的功能,尽管不会演示这些命令。
您必须在开发系统上安装Jython 2.1或更高版本才能完成本教程。 您的开发系统可以是与命令提示符结合使用的任何ASCII文本编辑器(例如Windows记事本)。 本教程包括有关在系统上获取和安装Jython的详细说明。
要使用Jython,您还必须在系统上安装Java运行时环境(JRE)。 建议您使用最新的JRE(在撰写本文时为1.4.2),但是Java 1.2或更高版本的任何版本都可以正常工作。 如果要在浏览器中使用Jython(即作为小程序),则必须至少对浏览器有JRE 1.1可用。 请参阅“ 相关主题”部分以下载最新版本的JDK。
本教程中的所有代码示例均已在Windows 2000上的Sun Java 1.4.1 JRE上运行的Jython上进行了测试。示例在其他操作系统上的任何类似配置上均应保持不变。
本教程包括一组附录,详细介绍了您将用来了解Jython的所有代码示例。 所有代码示例均已在Windows 2000的Sun Java 1.4.1 JRE上运行的Jython上进行了测试。这些示例在其他操作系统上的任何类似配置上均应保持不变。
面向对象编程(OOP)代表了软件编程技术的最新发展。 OOP基于在程序中创建目标问题的模型 (或仿真)的概念。 正确使用OOP技术可减少编程错误,加快软件开发速度,并促进现有代码的重用。 Jython完全支持OOP的概念和实践。
在以下各节中,我将介绍OOP并描述如何在Jython中实现。 在下一节中,我将讨论Jython中面向对象编程的一些更高级的功能。
Jython是一种面向对象的语言,完全支持面向对象的编程。 Jython定义的对象具有以下功能:
is
和is not
测试。 请注意, id(object)
内置函数返回唯一的整数标识值。 因此,表达式x is y
等于id(x) == id(y)
。
在对面向对象编程的支持中,Jython包括以下功能:
定义类非常类似于定义模块,因为可以定义变量和函数。 与Java语言不同,Jython允许每个源文件(或模块)定义任意数量的公共类。 因此,Jython中的模块非常类似于Java语言中的软件包。
我们使用class
语句在Jython中定义类。 class
语句具有以下形式:
class name ( superclasses ): statement
-- or --
class name ( superclasses ):
assignment
:
function
:
定义类时,可以选择提供零个或多个赋值语句。 这些创建由类的所有实例共享的类属性。 您还可以提供零个或多个函数定义。 这些创建方法。 超类列表是可选的。 我们将在本教程的稍后部分讨论超类。
类名在相同范围(模块,函数或类)中应该唯一。 类名实际上是绑定到类主体的变量(类似于任何其他分配)。 实际上,您可以定义多个变量来引用同一类。
类用于保存类(或共享)属性或创建类实例。 要创建类的实例,您可以像调用一个函数一样调用该类。 无需像C ++或Java语言那样使用新的运算符。 例如,与类
class MyClass:
pass
以下语句创建一个实例:
x = MyClass()
在Jython中(不同于Java语言),客户端可以将字段 (也称为attribute )添加到实例。 仅一个实例被更改。 要将字段添加到实例( x ),只需在该实例上设置新值,如下所示:
x.attr1 = 1
x.attr2 = 2
:
x.attrN = n
绑定到类中的任何变量都是类属性 (或变量)。 在类中定义的任何函数都是方法 。 方法接收该类的实例(通常称为self
)作为第一个(可能是唯一的)参数。 例如,要定义一些类属性和方法,可以输入:
class MyClass:
attr1 = 10 # class attributes
attr2 = "hello"
def method1(self):
print MyClass.attr1 # reference the class attribute
def method2(self, p1, p2):
print MyClass.attr2 # reference the class attribute
def method3(self, text):
self.text = text # instance attribute
print text, self.text # print my argument and my attribute
method4 = method3 # make an alias for method3
请注意,在类内部,应使用类名称(例如MyClass.attr1
)限定对类属性的所有引用,并使用self
变量(例如self.text
)对所有实例属性的引用进行限定。 在类之外,应该使用类名(例如MyClass.attr1
)或实例(例如x.attr1
)来限定对类属性的所有引用,并使用实例(例如x.text
)来x.attr1
对实例属性的所有引用x.text
,其中x是该类的实例)。
为了实现数据隐藏,通常需要创建“私有”变量,该变量只能由类本身访问。 Jython提供了一种命名约定,使在类之外访问属性和方法变得困难。 如果您声明以下形式的名称: __xxx或__xxx_yyy (这是两个下划线),则Jython解析器将自动修改(即,将类名添加到)声明的名称,实际上创建了隐藏变量。 例如:
class MyClass:
__attr = 10 # private class attribute
def method1(self):
pass
def method2(self, p1, p2):
pass
def __privateMethod(self, text):
self.__text = text # private attribute
请注意,与C ++和Java语言不同,对实例变量的所有引用都必须使用self
限定; 有没有隐含使用this
。
__init__
方法充当实例构造函数的角色。 每当创建实例时都会调用它。 应该为所有类定义此方法。 方法__init__
可以接受参数。 在Jython中,与C ++或Java语言不同,所有实例变量(也称为属性或字段)都是通过赋值动态创建的。 它们应该在__init__
内定义(即分配给)。 这样可以确保为后续使用方法定义它们。 一些示例如下:
class Class1:
def __init__ (self): # no arguments
self.data = [] # set implicit data
class Class2:
def __init__ (self, v1, v2): # 2 required arguments
self.v1 = v1 # set data from parameters
self.v2 = v2
class Class3:
def __init__ (self, values=None): # 1 optional argument
if values is None: values = []
self.values = values # set data from parameter
如果您在__init__
方法(或任何其他方法)中分配了任何资源,则需要确保在释放对象之前释放这些资源。 最好的方法是使用__del__
方法。 在垃圾回收器释放对象之前,将调用__del__
方法。 您还应该提供可以直接调用的清理方法(通常称为close
, destroy
或dispose
)。 这是一个例子:
class Class:
def __init__ (self, db):
self.connection = db.getConnection() # establish a connection
self.connection.open()
def __del__ (self): # cleanup at death
self.close()
def close(self): # cleanup
if not self.connection is None and self.connection.isOpen():
self.connection.close() # release connection
self.connection = None
也可以将类分配给变量(包括函数参数)。 从以下通用类实例工厂可以看出,这使得基于类编写动态代码变得非常容易:
def instanceMaker(xclass, *args):
return apply(xclass, args)
:
x = instanceMaker(MyClass) # same as: x = MyClass()
从类继承的能力是面向对象编程的基础。 Jython支持单继承和多继承。 单一继承意味着只能有一个超类。 多重继承意味着可以有多个超类。
继承是通过对其他类进行子类化来实现的。 这些类可以是其他Jython类或Java类。 任何数量的纯Jython类或Java接口都可以是超类,但是只能(直接或间接)继承一个Java类。 您不需要提供超类。
超类中的任何属性或方法也都在任何子类中,并且可由类本身或任何客户端使用(假定它是公开可见的)。 只要可以使用超类的实例,就可以使用子类的任何实例-这是多态的一个示例。 这些功能使重用,快速开发和易于扩展成为可能。
以下是继承的一些示例:
class Class1: pass # no inheritance
class Class2: pass
class Class3(Class1): pass # single inheritance
class Class4(Class3,Class2): pass # multiple inheritance
from java import awt
from java import io
# inherit a Java class and interface and a Jython class
class MyPanel(awt.Panel, io.Serializable, Class2):
:
子类的__init__
方法必须调用为其父类定义的任何__init__
方法。 这不是自动的。 下面的两个示例演示了如何将__init__
方法与继承一起使用。
class Class1(SuperClass):
def __init__ (self): # no arguments
SuperClass.__init__(self) # init my super-class
self.data = [] # set implicit data
class Class2(SuperClass):
def __init__ (self, v1, v2): # 2 required arguments
SuperClass.__init__(self, v1) # init my super-class with v1
self.v2 = v2
以下是一些使用多重继承进行初始化的示例:
class Class1(Super1, Super2):
def __init__ (self): # no arguments
Super1.__init__(self) # init each super-class
Super2.__init__(self)
self.data = [] # set implicit data
class Class2(Super1, Super2):
def __init__ (self, v1, v2, v3): # 3 required arguments
# note you may do work before calling the super __init__ methods
self.v3 = v3 # set data from parameter
Super1.__init__(self, v1) # init each super-class
Super2.__init__(self, v2)
您可以通过使用类名限定任何超类方法来调用它,如下所示:
class Class1:
def method1 (self):
:
class Class2(Class1):
def method1 (self): # override method1
:
Class1.method1(self) # call my super-class method
:
def method2 (self):
:
class Class3(Class2):
def method1 (self): # override method1
:
Class2.method1(self) # call my super-class method
:
def method3 (self):
:
请注意,辅助方法定义(在Class2
和Class3
)将覆盖超类定义。 不需要子类方法调用其超类方法; 但是,如果没有,则必须完全替换超类方法的功能。
调用方法有两种语法(假设您有一个由变量mci
引用的MyClass
实例):
mci.someMethod(...)
MyClass.someMethod(mci, ...)
第一种形式通常用于类客户端编码中,而第二种形式通常用于子类中以调用超类方法。
在本节中,我们将从Jython中的面向对象编程的概念概述转到更高级的讨论,其中包含诸如运算符重载,特殊属性和自省之类的主题。
Jython类为几种特殊属性提供支持。 最重要的信息如下所示:
名称 | 角色 | 注释) |
---|---|---|
__dict__ |
对象的可写属性 | 可用于内省对象的属性 |
__class__ |
对象的类别 | 访问对象的类(类似于Java编码中的x.getClass() ) |
__bases__ |
对象的直接超类的元组 | 可用于内省对象的超类 |
与大多数其他语言不同,在Jython中,您可以更改现有实例的类。 这样做会将您可以在实例上使用的方法更改为新类的方法,但不会更改其任何现有字段。 例如,要更改实例的类,请将新类分配给__class__
特殊属性(请参见特殊属性 ),如下所示:
x = SomeClass()
print isinstance(x, SomeClass) # prints: 1 (true)
print isinstance(x, SomeOtherClass) # prints: 0 (false)
:
# change the class (that is, the type) of the instance here
x.__class__ = SomeOtherClass
print isinstance(x, SomeClass) # prints: 0 (false)
print isinstance(x, SomeOtherClass) # prints: 1 (true)
y = SomeOtherClass()
print x.__class__ == y.__class__ # prints: 1 (true)
进行此更改之后, x实例将支持SomeOtherClass
的方法,而不是以前的SomeClass
。 更改对象的类时,请注意实例具有新类的正确属性。
这是一个使用特殊属性的实际示例(请参阅特殊属性 )。 模块printclass.py
可以内省类和实例以显示其属性和方法。 稍后再讨论内省,或者您可以查看内省 。 您还可以查看String操作和函数以及附录K:内置函数,以了解有关下面使用的函数的更多信息。 现在,仅关注callable
函数, vars
函数(隐式使用__dict__
属性)和__bases__
属性的使用。
__any__ = ['getMembers', 'printObject']
def addMember (list, item):
if not item in list:
list.append(item)
def getMembers (obj, memtype="attrs"):
""" Get all the members (of memtype) of the object. """
members = []
for name, value in vars(obj).items():
try:
item = obj.__name__, name, value
except:
item = "", name, value
if memtype.lower().startswith("attr"):
if not callable(value):
addMember(members, item)
elif memtype.lower().startswith("meth"):
if callable(value):
addMember(members, item)
elif memtype.lower() == "all":
addMember(members, item)
try:
for base in obj.__bases__:
members.extend(getMembers(base, memtype))
except:
pass
return members
import sys
def printObject (obj, stream=sys.stdout):
""" Print all the members of the object. """
members = getMembers(obj, "attrs")
members.sort()
print >>stream, "Attributes:"
for objname, memname, value in members:
print >>stream, " %s.%s" % (objname, memname)
members = getMembers(obj, "methods")
members.sort()
print >>stream, "Methods:"
for objname, memname, value in members:
print >>stream, " %s.%s" % (objname, memname)
下面的代码使用上一部分中的函数来内省UserList
类。 有关UserList
类的定义,请参见运算符重载 。
if __name__ == "__main__":
from UserList import UserList
class MyClass(UserList):
def __init__ (self, x, y):
UserList.__init__(self)
self.__x = x
self.__y = y
def method1 (self):
return self.x + self.y
def method2 (self, x, y):
return self.x + self.y + x + y
print "For class:", `MyClass`
printObject(MyClass)
print
aMyClass = MyClass(1, 2)
aMyClass.extend([1,2,3,4])
print "For instance:", `aMyClass`
printObject(aMyClass)
以下输出(重新格式化为多列以节省空间)是上述模块中运行主代码的结果。 请注意,私有字段和方法(请参阅隐藏变量 )具有错误的名称。
For class:
Attributes: Methods:
MyClass.__doc__ MyClass.__init__ UserList.__len__
MyClass.__module__ MyClass.method1 UserList.__lt__
UserList.__doc__ MyClass.method2 UserList.__mul__
UserList.__module__ UserList._UserList__cast UserList.__ne__
UserList.__add__ UserList.__radd__
UserList.__cmp__ UserList.__repr__
UserList.__contains__ UserList.__rmul__
UserList.__delitem__ UserList.__setitem__
UserList.__delslice__ UserList.__setslice__
UserList.__eq__ UserList.append
UserList.__ge__ UserList.count
UserList.__getitem__ UserList.extend
UserList.__getslice__ UserList.index
UserList.__gt__ UserList.insert
UserList.__iadd__ UserList.pop
UserList.__imul__ UserList.remove
UserList.__init__ UserList.reverse
UserList.__le__ UserList.sort
For instance: [1, 2, 3, 4]
Attributes:
._MyClass__x
._MyClass__y
.data
Methods:
请注意,方法和类属性与类一起驻留,实例属性与实例一起驻留。 但是,所有类的方法都可以应用于每个实例。
您通常需要在运行时确定对象的特征。 我们称这是对对象的自省 。 Java平台通过java.lang.Class
类和java.lang.reflect
包中的类提供自省服务。 尽管功能强大,但这些API还是有些难以使用。 您可能已经怀疑过,Jython提供了一种更简单的内省方法。
在Jython中,我们可以使用dir
和vars
函数检查任何对象的绑定,例如模块,函数,类,序列,映射等。 为了更好地了解其工作原理,请考虑以下示例。 输出已插入到以“ ...”为前缀的print
语句之后(并重新格式化),以便于阅读。 dir
函数仅返回绑定名称,而vars
函数返回名称和值; 因此,当两个函数返回相同的名称时,我们只需要使用vars
函数,如下所示:
#-- empty start --
print "vars:", vars()
...vars: {'__doc__': None, '__name__': '__main__'}
x = 1
y = 2
z = 3
l = [x, y, z]
d = {x:"xxxx", y:"yyyy", z:"zzzz"}
#-- locals variables --
print x, y, z, l, d
...1 2 3 [1, 2, 3] {3: 'zzzz', 2: 'yyyy', 1: 'xxxx'}
#-- plus locals variables --
print "vars:", vars()
...vars: {'__name__': '__main__', 'x': 1, \
... 'd': {3: 'zzzz', 2: 'yyyy', 1: 'xxxx'}, '__doc__': None, \
... 'y': 2, 'l': [1, 2, 3], 'z': 3}
import sys
#-- plus import --
print "vars:", vars()
...vars: {'__name__': '__main__', 'z': 3, 'l': [1, 2, 3], \
... '__doc__': None, 'y': 2, 'x': 1, 'sys': sys module, \
... 'd': {3: 'zzzz', 2: 'yyyy', 1: 'xxxx'}}
#-- sys import --
print "vars sys:", vars(sys)
...vars sys: {'classLoader': \
... ,
... ... many values removed ...,
... 'warnoptions': }
del x, y, z
#-- post delete --
print "vars:", vars()
...vars: {'__name__': '__main__', 'l': [1, 2, 3], '__doc__': None, \
... 'sys': sys module, 'd': {3: 'zzzz', 2: 'yyyy', 1: 'xxxx'}}
def func (x, y):
return x, y
class MyClass ():
def __init__ (self, x, y):
self.__x = x
self.__y = y
def method1 (self):
return self.x + self.y
def method2 (self, x, y):
return self.x + self.y + x + y
#-- plus function and class --
print "vars:", vars()
....vars: {'func': , '__name__': '__main__', \
... 'l': [1, 2, 3], '__doc__': None, \
.... 'MyClass': , \
... 'sys': sys module, 'd': {3: 'zzzz', 2: 'yyyy', 1: 'xxxx'}}
#-- function --
print "dir: ", dir(func) # **** dir and vars different here ****
print "vars:", vars(func)
...dir: ['__dict__', '__doc__', '__name__', 'func_closure', \
... 'func_code', 'func_defaults', 'func_doc', 'func_globals', 'func_name']
...vars: None
#-- class --
print "vars:", vars(MyClass)
...vars: {'__doc__': None, '__init__': , \
... 'method2': , '__module__': '__main__', \
... 'method1': }
myclass = MyClass(1, 2)
#-- instance --
print "myclass:", myclass
print "vars:", vars(myclass)
...myclass: <__main__.MyClass instance at 19014134>
...vars: {'_MyClass__y': 2, '_MyClass__x': 1}
请注意, dir(x)
通常等效于x.__dict__.keys()
而vars(x)
通常等效于x.__dict__
。
特殊属性中描述的属性允许对类进行其他自省。 特别是,您可以使用__dict__
属性来确定类中的方法和实例中的字段。
除了dir
和vars
,Jython还提供了更多用于自省类和实例的函数,如下所示:
功能 | 注释) |
---|---|
hasattr(obj, name) |
测试以查看命名属性是否存在 |
getattr(obj, name {, default}) |
获取命名属性(如果存在); 否则,返回默认值(如果没有提供默认值,则引发异常) |
setattr(obj, name, value) |
设置命名属性的值 |
delattr(obj, name) |
删除命名属性 |
请参阅附录K:内置功能以了解有关这些功能的更多信息。
抽象类是其中某些或所有方法均缺失或定义不完整的类。 必须创建一个子类来提供或完成这些方法定义。 具体的类不是抽象的(也就是说,所有方法都是完整的)。 到目前为止,我们仅与具体的类一起工作。 创建抽象类以促进重用。 它们提供了设计的部分实现,您可以通过将其子类化来完成或扩展设计。
为了更好地了解其工作原理,我们将创建一个简单的抽象命令框架,该框架支持命令do
, undo
和redo
操作。 命令在(子)类中定义,可以通过创建新的do_...
和undo_...
方法轻松添加。 如前几节所述,我们通过自省访问了这些方法。
这是示例抽象命令框架:
class CommandProcessor: # an abstract class
""" Process Commands. """
def __init__ (self):
self.__history = []
self.__redo = []
def execute (self, cmdName, *args):
""" Do some command """
self.__history.append( (cmdName, args) )
processor = getattr(self, "do_%s" % cmdName, None)
if processor:
return processor(*args)
else:
raise NameError, "cannot find do_%s" % cmdName
def undo (self, count=1):
""" Undo some (or all) commands in LIFO order """
self.__redo = []
while count > 0 and len(self.__history) > 0:
cmdName, args = self.__history.pop()
count -= 1
processor = getattr(self, "undo_%s" % cmdName, None)
if processor:
self.__redo.append( (cmdName, args) )
processor(*args)
else:
raise NameError, "cannot find undo_%s" % cmdName
def redo (self, count=1):
""" Redo some (or all) undone commands """
while count > 0 and len(self.__redo) > 0:
cmdName, args = self.__redo.pop()
count -= 1
processor = getattr(self, "do_%s" % cmdName, None)
if processor:
processor(*args)
else:
raise NameError, "cannot find do_%s" % cmdName
注意:此示例基于Samuele Pedroni和Noel Rappin的Jython Essentials中的代码( 有关更多信息,请参见参考资料)。
这是示例抽象命令框架的测试用例:
class MyProcessor (CommandProcessor): # a concrete subclass
def __init__ (self):
CommandProcessor.__init__(self)
def do_Cmd1 (self, args):
print "Do Command 1:", args
def do_Cmd2 (self, args):
print "Do Command 2:", args
def do_Cmd3 (self, args):
print "Do Command 3:", args
def undo_Cmd1 (self, args):
print "Undo Command 1:", args
def undo_Cmd2 (self, args):
print "Undo Command 2:", args
def undo_Cmd3 (self, args):
print "Undo Command 3:", args
mp = MyProcessor()
print "execute:" ; mp.execute("Cmd1", None)
print "execute:" ; mp.execute("Cmd2", (1,2,3))
print "execute:" ; mp.execute("Cmd3", "Hello")
print "undo: " ; mp.undo(2)
print "redo: " ; mp.redo(2)
print "execute:", ;mp.execute("BadCmd", "Hello")
具有给定测试用例的框架将产生以下输出:
execute:
Do Command 1: None
execute:
Do Command 2: (1, 2, 3)
execute:
Do Command 3: Hello
undo:
Undo Command 3: Hello
Undo Command 2: (1, 2, 3)
redo:
Do Command 2: (1, 2, 3)
Do Command 3: Hello
execute:
Traceback (innermost last):
File "cmdproc.py", line 63, in ?
File "cmdproc.py", line 15, in execute
NameError: cannot find do_BadCmd
与C ++一样,但与Java语言不同,Jython允许许多标准语言运算符被类重载。 这意味着类可以为语言运算符定义特定的含义。 Jython还允许类模拟内置类型,例如数字,序列和映射。 要了解有关仿真的更多信息,请参见附录B:常见的重载运算符和方法 。
在下面的示例中,我们将使用标准的Jython UserList
类定义来展示实际中的运算符重载的示例。 UserList
是包装列表并像列表一样工作的类。 它的大部分功能都被委托 (传递给)了它所包含的列表,称为data
。 在更现实的示例中,将实现这些重载功能以访问某些其他存储,例如磁盘文件或数据库。
class UserList:
def __init__(self, initlist=None):
self.data = []
if initlist is not None:
if type(initlist) == type(self.data):
self.data[:] = initlist
elif isinstance(initlist, UserList):
self.data[:] = initlist.data[:]
else:
self.data = list(initlist)
def __cast(self, other):
if isinstance(other, UserList): return other.data
else: return other
# `self`, repr(self)
def __repr__(self): return repr(self.data)
# self < other
def __lt__(self, other): return self.data < self.__cast(other)
# self <= other
def __le__(self, other): return self.data <= self.__cast(other)
# self == other
def __eq__(self, other): return self.data == self.__cast(other)
# self != other, self <> other
def __ne__(self, other): return self.data != self.__cast(other)
# self > other
def __gt__(self, other): return self.data > self.__cast(other)
# self >= other
def __ge__(self, other): return self.data >= self.__cast(other)
# cmp(self, other)
def __cmp__(self, other):
raise RuntimeError, "UserList.__cmp__() is obsolete"
# item in self
def __contains__(self, item): return item in self.data
# len(self)
def __len__(self): return len(self.data)
# self[i]
def __getitem__(self, i): return self.data[i]
# self[i] = item
def __setitem__(self, i, item): self.data[i] = item
# del self[i]
def __delitem__(self, i): del self.data[i]
# self[i:j]
def __getslice__(self, i, j):
i = max(i, 0); j = max(j, 0)
return self.__class__(self.data[i:j])
# self[i:j] = other
def __setslice__(self, i, j, other):
i = max(i, 0); j = max(j, 0)
if isinstance(other, UserList):
self.data[i:j] = other.data
elif isinstance(other, type(self.data)):
self.data[i:j] = other
else:
self.data[i:j] = list(other)
# del self[i:j]
def __delslice__(self, i, j):
i = max(i, 0); j = max(j, 0)
del self.data[i:j]
# self + other (join)
def __add__(self, other):
if isinstance(other, UserList):
return self.__class__(self.data + other.data)
elif isinstance(other, type(self.data)):
return self.__class__(self.data + other)
else:
return self.__class__(self.data + list(other))
# other + self (join)
def __radd__(self, other):
if isinstance(other, UserList):
return self.__class__(other.data + self.data)
elif isinstance(other, type(self.data)):
return self.__class__(other + self.data)
else:
return self.__class__(list(other) + self.data)
# self += other (join)
def __iadd__(self, other):
if isinstance(other, UserList):
self.data += other.data
elif isinstance(other, type(self.data)):
self.data += other
else:
self.data += list(other)
return self
# self * other (repeat)
def __mul__(self, n):
return self.__class__(self.data*n)
__rmul__ = __mul__
# self *= other (repeat)
def __imul__(self, n):
self.data *= n
return self
# implement "List" functions below:
def append(self, item): self.data.append(item)
def insert(self, i, item): self.data.insert(i, item)
def pop(self, i=-1): return self.data.pop(i)
def remove(self, item): self.data.remove(item)
def count(self, item): return self.data.count(item)
def index(self, item): return self.data.index(item)
def reverse(self): self.data.reverse()
def sort(self, *args): apply(self.data.sort, args)
def extend(self, other):
if isinstance(other, UserList):
self.data.extend(other.data)
else:
self.data.extend(other)
像函数一样,类可以嵌套。 Jython中的嵌套类的工作方式类似于Java语言中的静态内部类。 这是一个例子:
class MyDataWrapper:
class Data: pass # inner data structure class
def __init__ (self):
self.data = Data()
def set (self, name, value):
setattr(self.data, name, value)
def get (self, name, default=None):
return getattr(self.data, name, default)
与任何编程语言一样,Jython支持使用print
语句进行调试。 要实现此调试解决方案,我们只需在程序中添加一条print
语句,运行该程序,然后检查生成的输出以查找错误的线索。 尽管非常基础,但在许多情况下,此调试解决方案还是完全令人满意的。
这是用于调试的示例print
语句。
:
def myFunc(x):
print "x at entry:", x
:
print "x at exit:", x
return x
:
z = myFunc(20)
对于print
statement解决方案不足以满足调试需求的时代,Jython提供了一个简单的命令行调试器,类似于Java平台的jdb
调试器。 Jython调试器完全用Jython编写,因此可以轻松检查或扩展。 此外,Jython提供了一组抽象的基本调试类,以允许在此框架上构建其他调试器,例如GUI调试器。
要启动调试器,请运行以下命令:
c:\>jython c:\jython-2.1\lib\pdb.py .py
在调试器提示“(Pdb)”之后输入调试器命令。 这是一个使用factor.py
模块的示例调试会话(请参见阶乘引擎:factor.py ):
C:\Articles>jython \jython-2.1\lib\pdb.py factor.py
> C:\Articles\(0)?()
(Pdb) step
> C:\Articles\(1)?()
(Pdb) step
> C:\Articles\factor.py(0)?()
(Pdb) list 67
62 try:
63 print "For", value, "result =",
fac.calculate(value)
64 except ValueError, e:
65 print "Exception -", e
66
67 doFac(-1)
68 doFac(0)
69 doFac(1)
70 doFac(10)
71 doFac(100)
72 doFac(1000)
(Pdb) tbreak 67
Breakpoint 1 at C:\Articles\factor.py:67
(Pdb) continue
factor.py running...
Deleted breakpoint 1
> C:\Articles\factor.py(67)?()
-> doFac(-1)
(Pdb) next
For -1 result = Exception - only positive integers supported: -1
> C:\Articles\factor.py(68)?()
-> doFac(0)
(Pdb) next
For 0 result = 1
> C:\Articles\factor.py(69)?()
-> doFac(1)
(Pdb) next
For 1 result = 1
> C:\Articles\factor.py(70)?()
-> doFac(10)
(Pdb) next
For 10 result = 3628800
> C:\Articles\factor.py(71)?()
-> doFac(100)
(Pdb) next
For 100 result =
93326215443944152681699238856266700490715968264381621468592963895217599
99322991560894146397615651828625
3697920827223758251185210916864000000000000000000000000
> C:\Articles\factor.py(72)?()
-> doFac(1000)
(Pdb) next
For 1000 result = 402387260077 ... many other digits deleted ...
0000000000000000000000
--Return--
> C:\Articles\factor.py(72)?()->None
-> doFac(1000)
(Pdb) next
--Return--
> C:\Articles\(1)?()->None
(Pdb) next
C:\Articles>
要了解有关使用Jython调试器进行调试的更多信息,请参见附录C:Jython调试器命令 。
有时您可能会注意到Jython程序的运行时间超出了预期。 您可以使用Jython探查器来找出程序中哪些部分花费的时间最长并对其进行优化。 探查器可让您探查整个程序或单个功能。
这是一个示例运行, factor.py
程序进行了概要分析(请参阅阶乘引擎:factor.py ):
c:\>jython \jython-2.1\lib\profile.py \articles\factor.py
\articles\factor.py running...
For -1 result = Exception - only positive integers supported: -1
For 0 result = 1
For 1 result = 1
For 10 result = 3628800
For 100 result =
93326215443944152681699238856266700490715968264381621468592963895217599
99322991560894146397615651828625369792082722375825118521091686400000000
0000000000000000
For 1000 result = 402387260077 ... many other digits deleted ...
0000000000000000000000
237 function calls (232 primitive calls) in 0.250 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.130 0.130 0.240 0.240 :0(?)
1 0.000 0.000 0.110 0.110 factor.py:0(?)
220 0.010 0.000 0.010 0.000 \
factor.py:27(fireListeners)
6 0.060 0.010 0.070 0.012 factor.py:34(calculate)
1 0.000 0.000 0.000 0.000 factor.py:5(Factorial)
1 0.000 0.000 0.000 0.000 factor.py:6(__init__)
6/1 0.040 0.007 0.110 0.110 factor.py:61(doFac)
1 0.010 0.010 0.250 0.250 \
profile:0(execfile('\\articles\\factor.py'))
0 0.000 0.000 profile:0(profiler)
从此运行中,您可以看到(除了初始启动代码之外)大部分的编程时间都由calculate
函数使用。 有关分析的详细信息,请参阅Jython的Python的参考手册 ,提供相关信息 。
与C和Java语言(从1.4版开始)一样,Jython支持断言。 断言是程序正确运行所必须满足的条件; 如果它们不是真的,则程序可能无法正常运行。 通常,它们用于验证功能的输入值。 Jython对断言的支持来自以下assert
语句的形式:
assert expression {, message}
请注意, expression
是任何Jython表达式; 如果为false,则会引发exceptions.AssertionError
异常。 如果message
提供,它成为与异常关联的消息。 例如:
:
def myFunc(x):
assert x >= 0, "argument %r must be >= 0" % x
return fac(x)
:
z = myFunc(20) # no exception raised
z = myFunc(-1) # AssertionError raised
Jython最强大的功能之一是它与Java代码交互的能力。 Jython程序可以创建任何Java类的实例,并可以在任何Java实例上调用任何方法。 Jython还可以继承Java类,从而允许Java代码调用Jython代码。 Jython通过强大而透明地使用Java Reflection API(软件包java.lang.reflect
),使调用Java方法非常容易。
要完成本教程的这一部分,您需要熟悉Java语言并选择Java运行时API。 您应该了解Java平台上面向对象编程的基本概念,并熟悉Java数据类型,类,线程以及java.lang
, java.util
, java.io
和javax.swing
包。
注意:由于反射API已在1.4版中进行了高度优化,因此Jython在Java 1.4版及更高版本上的运行速度要快得多。
如Inheritance中所示,Jython类可以继承 Java类。 子类化使扩展Java类(例如GUI组件)变得非常容易。 这允许Java代码调用Jython代码,而无需意识到它是Jython代码。 还可以在其他Java代码使用的Jython类中实现,如以下示例所示:
from java import util
class MyArray(util.ArrayList): # subclass a Java class
:
def get (self, index): # override the get method
"@sig public java.lang.Object get(int index)"
if 0 <= index < self.size:
return util.ArrayList.get(self, index)
return None # OutOfBounds now returns null
在由jythonc
编译之后,可以在可以使用java.util.ArrayList
实例的任何地方的Java代码中使用以上类。 请注意,在调用超类方法时,将self值作为参数传递。
除了子类化Java类之外,还可以直接在Jython中访问Java类。 例如,此代码序列:
from java.util import Date
:
d = Date() # now
print d, d.time, d.getTime()
将产生以下输出:
Tue Dec 02 14:44:02 CST 2003 1070397842496 1070397842496
在从Jython调用Java类的示例中,您可能已经注意到表达式d.time
和d.getTime()
产生相同的结果。 这是因为他们做同样的事情。 Jython具有一个非常方便的功能,使JavaBean属性显示为Jython属性。 JavaBean属性是通过(通常)匹配以下形式的Java方法对来定义的,其中
是属性的类型,而
是属性的名称:
get()
-- and --
void set( value)
例如,Java方法long getTime() { ... }
和void setTime(long t) { ... }
定义了long
属性time 。 因此,Jython引用d.time
会自动动态地转换为Java表达式d.getTime()
。
Jython还可以设置属性,因此允许d.time = 1000000L
。 Jython参考d.time = value
会自动动态地转换为Java表达式d.setTime( value )
。 应用此更改后, 来自Jython的Calling Java类的print语句将导致以下结果:
Wed Dec 31 18:01:40 CST 1969 100000 100000
在Java对象上调用方法非常容易。 就像它们是Jython方法一样调用它们。 Jython自动在Jython和Java类型之间来回映射参数和返回值。 例如,以下是Jython的简短序列,该序列广泛使用Java类和方法:
1: from javax import swing
2: import sys
3:
4: f = swing.JFrame(sys.argv[1], size=(200,200),
5: defaultCloseOperation=swing.JFrame.EXIT_ON_CLOSE)
6: f.contentPane.add(swing.JLabel(sys.argv[2]))
7: f.visible = 1
此代码序列创建并显示一个GUI框架窗口。 脚本的第一个命令行参数将成为标题,第二个将成为内容文本。 第4行创建框架,并传递标题,所需的大小和关闭动作。 size
和defaultCloseOperation
参数是如上所述的属性,因此,从Jython程序调用时,可以(非常方便地)在JFrame
的构造函数中进行设置。 标题设置为JFrame
等效于__init__
方法的参数。 第6行访问JFrame
的contentPane
属性,并调用其add
方法添加一个JLabel
以显示第二个参数。 第7行通过将其visible
属性设置为1
(true)使该框架可见。
该GUI的示例如下所示:
如从Java代码调用Jython中所示,当在可以从Java语言调用的类中重写Java方法时,您需要提供签名信息。 这是通过文档注释完成的。 注释的第一行(以"@sig"
开头)用作jythonc
程序的指令(在第1部分中进行了讨论),以生成与Java兼容的方法签名。 例如,以下注释描述了使用Java语言的声明语法的get
方法。 在签名中,类型必须完全合格。
"@sig public java.lang.Object get(int index)"
Jython不支持重载方法,重载方法是名称相同但参数数量和/或类型不同的方法。 相反,Jython支持默认参数和可变数量的参数,如果您从使用重载的Java类继承而要覆盖重载的方法,则可能会产生问题。 在Jython中,您必须定义基本方法并接受不同数量的参数。 考虑一个总是返回一个空白的InputStream
的(不太实际)示例:
from java import io
class AlwaysBlank(io.InputStream):
# covers all forms of read(...)
def read(self, *args):
if len(args) > 0:
# covers forms: int read(byte[])
# int read(byte[], int off, int len)
return apply(io.InputStream.read, (self,) + args)
else:
# covers form: int read()
return ord(' ')
该代码基于Jython主页上的示例。
Jython支持Java样式的数组对象的创建。 数组主要用于将数组传递给Java方法以及从Java方法返回数组,但是它们是通用的,可以在纯Jython代码中使用。 数组元素是使用Java基本类型和类类型键入的。 数组的行为很像Jython列表,但是它们不能更改长度。
jarray
模块提供了阵列支持。 jarray
模块中的两个函数zeros
和array
用于创建数组。 数组函数将Jython序列映射到Java数组。 一些示例如下:
from jarray import zeros, array
from java import util
from javax import swing
a1 = zeros(100, 'i') # an array of 100 int 0s
a2 = array([1,2,10,-5,7], 'i') # an array of ints as listed
# an array of doubles 0.0 to 49.0
a3 = array([i * 1.0 for i in range(50)], 'd')
a4 = zeros(10, util.Map) # an array of 10 null Maps
a5 = array((swing.JFrame("F1"), # an array of 3 JFrames
swing.JFrame("F2"),
swing.JFrame("F3")), swing.JFrame)
a6 = array("Hello", 'c') # an array of characters
有关数组类型的字符代码的列表,请参见附录A:数组类型的字符代码。
Java运行时广泛使用线程,线程用于处理GUI事件,执行异步I / O,实现异步处理等。
这很容易用Jython创建Java线程:只要创建的实例java.lang.Thread
和小类java.lang.Runnable
。 有关示例,请参见GUI:fgui.py 。 您还可以使用以下形式的thread
模块和函数从Jython函数中创建线程:
start_new_thread(function, args)
-- and --
exit()
start_new_thread
函数在新的Java线程中运行function
参数,并将args元组值传递给该函数。 可以在线程中使用exit
函数结束它(通常作为if
语句的目标)。
使用Java或Jython线程开发多线程程序时,有时必须创建同步函数(或方法)。 同步函数是一次只能从一个线程调用的函数; 这意味着在第一个线程退出之前,其他线程将无法进入该函数。 Jython提供了synchronized
模块和两个函数来创建同步函数。 这些函数具有以下形式:
make_synchronized(function)
-- and --
apply_synchronized(syncobj, function, pargs {, kwargs})
make_synchronized
函数永久同步该function
参数。 apply_synchronized
函数会在syncobj
上暂时进行同步,然后调用该function
参数。
使用make_synchronized
来通知事件非常简单,如下所示:
from synchronize import *
from java import lang
# define synchronization helpers
def waitForSignal (monitor):
""" Wait until the monitor is signaled. """
lang.Object.wait(monitor)
# replace with synchronized version; syncs on 1st argument
waitForSignal = make_synchronized(waitForSignal)
def notifySignal (monitor):
""" Signal monitor. """
lang.Object.notifyAll(monitor)
# replace with synchronized version; syncs on 1st argument
notifySignal = make_synchronized(notifySignal)
class Gui: # GUI support
:
def doExit (self):
self.visible = 0
notifySignal(self)
if __name__ == "__main__": # main code
:
gui = Gui()
:
print "Waiting until GUI exit requested..."
waitForSignal(gui)
print "Done"
这是使用Jython线程的示例。 该示例显示了一组生产者和使用者线程共享对公共缓冲区的访问。 我们将从共享缓冲区的定义开始,如下所示。
""" A Jython Thread Example. """
from java import lang
from synchronize import *
from thread import start_new_thread
from sys import stdout
def __waitForSignal (monitor):
apply_synchronized(monitor, lang.Object.wait, (monitor,))
def __signal (monitor):
apply_synchronized(monitor, lang.Object.notifyAll, (monitor,))
def __xprint (stream, msg):
print >>stream, msg
def xprint (msg, stream=stdout):
""" Synchronized print. """
apply_synchronized(stream, __xprint, (stream, msg))
class Buffer:
""" A thread-safe buffer. """
def __init__ (self, limit=-1):
self.__limit = limit # the max size of the buffer
self.__data = []
self.__added = () # used to signal data added
self.__removed = () # used to signal data removed
def __str__ (self):
return "Buffer(%s,%i)" % (self.__data, self.__limit)
def __len__ (self):
return len(self.__data)
def add (self, item):
""" Add an item. Wait if full. """
if self.__limit >= 0:
while len(self.__data) > self.__limit:
__waitForSignal(self.__removed)
self.__data.append(item);
xprint("Added: %s" % item)
__signal(self.__added)
def __get (self):
item = self.__data.pop(0)
__signal(self.__removed)
return item
def get (self, wait=1):
""" Remove an item. Wait if empty. """
item = None
if wait:
while len(self.__data) == 0:
__waitForSignal(self.__added)
item = self.__get()
else:
if len(self.__data) > 0: item = self.__get()
xprint("Removed: %s" % item)
return item
get = make_synchronized(get)
该示例的下一步是查看生产者和消费者定义,如下所示:
class Producer:
def __init__ (self, name, buffer):
self.__name = name
self.__buffer = buffer
def __add (self, item):
self.__buffer.add(item)
def __produce (self, *args):
for item in args:
self.__add(item)
def produce (self, items):
start_new_thread(self.__produce, tuple(items))
class Consumer:
def __init__ (self, name, buffer):
self.__name = name
self.__buffer = buffer
def __remove (self):
item = self.__buffer.get()
return item
def __consume (self, count):
for i in range(count):
self.__remove()
def consume (self, count=1):
start_new_thread(self.__consume, (count,))
最后,这是示例代码的试运行:
# all producers and consumer share this one
buf = Buffer(5)
p1 = Producer("P1", buf)
p2 = Producer("P2", buf)
p3 = Producer("P3", buf)
p4 = Producer("P4", buf)
c1 = Consumer("C1", buf)
c2 = Consumer("C2", buf)
# create 6 items
p1.produce(["P1 Message " + str(i) for i in range(3)])
p2.produce(["P2 Message " + str(i) for i in range(3)])
# consume 20 items
for i in range(5):
c1.consume(2)
c2.consume(2)
# create 20 more items
p3.produce(["P3 Message " + str(i) for i in range(10)])
p4.produce(["P4 Message " + str(i) for i in range(10)])
# consume 4 items
c1.consume(2)
c2.consume(2)
# let other threads run
lang.Thread.currentThread().sleep(5000)
xprint("Buffer has %i item(s)left" % len(buf))
生产者使用者示例产生以下结果(包装为两列以节省空间):
Added: P1 Message 0 Added: P3 Message 7
Added: P1 Message 1 Removed: P3 Message 7
Added: P1 Message 2 Added: P3 Message 8
Added: P2 Message 0 Removed: P3 Message 8
Added: P2 Message 1 Added: P3 Message 9
Added: P2 Message 2 Removed: P3 Message 9
Removed: P1 Message 0 Added: P4 Message 0
Removed: P1 Message 1 Removed: P4 Message 0
Removed: P1 Message 2 Added: P4 Message 1
Removed: P2 Message 0 Removed: P4 Message 1
Removed: P2 Message 1 Added: P4 Message 2
Removed: P2 Message 2 Removed: P4 Message 2
Added: P3 Message 0 Added: P4 Message 3
Removed: P3 Message 0 Removed: P4 Message 3
Added: P3 Message 1 Added: P4 Message 4
Removed: P3 Message 1 Added: P4 Message 5
Added: P3 Message 2 Added: P4 Message 6
Removed: P3 Message 2 Added: P4 Message 7
Added: P3 Message 3 Added: P4 Message 8
Removed: P3 Message 3 Added: P4 Message 9
Added: P3 Message 4 Removed: P4 Message 4
Removed: P3 Message 4 Removed: P4 Message 5
Added: P3 Message 5 Removed: P4 Message 6
Removed: P3 Message 5 Removed: P4 Message 7
Added: P3 Message 6 Buffer has 2 item(s)left
Removed: P3 Message 6
通常,您将需要在Jython代码中使用Java服务。 在这些情况下,您可以在每次需要使用给定服务时公开地进行操作,也可以将Java服务包装在Jython库函数中,然后在Jython代码中使用该函数。
推荐使用第二个选项,因为它封装并抽象了Java代码。
作为如何将Java服务包装在Jython库函数中的示例,我们将看一下JavaUtils.py
模块摘录。 下面的代码介绍了JavaUtils
模块。 请参阅本教程的第1部分 ,以刷新有关模块的信息。
""" This module defines several functions to ease interfacing with Java code."""
from types import *
from java import lang
from java import util
from java import io
# only expose these
__all__ = ['loadProperties', 'getProperty',
'mapToJava', 'mapFromJava', 'parseArgs']
您通常将需要访问Java属性文件以获取配置信息。 Jython允许您loadProperties
使用loadProperties
和getProperty
函数,如下所示:
def loadProperties (source):
""" Load a Java properties file into a Dictionary. """
result = {}
if type(source) == type(''): # name provided, use file
source = io.FileInputStream(source)
bis = io.BufferedInputStream(source)
props = util.Properties()
props.load(bis)
bis.close()
for key in props.keySet().iterator():
result[key] = props.get(key)
return result
def getProperty (properties, name, default=None):
""" Gets a property. """
return properties.get(name, default)
因此,例如,如果您要使用访问Java属性文件中的功能,如下所示
import sys
file = sys.argv[1]
props = loadProperties(file)
print "Properties file: %s, contents:" % file
print props
print "Property %s = %i" % ('debug', int(getProperty(props, 'debug', '0')))
具有的属性文件内容
# This is a test properties file
debug = 1
error.level = ERROR
now.is.the.time = false
那么结果输出将是:
Properties file: test.properties, contents:
{'error.level': 'ERROR', 'debug': '1', 'now.is.the.time': 'false'}
Property debug = 1
有时您需要在Jython中创建纯Java对象(例如,当您需要创建对象以通过网络发送到基于Java的服务器时,或者当您需要将该对象传递给类型敏感的Java服务时,例如以及Swing表单元格值)。 要将Jython内置类型转换为Java类型(反之亦然),请使用以下示例中的功能( Jython中包装Java服务的JavaUtils.py
模块摘录的延续):
def mapMapFromJava (map):
""" Convert a Map to a Dictionary. """
result = {}
iter = map.keySet().iterator()
while iter.hasNext():
key = iter.next()
result[mapFromJava(key)] = mapFromJava(map.get(key))
return result
def mapCollectionFromJava (coll):
""" Convert a Collection to a List. """
result = []
iter = coll.iterator();
while iter.hasNext():
result.append(mapFromJava(iter.next()))
return result
def mapFromJava (object):
""" Convert a Java type to a Jython type. """
if object is None: return object
if isinstance(object, util.Map):
result = mapMapFromJava(object)
elif isinstance(object, util.Collection):
result = mapCollectionFromJava(object)
else:
result = object
return result
def mapSeqToJava (seq):
""" Convert a sequence to a Java ArrayList. """
result = util.ArrayList(len(seq))
for e in seq:
result.add(mapToJava(e));
return result
def mapDictToJava (dict):
""" Convert a Dictionary to a Java HashMap. """
result = util.HashMap()
for key, value in dict.items():
result.put(mapToJava(key), mapToJava(value))
return result
def mapToJava (object):
""" Convert a Jython type to a Java type. """
if object is None: return object
t = type(object)
if t == TupleType or t == ListType:
result = mapSeqToJava(object)
elif t == DictType:
result = mapDictToJava(object)
else:
result = object
return result
使用mapToJava
,可以将这些类型写入java.io.ObjectOutputStream
。 从java.io.ObjectInputStream
读取对象后,可以使用mapFromJava
将对象转换回Jython类型。
请注意,这些方法支持一组有限但广泛使用的内置Jython类型。 Jython自动转换类似数值的类型,例如数字和字符串。 不支持用户定义的类。
为了继续该示例,将在上一节中讨论映射功能的以下用法,如下所示:
data = (1,2,3, [1,2,3], [c for c in "Hello!"], "Hello!", {1:'one', 2:'two'})
print "data:", data
toJava = mapToJava(data)
print "toJava:", toJava
fromJava = mapFromJava(toJava)
print "fromJava:", fromJava
print
print "type(%s)=%s" % ("data", type(data))
print "type(%s)=%s" % ("toJava", type(toJava))
print "type(%s)=%s" % ("fromJava", type(fromJava))
印刷品:
data: (1, 2, 3, [1, 2, 3], ['H', 'e', 'l', 'l', 'o', '!'], 'Hello!', \
{2: 'two', 1: 'one'})
toJava: [1, 2, 3, [1, 2, 3], [H, e, l, l, o, !], Hello!, {2=two, 1=one}]
fromJava: [1, 2, 3, [1, 2, 3], ['H', 'e', 'l', 'l', 'o', '!'], 'Hello!', \
{2: 'two', 1: 'one'}]
type(data)=org.python.core.PyTuple
type(toJava)=org.python.core.PyJavaInstance
type(fromJava)=org.python.core.PyList
注意, PyTuple
变成了PyJavaInstance
,然后变成了PyList
。 还要注意, toJava
表单的格式不同。 这是因为它是一个Java对象,是由Java toString()
方法而不是Jython repr()
函数打印的。 PyJavaInstance
是Jython将传递给Java API的一种类型。 最后,请注意, data
和fromJava
值相同,除了元组现在是等效列表。 有关Jython类型的更多信息,请参见附录L:Jython类型摘要 。
通常,与简单使用sys.argv
提供的内容相比,您需要对命令参数进行更多处理。 parseArgs
函数可用于获取任何命令行参数,作为位置参数序列(的元组)和开关字典。
因此,继续JavaUtils.py
模块摘录(分别来自Jython的Wrapping Java服务和Mapping Java类型 ),我们将看到:
def parseArgs (args, validNames, nameMap=None):
""" Do some simple command line parsing. """
# validNames is a dictionary of valid switch names -
# the value (if any) is a conversion function
switches = {}
positionals = []
for arg in args:
if arg[0] == '-': # a switch
text = arg[1:]
name = text; value = None
posn = text.find(':') # any value comes after a :
if posn >= 0:
name = text[:posn]
value = text[posn + 1:]
if nameMap is not None: # a map of valid switch names
name = nameMap.get(name, name)
if validNames.has_key(name): # or - if name in validNames:
mapper = validNames[name]
if mapper is None: switches[name] = value
else: switches[name] = mapper(value)
else:
print "Unknown switch ignored -", name
else: # a positional argument
positionals.append(arg)
return positionals, switches
此功能可以按如下方式使用(在文件parsearg.py
):
from sys import argv
from JavaUtils import parseArgs
switchDefs = {'s1':None, 's2':int, 's3':float, 's4':int}
args, switches = parseArgs(argv[1:], switchDefs)
print "args:", args
print "switches:", switches
对于命令c:\>jython parsearg.py 1 2 3 -s1 -s2:1 ss -s4:2
,它显示:
args: ['1', '2', '3', 'ss']
switches: {'s4': 2, 's2': 1, 's1': None}
与大多数脚本语言(如Perl和Rexx)一样,Jython对操作字符串也有广泛的支持。 此支持通常与Java语言提供的支持相似,但通常更简单易用。 在本节中,我们将讨论一些更常用的字符串操作和函数。 请参阅本教程的第1部分和Python库参考,以了解有关字符串方法的更多信息。
在接下来几节的示例中,我将使用以下值:
name ="Barry Feigenbaum"
addr = '12345 Any Street"
v1 = 100; v2 = v1 * 1.5; v3 = -v2; v4 = 1 / v2
s1 = "String 1"; s2 = "String 2"
sent = "The rain in Spain falls mainly on the plain."
要获取任何值或表达式(即对象)的字符串表示形式,请使用以下功能之一:
eval
函数可以从中重新创建该值。 请注意,对于许多类型(包括基本类型), str(x)
和repr(x)
生成相同(或非常相似)的字符串。
字符串是一种内置类型,既用作值又用作方法的对象。 Strings support the basic operations of concatenation, indexing, containment, and formatting, as well as the other operations of immutable sequences. We'll go over the basic string operations, starting with concatenation.
We use the plus ( +
) operator to concatenate two strings. For example, the following line:
print "abc" + "xyz"
prints: abcxyz
.
To select a character or characters (that is, a substring) from a string you use indexing. For example: "abcxwy"[2]
yields c , while "abcxwy"[2:4]
yields cx .
Many of the string functions test conditions, thus they are often used in conjunction with the if
and while
statements. Here's an example of how we could use containment testing to see if a character were contained in a string:
if ' ' in name: print "space found"
-- or --
if 'q' not in sent: print "q not found"
In addition to testing conditions , strings also support methods to test the nature of the string. These are islower
, isupper
, isalnum
, isnum
, isalpha
, isspace
, and istitle
. These methods test to see if all the characters in the strings meet these conditions.
Strings support several methods that allow you to find and edit sub-strings, change case, and a host of other actions. To find a string in another string use the find
/ rfind
or startswith
/ endswidth
methods. 例如:
if name.find(' ') >= 0: print "space found"
-- or --
if name.find("Jones") < 0: print "Jones not in name"
Sometimes you need to edit the content of a string, for example to change its case or insert or remove text from it. Jython supplies several methods to do this. To change case, Jython has the lower
, upper
, swapcase
, title
, and capitalize
methods. To change the text of a string, use the replace
method. For example, to match strings often you want to ignore case or you may want to replace sub-strings:
if s1.lower() == s2.lower(): print "equal"
-- or --
newaddr = addr.replace("Street", "St.")
Often strings have extra blanks around them that are not important, such as when the string is entered by a user. To remove these extra blanks use the lstrip
, rstrip
, or strip
methods. For example, to match a command entered by a user:
cmd = raw_input("Enter a command")
if cmd.lstrip.startswith("run "):
print "run command found"
Often you need to break strings into parts, such as the words in a sentence or join multiple strings into one string. Jython supports the split
, splitlines
, and join
functions to do this. The split
method splits a line into words, while splitlines
splits a file of lines into separate lines. The join
method reverses split
. You can also join strings by concatenation as discussed above. For example, to extract the words from a sentence and then rebuild the sentence use:
words = sent.split(' ') # use space to separate words
sent2 = ' '.join(words) # use space between words
It is very easy to format local or global variables using the modulus (%) operator. The locals
and globals
functions return dictionaries for all the local and global (respectively) variables. 例如:
fname = "Barry"; lname = "Feigenbaum"
address = "1234 any St."
city = "Anytown"; state = "TX"; zip = "12345"
age = 30
children = 3
:
print "Hello %(fname)s from %(city)s, %(state)s." % locals()
prints Hello Barry from Anytown, TX.
See Appendix J: Formatting strings and values for more about formatting program variables.
Below are some format (%) operator examples. See Appendix J: Formatting strings and values for more examples.
表达 | 结果 |
---|---|
"Hello %s" % "Barry" |
Hello Barry |
"Count: %i, " "Avg Cost: $%.2f; " "Max Cost: $%.2f" % (10, 10.5, 50.25) |
Count: 10, Avg Cost: $10.50; Max Cost: $50.25 |
"This is %i%%" % 10 |
This is 10% |
"My name is %(first)s %(last)s!" % {'last':'Feigenbaum', 'first':'Barry', 'mi':'A'} |
My name is Barry Feigenbaum! |
For those familiar with C's printf("... %x ...", v1, ..., vN)
function, a similar but enhanced service can be added in Jython, as shown here:
def printf(stream, format, *pargs, **kwargs):
# seePrinting to files for more information
if pargs:
print >>stream, format % pargs
elif kwargs:
print >>stream, format % kwargs
else:
print >>stream, format
这里
Using the above printf
function definition, the following examples:
from sys import stdout
printf(stdout, "%s is %.1f years old and has %i children",
fname, age, children)
printf(stdout, "The %(name)s building has %(floors)d floors",
floors=105, name="Empire State")
printf(stdout, "Hello World!")
print:
Barry is 30.0 years old and has 3 children
The Empire State building has 105 floors
Hello World!
You can use the pprint
module functions, in particular the pformat
function, to print complex data structures in a formatted form. For example, this code:
data = [[1,2,3], [4,5,6],{'1':'one', '2':'two'},
"jsdlkjdlkadlkad", [i for i in xrange(10)]]
print "Unformatted:"; print data
print
from pprint import pformat
print "Formatted:"; print pformat(data)
prints the following:
Unformatted:
[[1, 2, 3], [4, 5, 6], {'2': 'two', '1': 'one'}, \
'jsdlkjdlkadlkad', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
Formatted:
[[1, 2, 3],
[4, 5, 6],
{'2': 'two', '1': 'one'},
'jsdlkjdlkadlkad',
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
As an example of using the string operations from String operations and functions , the justify.py
program (listed below) takes paragraphs of input and formats them into pages. The text may be left-, center-, right-aligned, or justified. Page margins may be specified. Header and/or footer text may be supplied.
See Related topics for some sample results of using this program.
import sys
def stripLines (lines):
""" Removed extra whitespace (that is, newlines). """
newlines = []
for line in lines:
line = line.strip()
newlines.append(line)
return newlines
def splitParagraphs (lines):
""" Splits a set of lines into paragraphs. """
paras = []
para = ""
for line in lines:
if len(line) > 0: # in paragraph
para += ' ' + line
else: # between paragraphs
para = para.strip()
if len(para) > 0:
paras.append(para)
para = ""
return paras
class Formatter:
""" Formats and prints paragraphs. """
def __init__ (self, stream, pagelen=66, linewidth=85,
lmargin=10, rmargin=10, pindent=5,
alignment="justify",
headers=None, footers=None):
self.stream = stream # stream to print on
# format settings
self.pagelen = pagelen
self.pindent = pindent
self.linewidth = linewidth
self.lmargin = lmargin
self.rmargin = rmargin
self.headers = headers
self.footers = footers
self.alignment = alignment
self.pagecount = 1 # current page
self.linecount = 0 # current line
def genLine (self, line):
print >>self.stream, line
self.linecount += 1
def outputLine (self, line):
self.testEndPage()
if not (self.linecount == 0 and len(line) == 0):
self.genLine(line)
def newPage (self):
if self.headers:
self.outputHeader()
def padPage (self):
while self.linecount < self.pagelen:
self.genLine("")
def endPage (self):
if self.footers:
if len(self.footers) + self.linecount < self.pagelen:
self.padPage()
self.outputFooter()
else:
if self.linecount < self.pagelen:
self.padPage()
self.linecount = 0
self.pagecount += 1
self.genLine('-' * 20)
def testEndPage (self):
if self.footers:
if len(self.footers) + 1 + self.linecount >= self.pagelen:
self.endPage()
self.newPage()
else:
if self.linecount >= self.pagelen:
self.endPage()
self.newPage()
def padLine (self, line, firstline=0, lastline=0):
""" Add spaces as needed by alignment mode. """
if self.alignment == "left":
adjust = firstline * self.pindent
#line = line
elif self.alignment == "center":
adjust = 0
pad = self.linewidth - adjust - len(line)
line = ' ' * (pad / 2) + line
elif self.alignment == "right":
adjust = 0
pad = self.linewidth - adjust - len(line)
line = ' ' * pad + line
elif self.alignment == "justify":
adjust = firstline * self.pindent
pad = self.linewidth - adjust - len(line)
line = ""
# add 1+ spaces between words to extend line
words = line.split()
xpad = pad
for word in words:
line += word + ' '
if not lastline and xpad > 0:
line += ' ' * (pad / len(words) + 1)
xpad -= 1
line = line.strip()
return ' ' * adjust + line
def format (self, line, firstline=0, lastline=0):
# indent by left margin
return ' ' * self.lmargin + \
self.padLine(line.strip(), firstline, lastline)
def formatParagraph (self, para):
lcount = 0
adjust = self.pindent
line = ""
# process by words
words = para.split(' ')
for word in words:
line += ' '
# about to get too long
if len(line) + len(word) > self.linewidth - adjust:
line = self.format(line, lcount == 0, 0)
self.outputLine(line)
line = ""
lcount += 1
adjust = 0
line += word
# output last (only) line
if len(line) > 0:
line = self.format(line, lcount == 0, 1)
self.outputLine(line)
def outputHeader (self):
for line in self.headers:
self.genLine(' ' * self.lmargin + line.center(self.linewidth))
self.genLine("")
def outputFooter (self):
self.genLine("")
for line in self.footers:
self.genLine(' ' * self.lmargin + line.center(self.linewidth))
def outputPages (self, paras):
""" Format and print the paragraphs. """
self.newPage()
for para in paras:
self.formatParagraph(para)
self.outputLine("")
self.endPage()
As an extension to the find and replace functions described in String operations and functions , Jython supports regular expressions. Regular expressions (RE) are strings that contain plain match text and control characters and provide an extremely powerful string search and replace facility. Jython supports (at least) the following forms of regular expressions:
ORO
package to your CLASSPATH
. The simplest RE is an exact string to match. More complex REs include special control characters. The control characters allow you to create patterns of matching strings. For more information on RE syntax and options see Appendix H: Regular expression control characters and the Python Library Reference .
[Jennette, from Barry: We need to get the spacing right in this table, I have multiple nbsp's that show as only one space.]-->
Below are some example REs and the strings they match:
Control character | Regular expression | 火柴 | Does not match |
---|---|---|---|
-- none -- | abc | abc | b aabc abcc |
。 - any character | 交流电 | abc axc 交流电 |
交流电 abbc |
* - optional repeating subpattern | a.*c | abc axc 交流电 交流电 axxxxc |
A B C D |
? - optional subpattern | a.?c | abc | 交流电 aabc |
+ - required repeating subpattern | a.+c | abc abbc axxc |
交流电 A B C D |
...|... - choice of subpattern | abc|def | abcef abdef |
abef abcdef |
(...) - grouping | a(xx)|(yy)c | axxc ayyc axxyyc |
axc ayc |
(...)* - repeating grouping | a(xx)*c | 交流电 axxc axxxxc |
axxbxxc |
(...)+ - required repeating grouping | a(xx)+c | axxc axxxxc |
交流电 axxbxxc |
\c - match a special character | \.\?\*\+ | .?*+ | ?.*+ A B C D |
\s - matches white space | a\s*z | z z a z |
za za abyz |
The Jython re
module provides support for regular expressions. re
's primary functions are findall
, match
, and search
to find strings, and sub
and subn
to edit them. The match
function looks at the start of a string, the search
function looks anywhere in a string, and the findall
function repeats search
for each possible match in the string. search
is (by far) the most used of the regular expression functions.
Here are some of the most common RE functions:
功能 | 注释) |
---|---|
match(pattern, string {, options}) |
Matches pattern at the string start |
search(pattern, string {, options}) |
Matches pattern somewhere in the string |
findall(pattern, string) |
Matches all occurrences of pattern in the string |
split(pattern, string {, max}) |
Splits the string at matching points and returns the results in a list |
sub(pattern, repl, string {, max}) |
Substitutes the match with repl for max or all occurrences; returns the result |
subn(pattern, repl, string {, max}) |
Substitutes the match with repl for max or all occurrences; returns the tuple (result, count) |
Note that the match
ing functions return None
if no match is found. Otherwise the match
functions will return a Match
object from which details of the match can be found. See the Python Library Reference for more information on Match
objects.
Let's take a look at some examples of regular expressions functions in action:
import re
# do a fancy string match
if re.search(r"^\s*barry\s+feigenbaum\s*$", name, re.I):
print "It's Barry alright"
# replace the first name with an initial
name2 = re.sub(r"(B|b)arry", "B.", name)
If you are going to use the same pattern repeatedly, such as in a loop, you can speed up execution by using the compile
function to compile the regular expression into a Pattern
object and then using that object's methods, as shown here:
import re
patstr = r"\s*abc\s*"
pat = re.compile(patstr)
# print all lines matching patstr
for s in stringList:
if pat.match(s, re.I): print "%r matches %r" % (s, patstr)
The following simplified version of the Grep
utility (from grep.py
) offers a more complete example of a Jython string function.
""" A simplified form of Grep. """
import sys, re
if len(sys.argv) != 3:
print "Usage: jython grep.py "
else:
# process the arguments
pgm, patstr, filestr = sys.argv
print "Grep - pattern: %r file: %s" % (patstr, filestr)
pat = re.compile(patstr) # prepare the pattern
# seeFile I/O in Jython
for more information
file = open(filestr) # access file for read
lines = file.readlines() # get the file
file.close()
count = 0
# process each line
for line in lines:
match = pat.search(line) # try a match
if match: # got a match
print line
print "Matching groups: " + str(match.groups())
count += 1
print "%i match(es)" % count
When run on the words.txt file from File I/O in Jython , the program produces the following result:
C:\Articles>jython grep.py "(\w*)!" words.txt
Grep - pattern: '(\\w*)!' file: words.txt
How many times must I say it; Again! again! and again!
Matched on: ('Again',)
Singing in the rain! I'm singing in the rain! \
Just singing, just singing, in the rain!
Matched on: ('rain',)
2 match(es)
In addition to the Java platform's file-related APIs (packages java.io
and, in Java 1.4, java.nio
), Jython provides simple yet powerful access to files using the File
type and services in the os
, os.path
, and sys
modules. (See Appendix F: The os module , Appendix G: The os.path module , Appendix E: The sys module and the Python Reference Manual for more details on the os
and os.path
packages.)
We'll start with a look at some basic file-access operations. A File
object is created using the built-in open
function, shown below, where path
is the path to the file, mode
is the access mode string, and size
is the suggested buffer size:
file = open(path {, mode {, size}})
The mode
string has the following syntax: (r|w|a){+}{b}
; the default mode is r
. Here is a listing of all the available access mode strings:
The name of the file is accessed through the name
attribute. The mode of the file is accessed through the mode
attribute.
Files support the following methods:
方法 | 注释) |
---|---|
close() |
Flush and close an open file |
flush() |
Outputs any buffered data |
read({size}) |
Reads up to size (or the whole file) |
readline({size}) |
Read a line (including ending '\n') up to size |
readlines() |
Reads the file and returns a list of lines (including ending '\n') |
seek(offset {, mode}) |
Seek to a position, mode: 0 - start of file, 1 - current offset, 2 - end of file |
tell() |
Return the current offset |
truncate({size}) |
Truncate (delete extra content) to current offset or specified size |
write(string) |
Write the string to a file. To write lines, end the string in '\n' |
writelines(lines) |
Write the list as a set of strings. To write lines, end each string in '\n' |
We'll look at a couple of simple file processing examples, starting with the file copy program below:
import sys
f = open(sys.argv[1], "rb") # open binary for reading
bin = f.read()
f.close()
f = open(sys.argv[2], "wb") # open binary (truncated) for write
f.write(bin)
f.close()
And here is a text file sort procedure:
import sys
f = open(sys.argv[1], "r") # read the file by lines
lines = f.readlines()
f.close()
lines.sort() # sort and print the lines
print "File %s sorted" % f.name
print lines
As a more complete example of file processing, study the following word-counting program:
import sys
def clean (word):
""" Remove any punctuation and map to a common case. """
word = word.lower()
# remove any special characters
while word and word[-1] in ".,;!": word = word[:-1]
while word and word[0] in ".,;!": word = word[1:]
return word
words = {} # set of unique words and counts
if len(sys.argv) != 2:
print "Usage: jython wcount.py "
else:
file = open(sys.argv[1]) # access file for read
lines = file.readlines() # get the file
file.close()
# process each line
for line in lines:
# process each word in the line
for word in line.split():
word = clean(word)
words[word] = words.get(word, 0) + 1 # update the count
# report the results
keys = words.keys()
keys.sort()
for word in keys:
print "%-5i %s" % (words[word], word)
Given the following input file ( words.txt )
Now is the time for all good men to come to the aid of their country.
The rain in Spain falls mainly on the plain.
How many times must I say it; Again! again! and again!
Singing in the rain! I'm singing in the rain! \
Just singing, just singing, in the rain!
the word-counting program (from A word-counting program in Jython ) would return the following results (wrapped into two columns to save space):
3 again 1 many
1 aid 1 men
1 all 1 must
1 and 1 now
1 come 1 of
1 country 1 on
1 falls 1 plain
1 for 4 rain
1 good 1 say
1 how 4 singing
1 i 1 spain
1 i'm 7 the
4 in 1 their
1 is 1 time
1 it 1 times
2 just 2 to
1 mainly
Let's take a look at the word-counting script re-implemented in the Java language. Notice the extensive use of types in declarations and type-casts in the assignment statements. As you can see, the Java code is significantly larger (approximately two times) and arguably far more cryptic.
import java.io.*;
import java.util.*;
import java.text.*;
public class WordCounter
{
protected static final String specials = ".,;!";
/** Remove any punctuation and map to a common case. */
protected static String clean(String word) {
word = word.toLowerCase();
// remove any special characters
while (word.length() > 0 &&
specials.indexOf(word.charAt(word.length() - 1)) >= 0) {
word = word.substring(0, word.length() - 1);
}
while (word.length() > 0 &&
specials.indexOf(word.charAt(0)) >= 0) {
word = word.substring(1);
}
return word;
}
protected static Map words = new HashMap();
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.out.println("Usage: java WordCounter ");
}
else {
// access file for read
FileInputStream fis = new FileInputStream(args[0]);
DataInputStream dis = new DataInputStream(fis);
List lines = new ArrayList();
// get the file
for (String line = dis.readLine();
line != null;
line = dis.readLine()) {
lines.add(line);
}
dis.close();
// process each line
for (int i = 0; i < lines.size(); i++) {
String line = (String)lines.get(i);
System.out.println("Processing: " + line);
String[] xwords = line.split("\\s+");
for (int w = 0; w < xwords.length; w++) {
String word = clean(xwords[w]);
if (word.length() > 0) {
Integer count = (Integer)words.get(word);
if (count == null) {
count = new Integer(0);
}
// update the count
words.put(word,
new Integer(count.intValue() + 1));
}
}
}
// report the results
String[] keys = (String[])words.keySet().
toArray(new String[words.size()]);
Arrays.sort(keys);
MessageFormat form = new MessageFormat(
"{0,number, #########0} {1}");
for (int i = 0; i < keys.length; i++) {
System.out.println(form.format(
new Object[] {words.get(keys[i]), keys[i]}));
}
}
}
}
The print
statement can print to a file by use of the " >>
" operator. By default it prints to the console (actually the value of sys.stdout
). For example, the following commands are equivalent:
print "Hello World!"
import sys
print >>sys.stdout, "Hello world!"
Jython allows alternate target files. For example, to print to the standard error stream use:
print >>sys.stderr, "Hello world!"
To print to a file use:
f = open("myfile", "w")
for i in range(10):
print >>f, "Line", i
f.close()
And to add to the end of a file use:
f = open("myfile", "a")
print >>f, "Added line"
f.close()
Sometimes you may want to save an object persistently (beyond the lifetime of the program that creates it) or send it to another application. To do this you need to serialize (or pickle ) the object so it can be placed in a file or on a stream. You then need to de-serialize (or un-pickle ) the object to use it again. Jython provides a module, pickle
, that makes this very easy. The pickle
module contains the following useful functions:
功能 | 注释) |
---|---|
load(file) |
Returns an object re-created from a previously created image in a file. |
loads(string) |
Returns an object recreated from a previously created image in a string. |
dump(object, file {, bin}) |
Stores an object image into a file. If bin is omitted or false, use a text representation; else a binary representation (which is typically smaller). |
dumps(object{, bin}) |
Returns a string containing the image of the object. If bin is omitted or false, use a text representation; else a binary representation (which is typically smaller). |
Here's an example of pickle
at work. The following code sequence
import pickle
class Data:
def __init__ (self, x, y):
self.__x = x
self.__y = y
def __str__ (self):
return "Data(%s,%s)" % (self.__x, self.__y)
def __eq__ (self, other):
return self.__x == other.__x and self.__y == other.__y
data = Data(10, "hello")
file = open("data.pic", 'w')
pickle.dump(data, file)
file.close()
file = open("data.pic", 'r')
newdata = pickle.load(file)
file.close()
print "data:", data
print "newdata:", newdata
print "data is newdata:", data is newdata
print "data == newdata:", data == newdata
prints this:
data: Data(10,hello)
newdata: Data(10,hello)
data is newdata: 0 (false)
data == newdata: 1 (true)
The file created is in (semi-)readable plain text. For example, the above code created the file data.pic
:
(i__main__
Data
p0
(dp1
S'_Data__y'
p2
S'hello'
p3
sS'_Data__x'
p4
I10
sb.
Note that Jython cannot pickle objects that are Java objects, reference Java objects, or subclass Java classes. To do this you need to use the java.io.ObjectOutputStream
and java.io.ObjectInputStream
classes.
As shown in the previous section, Jython can store objects into a file. Using a file per object can cause problems (that is, it can waste space and you will need to name each file). Jython supports a file that can hold multiple objects, called a shelf . A shelf acts much like a persistent dictionary. To create shelves, use the open
function of module shelve
. For example, the following code:
import shelve, sys
def printshelf (shelf, stream=sys.stdout): # print the entries in a shelf
for k in shelf.keys():
print >>stream, k, '=', shelf[k]
def clearshelf (shelf): # remove all keys in the shelf
for k in shelf.keys():
del shelf[k]
# create shelf
shelf = shelve.open("test.shelf")
clearshelf(shelf)
shelf["x"] = [1,2,3,4]
shelf["y"] = {'a':1, 'b':2, 'c':3}
printshelf(shelf)
shelf.close()
print
# update shelf
shelf = shelve.open("test.shelf")
printshelf(shelf)
print
shelf["z"] = sys.argv[1]
printshelf(shelf)
shelf.close()
print
# verify shelf persistent
shelf = shelve.open("test.shelf")
printshelf(shelf)
shelf.close()
produces this output (with argument "This is a test string"):
x = [1, 2, 3, 4]
y = {'b': 2, 'a': 1, 'c': 3}
x = [1, 2, 3, 4]
y = {'b': 2, 'a': 1, 'c': 3}
x = [1, 2, 3, 4]
z = This is a test string
y = {'b': 2, 'a': 1, 'c': 3}
x = [1, 2, 3, 4]
z = This is a test string
y = {'b': 2, 'a': 1, 'c': 3}
Note that the open
function produces two files based on the file name passed to open
:
We'll close this second installment of the "Introduction to Jython" tutorial with a complete program that encompasses many of the details we have so far discussed. The Factorial Calculator is a GUI application written entirely in Jython. It calculates the value of x! (x factorial) for any positive integer value. Because x! can be very large, this example takes advantage of Jython's ability to process very large integers. Calculations for large values of x (say, > 10000) can take several minutes, so the user interface includes a progress bar and a Cancel button to interrupt a calculation.
In the sections that follow, you can see the two most essential components of the Factorial Calculator: the class that supplies the factorial calculation engine, and the set of classes that comprise the GUI. The complete, runnable code for the Factorial Calculator is available for download in Related topics . Note that in order to completely understand the GUI code you should have some experience with creating Swing GUIs. Even without this prior knowledge, you should be able to discern many elements of the code from our prior discussion throughout this tutorial.
To get started, let's see what our GUI application looks like. Here's a screenshot of the GUI showing the result of calculating 100! (that is, 100 factorial).
Factorial
is the class that supplies the factorial calculation engine. It consists of a sequence of code with additional explanation lines (identified by -- ) added.
-- import the needed modules
import sys
import exceptions
-- create the Factorial class, a general purpose factorial calculation engine
class Factorial:
"""A general purpose factorial calculation engine"""
-- define the constructor
def __init__ (self):
self.__listeners = []
self.__cancelled = 0
-- allow other classes to register event listeners;
--- used to track calculation progress
-- A "listener" is a function that takes an integer % argument
def addListener (self, listener):
if listener not in self.__listeners:
self.__listeners.append(listener)
def addListeners (self, listeners):
for l in listeners:
self.addListener(l)
def removeListener (self, listener):
self.__listeners.remove(listener)
def removeListeners (self, listeners):
for l in listeners:
self.removeListener(l)
def fireListeners (self, value): # notify all listeners
for func in self.__listeners:
func(value)
-- allow others to cancel a long running calculation
def cancel (self):
self.__cancelled = 1
-- perform the factorial calculation;
-- may take a long time (many minutes) for big numbers
def calculate (self, value):
if type(value) != type(0) or value < 0:
raise ValueError, \
"only positive integers supported: " + str(value)
self.__cancelled = 0
result = 1L
self.fireListeners(0) # 0% done
# calculate factorial -- may take quite a while
if value > 1: # need to do calculation
last = 0
# using iteration (vs. recursion) to increase performance
# and eliminate any stack overflow possibility
for x in xrange(1, value + 1):
if self.__cancelled: break # early abort requested
result = result * x # calc next value
next = x * 100 / value
if next != last: # signal progress
self.fireListeners(next)
last = next
self.fireListeners(100) # 100% done
if self.__cancelled: result = -1
return result
# test case
if __name__ == "__main__":
print sys.argv[0], "running..."
fac = Factorial()
def doFac (value):
try:
print "For", value, "result =", fac.calculate(value)
except ValueError, e:
print "Exception -", e
doFac(-1)
doFac(0)
doFac(1)
doFac(10)
doFac(100)
doFac(1000)
Here you can see the set of classes that supplies the factorial GUI. The set consists of a sequence of code with additional explanation lines (identified by -- ) added.
-- import the needed modules
import sys
import string
from types import *
from java import lang
from java import awt
from java.awt import event as awtevent
from javax import swing
from factor import Factorial
-- PromptedValueLayout is a customized Java LayoutManager not discussed here
-- but included with the resources
from com.ibm.articles import PromptedValueLayout as ValueLayout
-- support asynchronous processing
class LongRunningTask(lang.Thread):
def __init__ (self, runner, param=None):
self.__runner = runner # function to run
self.__param = param # function parameter (if any)
self.complete = 0
self.running = 0
-- Java thread body
def run (self):
self.complete = 0; self.running = 1
if self.__param is not None:
self.result = self.__runner(self.__param)
else:
self.result = self.__runner()
self.complete = 1; self.running = 0
-- start a long running activity
def doAsync (func, param):
LongRunningTask(func, param).start()
-- Swing GUI services must be called only on the AWT event thread,
class SwingNotifier(lang.Runnable):
def __init__ (self, processor, param=None):
self.__runner = processor # function to do GUI updates
self.__param = param # function parameter (if any)
-- Java thread body
def run (self):
if self.__param is not None: self.__runner(self.__param)
else: self.__runner()
def execute (self):
swing.SwingUtilities.invokeLater(self)
-- define and construct a GUI for factorial calculation
class FactorialGui(swing.JPanel):
"""Create and process the GUI."""
def __init__ (self, engine):
swing.JPanel.__init__(self)
self.__engine = engine
engine.addListener(self.update)
self.createGui()
def update (self, value): # do on AWT thread
SwingNotifier(self.updateProgress, value).execute()
def updateProgress (self, value): # display progress updates
self.__progressBar.value = value
-- Calculate button press handler
def doCalc (self, event): # request a factorial
self.__outputArea.text = ""
ivalue = self.__inputField.text # get value to calculate
value = -1
try: value = int(ivalue) # convert it
except: pass
if value < 0: # verify it
self.__statusLabel.text = \
"Cannot make into a positive integer value: " + ivalue
else:
self.__calcButton.enabled = 0
self.__cancelButton.enabled = 1
msg = "Calculating factorial of %i..." % value
if value > 25000: msg += \
"; May take a very long time to complete!"
self.__statusLabel.text = msg # tell user we're busy
doAsync(self.calcFac, value) # do the calculation
-- main calculation worker
def calcFac (self, value):
stime = lang.System.currentTimeMillis()
fac = self.__engine.calculate(value) # time calculation
etime = lang.System.currentTimeMillis()
svalue = ""; order = 0
if fac >= 0: # we have a result, not cancelled
svalue = str(fac); order = len(svalue) - 1
formatted = ""
while len(svalue) > 100: # wrap long numbers
formatted += svalue[0:100] + '\n'
svalue = svalue[100:]
formatted += svalue
svalue = formatted
ftime = lang.System.currentTimeMillis()
SwingNotifier(self.setResult, \
(svalue, order, ftime - stime, etime - stime)).execute()
-- display the result
def setResult (self, values):
svalue, order, ttime, ftime = values
self.__cancelButton.enabled = 0
if len(svalue) > 0:
self.__statusLabel.text = \
"Setting result - Order: 10**%i" % order
self.__outputArea.text = svalue
self.__statusLabel.text = \
"Total time: %ims, Calc time: %ims, Order: 10**%i" % \
(ttime, ftime, order)
else:
self.__statusLabel.text = "Cancelled"
self.__calcButton.enabled = 1
-- Cancel button press handler
def doCancel (self, event): # request a cancel
self.__cancelButton.enabled = 0
self.__engine.cancel()
-- create (layout) the GUI
def createGui (self): # build the GUI
self.layout = awt.BorderLayout()
progB = self.__progressBar = \
swing.JProgressBar(0, 100, stringPainted=1);
inf = self.__inputField = swing.JTextField(5)
inl = swing.JLabel("Calculate value of:", swing.JLabel.RIGHT)
inl.labelFor = inf
outf = self.__outputArea = swing.JTextArea()
outl = swing.JLabel("Result:", swing.JLabel.RIGHT)
outl.labelFor = outf
calcb = self.__calcButton = \
swing.JButton("Calculate", actionPerformed=self.doCalc,
enabled=1, mnemonic=awtevent.KeyEvent.VK_C)
cancelb = self.__cancelButton = \
swing.JButton("Cancel", actionPerformed=self.doCancel,
enabled=0, mnemonic=awtevent.KeyEvent.VK_L)
vl = ValueLayout(5, 5)
inp = swing.JPanel(vl)
vl.setLayoutAlignmentX(inp, 0.2)
inp.add(inl); inp.add(inf, inl)
self.add(inp, awt.BorderLayout.NORTH)
vl = ValueLayout(5, 5)
outp = swing.JPanel(vl)
vl.setLayoutAlignmentX(outp, 0.2)
outp.add(outl); outp.add(swing.JScrollPane(outf), outl)
xoutp = swing.JPanel(awt.BorderLayout())
xoutp.add(progB, awt.BorderLayout.NORTH)
xoutp.add(outp, awt.BorderLayout.CENTER)
self.add(xoutp, awt.BorderLayout.CENTER)
sp = swing.JPanel(awt.BorderLayout())
bp = swing.JPanel()
bp.add(calcb)
bp.add(cancelb)
sp.add(bp, awt.BorderLayout.NORTH)
sl = self.__statusLabel = swing.JLabel(" ")
sp.add(sl, awt.BorderLayout.SOUTH)
self.add(sp, awt.BorderLayout.SOUTH)
-- main entry point; launches the GUI in a frame
if __name__ == "__main__":
print sys.argv[0], "running..."
frame = swing.JFrame("Factorial Calculator",
defaultCloseOperation=swing.JFrame.EXIT_ON_CLOSE)
cp = frame.contentPane
cp.layout = awt.BorderLayout()
cp.add( FactorialGui(Factorial()) )
frame.size = 900, 500
frame.visible = 1
This completes the two-part "Introduction to Jython" tutorial. While much of the tutorial functions as an overview, I hope I have provided you with enough advanced discussion, code examples, and incentive to proceed into more hands-on learning, specifically by developing your own programs in Jython.
In my opinion, Jython does for the Java platform what Visual Basic does for Microsoft's .NET: It provides much easier access to a complex development and execution environment. While easy to use, Jython improves upon the Java language by incorporating features the Java language lacks (some of which are also available today in .NET languages such as C#) without sacrificing any of the Java platform's capability (unless you count compile-time-type checking or a small reduction in effective performance).
We've discussed many of Jython's enhancements in this tutorial -- including for each
iteration, property methods accessible as attributes, collection literals, generic collections that hold basic types (such as integers), generic functions, first-class functions, overloadable operators, C-like printf
formatting, functions as event handlers, and dynamic code execution. Some of these features are so compelling that they will be included in the next version of the Java platform (that is, 1.5). Of course, with Jython you don't have to wait -- you can begin using them today!
The table below lists the character codes for Jython array types (see Java arrays from Jython ).
Character type code | Corresponding Java type |
---|---|
'z' | Boolean |
'c' | 烧焦 |
'b' | byte |
'h' | 短 |
'i' | 整型 |
'l' | 长 |
'f' | 浮动 |
'd' | 双 |
Note: The above table is from www.jython.org .
The following are the operators that are commonly (additional operators can be) overloaded:
操作员 | Function to override | 注释) |
---|---|---|
x + y x += y +x |
__add__(self, other) __radd__ (self, other) __iadd__(self, other) __pos__ self) |
Implements + operator |
x - y x -= y -x |
__sub__(self, other) __rsub__(self, other) __isub__(self, other) __neg__(self) |
Implements - operator |
x * y x *= y |
__mul__(self, other) __rmul__(self, other) __imul__(self, other) |
Implements * operator |
x / y x /= y |
__div__(self, other) __rdiv__(self, other) __idiv__(self, other) |
Implements / operator |
x % y x %= y |
__mod__(self, other) __rmod__(self, other) __imod__(self, other) |
Implements % operator |
x & y x &= y |
__and__(self, other) __rand__(self, other) __iand__(self, other) |
Implements & operator |
x | ÿ x |= y |
__or__(self, other) __ror__(self, other) __ior__(self, other) |
Implements | 算子 |
x ^ y x ^= y |
__xor__(self, other) __rxor__(self, other) __ixor__(self, other) |
Implements ^ operator |
~ x | __invert__(self) | Implements ~ operator |
x << y x <<= y |
__lshift__(self, other) __rlshift__(self, other) __ilshift__(self, other) |
Implements << operator |
x >> y x >>= y |
__rshift__(self, other) __ rrshift__(self, other) __ irshift__(self, other) |
Implements >> operator |
x ** y x **= y |
__pow__(self, other) __rpow__(self, other) __ipow__(self, other) |
Implements ** operator |
divmod(x,y) | __divmod__(self, other) __rdivmod__(self, other) |
Implements divmod() |
x < y | __lt__(self, other) | Implements < operator. This should return the opposite value returned by __ge__. |
x <= y | __le__(self, other) | Implements <= operator. This should return the opposite value returned by __gt__. |
x > y | __gt__(self, other) | Implements > operator. This should return the opposite value returned by __le__. |
x >= y | __ge__(self, other) | Implements >= operator. This should return the opposite value returned by __lt__. |
x == y | __eq__(self, other) | Implements == operator. This should return the opposite value returned by __ne__. |
x != y x <> y |
__ne__(self, other) | Implements != operator. This should return the opposite value returned by __eq__. |
cmp(x,y) | __cmp__(self, other) | Implements cmp() ; also used for relational tests if above specific overrides are not defined. This should return a < 0, 0 or > 0 value (say -1, 0 or 1). |
X | __nonzero__(self) | Implements logical tests. This should return 0 or 1. |
hash(x) | __hash__(self) | Implements hash() . Returns an integer value. Instances that are equal (that is, __eq__ returns true) should return the same __hash__ value (that is, (x == y) and (hash(x) == hash(y)) should be true. Similar to java.lang.Object.hashCode() . |
绝对(x) | __abs__(self) | Implements abs() |
int(x) | __int__(self) | Implements int() |
long(x) | __long__(self) | Implements long() |
float(x) | __float__(self) | Implements float() |
complex(x) | __complex__(self) | Implements complex() |
oct(x) | __oct__(self) | Implements oct() |
hex(x) | __hex__(self) | Implements hex() |
coerce(x,y) | __coerce__(self, other) | Implements coerce() |
y = x.name | __getattr__ (self, name) | Implements attribute lookup |
x.name = y | __setattr__ (self, name) | Implements attribute creation/update |
del x.name | __delattr__ (self, name) | Implements attribute removal |
y = c[i] | __getitem_ (self, i) | Implements item lookup |
c[i] = y | __setitem__ (self, i) | Implements item creation/update |
del c[i] | __delitem__ (self, i) | Implements item removal |
x(arg, ...) | __call__ (self, arg, ...) | Implements the call operator |
len(c) | __len__ (self) | Implements len() |
x in c x not in c |
__contains__ (self, other) | Implements in operator |
class() | __init__ (self, ...) | Instance constructor; called when the class is created |
del x | __del__ (self) | Instance destructor; called just before being deallocated |
repr(x) -- or -- `x` |
__repr__(self) | Implements repr() on this class |
str(x) | __str__(self) | Implements str() on this class; Jython uses __repr__ if __str__ is not defined. str() is used like x.toString() in Java |
Note: For the binary operators, the __xxx__ form is used when the left (or both) argument implements the function; the __ r xxx__ form is used only if the right argument implements the function and the left argument does not; the __ i xxx__ form is used to implement the augmented assignment ( x ?= y
) operation. See the Python Reference Manual for more details and overload-able functions.
The debugger provides the following functions/features:
命令 | Arguments | 功能 |
---|---|---|
h, help | -- none -- | List the available commands |
w, where | -- none -- | Shows the current stack trace |
d, down | -- none -- | Move down one stack frame |
u, up | -- none -- | Move up one stack frame |
b, break | line# | function, condition_expr | Set a breakpoint at a line number or function with an optional expression to evaluate - stop only if true |
tbreak | line# | function, condition_expr | Set a breakpoint at a line number or function with an optional expression to evaluate - stop only if true; the breakpoint is automatically cleared when hit |
cl, clear | bpid... | Clears all or listed breakpoints |
使能 | bpid... | Enables breakpoints |
禁用 | bpid... | Disabled breakpoints |
忽视 | bpid, count | Sets the breakpoint ignore (auto-resume) count |
健康)状况 | bpid, condition_expr | Sets the breakpoint condition expression |
s, step | -- none -- | Steps over the next line, possibly into a function |
n, next | -- none -- | Resume until the next line is reached |
r, return | -- none -- | Resume until the current function returns |
c, cont, continue | -- none -- | Resume execution |
j, jump | line# | Set a new current line |
l, list | line#1, line#1 | Lists source from line#1 to line#2, if omitted, then list the lines around the current line |
a, args | -- none -- | Show the arguments of the current function |
p, pp | expr | Evaluate the expression and print its result; pp formats the result |
打印 | expr | Do the print statement, that is, - !print expr |
别名 | name, expr | Create a named expression to simplify printing of repeated values |
unalias | 名称 | Delete an alias |
q, quit | -- none -- | End the debugging session |
! | 声明 | Execute the Jython statement |
Note: entering a blank line repeats the prior command.
Jython uses these rules to map parameter types:
Java Parameter Types | Allowed Python Types |
---|---|
烧焦 | String (must have length 1) |
Boolean | Integer (true = nonzero) |
byte, short, int, long | 整数 |
float, double | 浮动 |
java.lang.String, byte[], char[] | 串 |
java.lang.Class | Class or JavaClass |
Foobar[] | Array (must contain objects of class or subclass of Foobar) |
java.lang.Object | String->java.lang.String, all others unchanged |
org.python.core.PyObject | All unchanged |
Foobar | Instance --> Foobar (if Instance is subclass of Foobar); JavaInstance --> Foobar (if JavaInstance is instance of Foobar or subclass) |
Jython uses these rules to map return value types:
Java Return Type | Returned Python Type |
---|---|
烧焦 | String (of length 1) |
Boolean | Integer (true = 1, false = 0) |
byte, short, int, long | 整数 |
float, double | 浮动 |
java.lang.String | 串 |
java.lang.Class | JavaClass which represents given Java class |
Foobar[] | Array (containing objects of class or subclass of Foobar) |
org.python.core.PyObject (or subclass) | 不变的 |
Foobar | JavaInstance which represents the Java Class Foobar |
Note: the above two tables are from the www.jython.org site.
The sys module has some important variables:
Variable | 注释) |
---|---|
精氨酸 | The arguments supplied to the main module. argv[0] is the program name, argv[1] is the first argument and so on |
maxint minint |
Largest/smallest integer value |
platform | The version of Java Jython is running on |
路径 | The module search path |
stdin stdout stderr |
Standard input, output and error streams |
模组 | List of currently loaded modules |
版 version_info |
Jython version and details |
The sys module has some important functions:
功能 | 注释) |
---|---|
exit(int) | Exits the program |
exc_info() | Get information on the most recent exception |
See the Python Library Reference for more information.
The os module has some important variables:
Variable | 注释) |
---|---|
名称 | Type of host |
curdir | String to represent the current directory |
pardir | String to represent the parent directory |
九月 | String to separate directories in a path |
pathsep | String to separate paths in a path set string |
linesep | String to separate text lines |
环境 | The current environment string |
The sys module has some important functions:
功能 | 注释) |
---|---|
getcwd() | Get the current directory |
mkdir(path) makedirs(path) rmdir(path) |
Create/delete a directory |
remove(path) -- or -- unlink(path) |
Delete a file |
listdir(path) | List the files in a path |
rename(path, new) | Renames a file/directory to new |
system(command) | Run a shell command |
See the Python Library Reference for more information.
The os.path module has some important functions:
功能 | 注释) |
---|---|
exists(path) | True is path exists |
abspath(path) normpath(path) normcase(path) |
Returns the absolute form of the path Returns the normalized form of the path Returns the path in the normal case |
basename(path) dirname(path) |
Returns the file part of path Returns the directory part of path |
commonprefix(list) | Returns the longest common prefix of the paths in the list |
gethome() | Gets the home directory |
getsize(path) | Gets the size of the path file |
isabs(path) isfile(path) isdir(path) |
Tests to see if path is absolute Tests to see if path is a file Tests to see if path is a dir |
samepath(path1, path2) | True if path1 and path2 represent the same file |
join(list) | Joins the path elements in the list |
split(path) splitdrive(path) splitext(path) |
Returns (path, last_element) Returns (drive, rest_of_path) Returns (root, extension) |
See the Python Library Reference for more information.
The most useful Regular Expression special characters are:
Special Notation | 注释) |
---|---|
Any character except those below | Matches that character |
。 | Matches any character |
^ | Matches the start of the string |
$ | Matches the end of the string |
? ?? |
Matches longest 0 or 1 of the proceeding Matches shortest 0 or 1 of the proceeding |
+ +? |
Matches longest 1 or more of the proceeding Matches shortest 1 or more of the proceeding |
* *? |
Matches longest 0 or more of the proceeding Matches shortest 0 or more of the proceeding |
{m,n} {m,n}? |
Matches longest m to n of the proceeding Matches shortest m to n of the proceeding |
[...] [^...] |
Defines the set of enclosed characters Defines the set of non-enclosed characters |
...|... | Matches a choice (or) |
(...) (?...) |
Matches the sequence (or group) ...; groups are ordered from left to right with origin 1 Matches a sequence but does not define a group |
(?P (?P=name) |
Matches a sequence (or group) ... giving it a name Matches the sequence defined with the name |
(?=...) (?!...) |
Matches ... but does not consume the test Matches not ... but does not consume the test |
\C | Special characters: \c literal escapes: .?*+&^$|()[] \c function escapes: see below |
See the Python Library Reference for more information.
Function escapes:
Function Escapes | 注释) |
---|---|
\一个 \Z |
Matches at start of line Matches at end of line |
\B \b |
Matches not at beginning or end of a word Matches at beginning or end of a word |
\ D \d |
Matches not any decimal digit (0..9) Matches any decimal digit (0..9) |
\ S \s |
Matches not any white space Matches any white space |
\W \w |
Matches not any alpha-numeric characters Matches any alpha-numeric characters |
\# | Matches group # |
Several options exist to modify how regular expression are processed. Options are bit flags and may be combined by OR-ing (|) them together. Some of the more useful options are:
选项 | 注释) |
---|---|
IGNORECASE -- or -- 一世 |
Match ignoring case |
MULTILINE -- or -- 中号 |
Causes '^' and '$' to match internal line boundaries (vs. just the start and end of the string) |
DOTALL -- or -- 小号 |
Causes '.' to match a newline |
The following is the code generated by jythonc
compiler for the factor.py file of The factorial engine: factor.py .
import org.python.core.*;
public class factor extends java.lang.Object {
static String[] jpy$mainProperties =
new String[] {"python.modules.builtin",
"exceptions:org.python.core.exceptions"};
static String[] jpy$proxyProperties =
new String[] {"python.modules.builtin",
"exceptions:org.python.core.exceptions",
"python.options.showJavaExceptions",
"true"};
static String[] jpy$packages = new String[] {};
public static class _PyInner extends PyFunctionTable
implements PyRunnable {
private static PyObject i$0;
private static PyObject i$1;
private static PyObject s$2;
private static PyObject l$3;
private static PyObject i$4;
private static PyObject s$5;
private static PyObject s$6;
private static PyObject s$7;
private static PyObject s$8;
private static PyObject s$9;
private static PyObject i$10;
private static PyObject i$11;
private static PyObject s$12;
private static PyFunctionTable funcTable;
private static PyCode c$0___init__;
private static PyCode c$1_addListener;
private static PyCode c$2_addListeners;
private static PyCode c$3_removeListener;
private static PyCode c$4_removeListeners;
private static PyCode c$5_fireListeners;
private static PyCode c$6_cancel;
private static PyCode c$7_calculate;
private static PyCode c$8_Factorial;
private static PyCode c$9_doFac;
private static PyCode c$10_main;
private static void initConstants() {
i$0 = Py.newInteger(0);
i$1 = Py.newInteger(1);
s$2 = Py.newString("only positive integers supported: ");
l$3 = Py.newLong("1");
i$4 = Py.newInteger(100);
s$5 = Py.newString("__main__");
s$6 = Py.newString("running...");
s$7 = Py.newString("For");
s$8 = Py.newString("result =");
s$9 = Py.newString("Exception -");
i$10 = Py.newInteger(10);
i$11 = Py.newInteger(1000);
s$12 = Py.newString("C:\\Articles\\factor.py");
funcTable = new _PyInner();
c$0___init__ = Py.newCode(1, new String[] {"self"},
"C:\\Articles\\factor.py",
"__init__", false, false,
funcTable, 0,
null, null, 0, 1);
c$1_addListener = Py.newCode(2,
new String[]
{"self", "listener", "ll"},
"C:\\Articles\\factor.py",
"addListener", false,
false, funcTable, 1,
null, null, 0, 1);
c$2_addListeners = Py.newCode(2,
new String[]
{"self", "listeners", "l"},
"C:\\Articles\\factor.py",
"addListeners", false,
false, funcTable, 2,
null, null, 0, 1);
c$3_removeListener = Py.newCode(2,
new String[]
{"self", "listener", "ll"},
"C:\\Articles\\factor.py",
"removeListener", false,
false, funcTable, 3,
null, null, 0, 1);
c$4_removeListeners = Py.newCode(2,
new String[]
{"self", "listeners", "l"},
"C:\\Articles\\factor.py",
"removeListeners", false,
false, funcTable, 4,
null, null, 0, 1);
c$5_fireListeners = Py.newCode(2,
new String[]
{"self", "value", "func"},
"C:\\Articles\\factor.py",
"fireListeners", false,
false, funcTable, 5,
null, null, 0, 1);
c$6_cancel = Py.newCode(1,
new String[]
{"self"},
"C:\\Articles\\factor.py",
"cancel", false,
false, funcTable, 6,
null, null, 0, 1);
c$7_calculate = Py.newCode(2,
new String[]
{"self", "value", "next",
"x", "last", "result"},
"C:\\Articles\\factor.py",
"calculate", false,
false, funcTable, 7,
null, null, 0, 1);
c$8_Factorial = Py.newCode(0,
new String[]
{},
"C:\\Articles\\factor.py",
"Factorial", false,
false, funcTable, 8,
null, null, 0, 0);
c$9_doFac = Py.newCode(1,
new String[]
{"value", "e"},
"C:\\Articles\\factor.py",
"doFac", false,
false, funcTable, 9,
null, null, 0, 1);
c$10_main = Py.newCode(0,
new String[] {},
"C:\\Articles\\factor.py",
"main", false,
false, funcTable, 10,
null, null, 0, 0);
}
public PyCode getMain() {
if (c$10_main == null) _PyInner.initConstants();
return c$10_main;
}
public PyObject call_function(int index, PyFrame frame) {
switch (index){
case 0:
return _PyInner.__init__$1(frame);
case 1:
return _PyInner.addListener$2(frame);
case 2:
return _PyInner.addListeners$3(frame);
case 3:
return _PyInner.removeListener$4(frame);
case 4:
return _PyInner.removeListeners$5(frame);
case 5:
return _PyInner.fireListeners$6(frame);
case 6:
return _PyInner.cancel$7(frame);
case 7:
return _PyInner.calculate$8(frame);
case 8:
return _PyInner.Factorial$9(frame);
case 9:
return _PyInner.doFac$10(frame);
case 10:
return _PyInner.main$11(frame);
default:
return null;
}
}
private static PyObject __init__$1(PyFrame frame) {
frame.getlocal(0).__setattr__("_Factorial__listeners",
new PyList(new PyObject[] {}));
frame.getlocal(0).__setattr__("_Factorial__cancelled", i$0);
return Py.None;
}
private static PyObject addListener$2(PyFrame frame) {
frame.setlocal(2,
frame.getlocal(0).__getattr__("_Factorial__listeners"));
if (frame.getlocal(1)._notin(
frame.getlocal(2) ).__nonzero__()) {
frame.getlocal(2).invoke("append", frame.getlocal(1));
}
return Py.None;
}
private static PyObject addListeners$3(PyFrame frame) {
// Temporary Variables
int t$0$int;
PyObject t$0$PyObject, t$1$PyObject;
// Code
t$0$int = 0;
t$1$PyObject = frame.getlocal(1);
while ((t$0$PyObject =
t$1$PyObject.__finditem__(t$0$int++)) != null) {
frame.setlocal(2, t$0$PyObject);
frame.getlocal(0).invoke("addListener",
frame.getlocal(2));
}
return Py.None;
}
private static PyObject removeListener$4(PyFrame frame) {
frame.setlocal(2,
frame.getlocal(0).__getattr__("_Factorial__listeners"));
frame.getlocal(2).invoke("remove", frame.getlocal(1));
return Py.None;
}
private static PyObject removeListeners$5(PyFrame frame) {
// Temporary Variables
int t$0$int;
PyObject t$0$PyObject, t$1$PyObject;
// Code
t$0$int = 0;
t$1$PyObject = frame.getlocal(1);
while ((t$0$PyObject =
t$1$PyObject.__finditem__(t$0$int++)) != null) {
frame.setlocal(2, t$0$PyObject);
frame.getlocal(0).invoke("removeListener",
frame.getlocal(2));
}
return Py.None;
}
private static PyObject fireListeners$6(PyFrame frame) {
// Temporary Variables
int t$0$int;
PyObject t$0$PyObject, t$1$PyObject;
// Code
t$0$int = 0;
t$1$PyObject =
frame.getlocal(0).__getattr__("_Factorial__listeners");
while ((t$0$PyObject =
t$1$PyObject.__finditem__(t$0$int++)) != null) {
frame.setlocal(2, t$0$PyObject);
frame.getlocal(2).__call__(frame.getlocal(1));
}
return Py.None;
}
private static PyObject cancel$7(PyFrame frame) {
frame.getlocal(0).__setattr__("_Factorial__cancelled", i$1);
return Py.None;
}
private static PyObject calculate$8(PyFrame frame) {
// Temporary Variables
int t$0$int;
PyObject t$0$PyObject, t$1$PyObject;
// Code
if (((t$0$PyObject = frame.getglobal("type").
__call__(frame.getlocal(1)).
_ne(frame.getglobal("types").
__getattr__("IntType"))).__nonzero__()
? t$0$PyObject
: frame.getlocal(1)._lt(i$0)).__nonzero__()) {
throw Py.makeException(
frame.getglobal("ValueError"),
s$2._add(frame.getglobal("str").
__call__(frame.getlocal(1))));
}
frame.getlocal(0).__setattr__("_Factorial__cancelled", i$0);
frame.setlocal(5, l$3);
frame.getlocal(0).invoke("fireListeners", i$0);
if (frame.getlocal(1)._le(i$1).__nonzero__()) {
frame.setlocal(5, l$3);
}
else {
frame.setlocal(4, i$0);
t$0$int = 0;
t$1$PyObject = frame.getglobal("range").
__call__(i$1,frame.getlocal(1)._add(i$1));
while ((t$0$PyObject = t$1$PyObject.
__finditem__(t$0$int++)) != null) {
frame.setlocal(3, t$0$PyObject);
if (frame.getlocal(0).
__getattr__("_Factorial__cancelled").__nonzero__()) {
break;
}
frame.setlocal(5,
frame.getlocal(5)._mul(frame.getlocal(3)));
frame.setlocal(2,
frame.getlocal(3)._mul(i$4)._div(frame.getlocal(1)));
if
(frame.getlocal(2)._ne(frame.getlocal(4)).__nonzero__()) {
frame.getlocal(0).invoke("fireListeners",
frame.getlocal(2));
frame.setlocal(4, frame.getlocal(2));
}
}
}
frame.getlocal(0).invoke("fireListeners", i$4);
if (frame.getlocal(0).
__getattr__("_Factorial__cancelled").__nonzero__()) {
frame.setlocal(5, i$1.__neg__());
}
return frame.getlocal(5);
}
private static PyObject Factorial$9(PyFrame frame) {
frame.setlocal("__init__",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$0___init__));
frame.setlocal("addListener",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$1_addListener));
frame.setlocal("addListeners",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$2_addListeners));
frame.setlocal("removeListener",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$3_removeListener));
frame.setlocal("removeListeners",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$4_removeListeners));
frame.setlocal("fireListeners",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$5_fireListeners));
frame.setlocal("cancel",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$6_cancel));
frame.setlocal("calculate",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$7_calculate));
return frame.getf_locals();
}
private static PyObject doFac$10(PyFrame frame) {
// Temporary Variables
PyException t$0$PyException;
// Code
try {
Py.printComma(s$7);
Py.printComma(frame.getlocal(0));
Py.printComma(s$8);
Py.println(frame.getglobal("fac").
invoke("calculate", frame.getlocal(0)));
}
catch (Throwable x$0) {
t$0$PyException = Py.setException(x$0, frame);
if (Py.matchException(t$0$PyException,
frame.getglobal("ValueError"))) {
frame.setlocal(1, t$0$PyException.value);
Py.printComma(s$9);
Py.println(frame.getlocal(1));
}
else throw t$0$PyException;
}
return Py.None;
}
private static PyObject main$11(PyFrame frame) {
frame.setglobal("__file__", s$12);
frame.setlocal("sys",
org.python.core.imp.importOne("sys", frame));
frame.setlocal("types",
org.python.core.imp.importOne("types", frame));
frame.setlocal("exceptions",
org.python.core.imp.importOne("exceptions", frame));
frame.setlocal("Factorial",
Py.makeClass("Factorial",
new PyObject[] {},
c$8_Factorial, null));
if (frame.getname("__name__")._eq(s$5).__nonzero__()) {
Py.printComma(frame.getname("sys").
__getattr__("argv").__getitem__(i$0));
Py.println(s$6);
frame.setlocal("fac",
frame.getname("Factorial").__call__());
frame.setlocal("doFac",
new PyFunction(frame.f_globals,
new PyObject[] {}, c$9_doFac));
frame.getname("doFac").__call__(i$1.__neg__());
frame.getname("doFac").__call__(i$0);
frame.getname("doFac").__call__(i$1);
frame.getname("doFac").__call__(i$10);
frame.getname("doFac").__call__(i$4);
frame.getname("doFac").__call__(i$11);
}
return Py.None;
}
}
public static void moduleDictInit(PyObject dict) {
dict.__setitem__("__name__", new PyString("factor"));
Py.runCode(new _PyInner().getMain(), dict, dict);
}
public static void main(String[] args) throws java.lang.Exception {
String[] newargs = new String[args.length+1];
newargs[0] = "factor";
System.arraycopy(args, 0, newargs, 1, args.length);
Py.runMain(factor._PyInner.class, newargs,
factor.jpy$packages,
factor.jpy$mainProperties, null,
new String[] {"factor"});
}
}
Note: The above code has been reformatted for line length.
Note that a simplified form of Appendix J originally appeared as multiple panels in Part 1 of this tutorial.
Jython strings support a special formating operation similar to C's printf
, but using the modulo ("%") operator. 右边的项目集在字符串中匹配的%x
位置处替换为左边的字符串。 设置值通常是单个值,值的元组或值的字典。
The general format of the format specification is
%{(key)}{flag}...{width}{.precision}x
这是格式项目的指南:
The format operator supports the following format characters:
Character(s) | Result Format | 注释) |
---|---|---|
%s, %r | 串 | %s does str(x) , %r does repr(x) |
%i, %d | Integer Decimal | Basically the same format |
%o, %u, %x, %X | Unsigned Value | In octal, unsigned decimal, hexadecimal |
%f, %F | Floating Decimal | Shows fraction after decimal point |
%e, %E, %g, %G | Exponential | %g is %f unless the value is small; else %e |
%c | 字符 | Must be a single character or integer |
%% | 字符 | The % character |
Note: more details on the structure and options of the format item can be found in the Python Library Reference ( Related topics ). Use of case in format characters (for example, X vs x causes the symbol to show in matching case.
例如
print "%s is %i %s %s than %s!" % ("John", 5, "years", "older", "Mark")
print "Name: %(last)s, %(first)s" % \
{'first':"Barry", 'last':"Feigenbaum", 'age':18}
版画
John is 5 years older than Mark!
Name: Feigenbaum, Barry
Note that Appendix K appeared in Part 1 of this tutorial.
Jython provides very useful built-in functions that can be used without any imports. The most commonly used ones are summarized below:
Syntax | Use/Comment(s) | Example(s) |
---|---|---|
绝对(x) | Absolute value | abs(-1) --> 1 |
apply(func, pargs {, kargs}) -- or -- func(*pargs {, **kargs}) |
Execute the function with the supplied positional arguments and optional keyword arguments | apply(lambda x, y: x * y, (10, 20)) --> 200 |
callable(x) | Tests to see if the object is callable (ie, is a function, class or implements __call__) | callable(MyClass) --> 1 |
chr(x) | Converts the integer (0 - 65535) to a 1-character string | chr(9) --> "\t" |
cmp(x, y) | Compares x to y: returns: negative if x < y; 0 if x == y; positive if x > y | cmp("Hello", "Goodbye") --> > 0 |
coerce(x, y) | Returns the tuple of x and y coerced to a common type | coerce(-1, 10.2) --> (-1.0, 10.2) |
compile(text, name, kind) | Compile the text string from the source name. Kind is: "exec", "eval" or "single" |
|
complex(r, i) | Create a complex number | complex(1, 2) --> 1.0+2.0j complex("1.0-0.1j") --> 1.0-0.1j |
dir({namespace}) | Returns a list of the keys in a namespace (local if omitted) | dir() --> [n1, ..., nN] |
vars({namespace}) | Returns the namespace (local if omitted); do not change it | vars() --> {n1:v1, ..., nN:vN} |
divmod(x, y) | Returns the tuple (x /y, x % y) | divmod(100, 33) --> (3, 1) |
eval(expr {, globals {, locals}}) | Evaluate the expression in the supplied namespaces |
|
execfile(name {,globals {, locals}}) | Read and execute the named file in the supplied namespaces | execfile("myfile.py") |
filter(func, list) | Creates a list of items for which func returns true | filter(lambda x: x > 0, [-1, 0, 1, -5, 10]) --> [1, 10] |
float(x) | Converts x to a float | float(10) --> 10.0 float("10.3") --> 10.3 |
getattr(object, name {, default}) | Gets the value of the object's attribute; if not defined return default (or an exception if no default) | getattr(myObj, "size", 0) --> 0 |
setattr(object, name, value) | Creates/sets the value of the object's attribute | setattr(myObj, "size", 10) |
hasattr(object, name) | Test to see if the object has an attribute | hasattr(myObj, "size") --> 0 |
globals() | Returns the current global namespace dictionary | {n1:v1, ..., nN:vN} |
locals() | Returns the current local namespace dictionary | {n1:v1, ..., nN:vN} |
hash(object) | Returns the object's hash value. Similar to java.lang.Object.hashCode() |
hash(x) --> 10030939 |
hex(x) | Returns a hex string of x | hex(-2) --> "FFFFFFFE" |
id(object) | Returns a unique stable integer id for the object | id(myObj) --> 39839888 |
input(prompt) | Prompts and evaluates the supplied input expression; equivalent to eval(raw_input(prompt)) |
input("Enter expression:") with "1 + 2" --> 3 |
raw_input(prompt) | Prompts for and inputs a string | raw_input("Enter value:") with "1 + 2" --> "1 + 2" |
int(x{, radix}) | Converts to an integer; radix: 0, 2..36; 0 implies guess | int(10.2) --> 10 int("10") --> 10 int("1ff", 16) --> 511 |
isinstance(object, class) | Tests to see if object is an instance of class or a subclass of class; class may be a tuple of classes to test multiple types | isinstance(myObj, MyObject) --> 0 isinstance(x, (Class1, Class2)) --> 1 |
issubclass(xclass, clsss) | Tests to see if xclass is a sub-(or same) class of class; class may be a tuple of classes to test multiple types | issubclass(MyObject, (Class1, Class2)) --> 0 |
len(x) | Returns the length (number of items) in the sequence or map | len("Hello") --> 5 |
list(seq) | Converts the sequence into a list | list((1, 2, 3)) --> [1,2,3] list("Hello") --> ['H','e','l','l','o'] |
tuple(seq) | Converts the sequence into a tuple | tuple((1, 2, 3)) --> (1,2,3) tuple("Hello")--> ('H','e','l','l','o') |
long(x {, radix}) | Converts to a long integer; radix: 0, 2..36; 0 implies guess | long(10) --> 10L long("10000000000") --> 10000000000L |
map(func, list, ...) | Creates a new list from the results of applying func to each element of each list | map(lambda x,y: x+y, [1,2],[3,4]) --> [4,6] map(None, [1,2],[3,4]) --> [[1,3],[2,4]] |
max(x) | Returns the maximum value | max(1,2,3) --> 3 max([1,2,3]) --> 3 |
min(x) | Returns the minimum value | min(1,2,3) --> 1 min([1,2,3]) --> 1 |
oct(x) | Converts to an octal string | oct(10) --> "012 oct(-1) --> "037777777777" |
open(name, mode {, bufsize}) | Returns an open file. Mode is:(r|w|a){+}{b} | open("useful.dat", "wb", 2048) |
ord(x) | Returns the integer value of the character | ord('\t') --> 9 |
pow(x,y) pow(x,y,z) |
Computes x ** y Computes x ** y % z |
pow(2,3) --> 8 |
range({start,} stop {, inc}) xrange({start,} stop {, inc}) |
Returns a sequence ranging from start to stop in steps of inc; start defaults to 0; inc defaults to 1. Use xrange for large sequences (say more than 20 items) | range(10) --> [0,1,2,3,4,5,6,7,8,9] range(9,-1,-1) --> [9,8,7,6,5,4,3,2,1,0] |
reduce(func, list {, init}) | Applies func to each pair of items in turn accumulating a result | reduce(lambda x,y:x+y, [1,2,3,4],5) --> 15 reduce(lambda x,y:x&y, [1,0,1]) --> 0 reduce(None, [], 1) --> 1 |
repr(object) -- or -- `object` |
Convert to a string from which it can be recreated, if possible | repr(10 * 2) --> "'20'" repr('xxx') --> "'xxx'" x = 10; `x` --> "10'" |
round(x {, digits}) | Rounds the number | round(10.009, 2) --> 10.01 round(1.5) --> 2 |
str(object) | Converts to human-friendly string | str(10 * 2) --> "20" str('xxx') --> 'xxx' |
type(object) | Returns the type (not the same as class) of the object. To get the class use object.__class__ . Module types has symbolic names for all Jython types |
x = "1"; type(x) is type('') --> 1 |
zip(seq, ...) | Zips sequences together; results is only as long as the shortest input sequence | zip([1,2,3],"abc") --> [(1,'a'),(2,'b'),(3,'c')] |
See the Python Library Reference ( Related topics ) for more details.
Note that Appendix L appeared in Part 1 of this tutorial.
Jython supports many object types. The module types defines symbols for these types. The function type gets the type of any object. The type value can be tested (see ). The table below summarizes the most often used types.
Type symbol | Jython runtime type | 注释) |
---|---|---|
ArrayType | PyArray | Any array object |
BuiltinFunctionType | PyReflectedFunction | Any built-in function object |
BuiltinMethodType | PyMethod | Any built-in method object |
ClassType | PyClass | Any Jython class object |
ComplexType | PyComplex | Any complex object |
DictType -- or -- DictionaryType |
PyDictionary | Any dictionary object |
FileType | PyFile | Any file object |
FloatType | PyFloat | Any float object |
FunctionType | PyFunction | Any function object |
InstanceType | PyInstance | Any class instance object |
-- none -- | PyJavaInstance | Any Java class instance object |
IntType | PyInteger | Any integer object |
LambdaType | PyFunction | Any lambda function expression object |
ListType | PyList | Any list object |
LongType | PyLong | Any long object |
MethodType | PyMethod | Any non-built-in method object |
ModuleType | PyModule | Any module object |
NoneType | PyNone | Any None (only one) object |
StringType | PyString | Any ASCII string object |
TracebackType | PyTraceback | Any exception traceback object |
TupleType | PyTuple | Any tuple object |
类型类型 | PyJavaClass | Any type object |
UnboundMethodType | PyMethod | Any method (without a bound instancee) object |
UnicodeType | PyString | Any Unicode string object |
XRangeType | PyXRange | Any extended range object |
Note: several types map to the same Java runtime type. For more information on types see the Python Library Reference ( Related topics ).
翻译自: https://www.ibm.com/developerworks/java/tutorials/j-jython2/j-jython2.html