2016-04-14 20:55:16
八、模块
简介
前面介绍了如何在程序中定义一次函数而重用代码。在其他程序中重用很多函数,就需要使用模块。模块基本上就是一个包含了所有你定义的函数和变量的文件。模块的文件名必须以py为扩展名。
import sys print 'The command line arguments are:' for i in sys.argv: print i print 'The PYTHONPATH is', sys.path
结果:
The command line arguments are: C:/Users/kk/PycharmProjects/untitled1/fun.py The PYTHONPATH is ['C:\Users\kk\PycharmProjects\untitled1', 'C:\Users\kk\PycharmProjects\untitled1', 'C:\Windows\system32\python27.zip', 'C:\Python27\DLLs', 'C:\Python27\lib', 'C:\Python27\lib\plat-win', 'C:\Python27\lib\lib-tk', 'C:\Python27', 'C:\Python27\lib\site-packages']
sys模块包含了与Python解释器和它的环境有关的函数。
当Python执行import sys语句的时候,它在sys.path变量中所列目录中寻找sys.py模块。如果找到
了这个文件,这个模块的主块中的语句将被运行,然后这个模块将能够被你 使用 。注意,初
始化过程仅在我们 第一次 输入模块的时候进行。另外,“sys”是“system”的缩写。
sys模块中的argv变量通过使用点号指明——sys.argv——这种方法的一个优势是这个名称不会与
任何在你的程序中使用的argv变量冲突。另外,它也清晰地表明了这个名称是sys模块的一部
分。
sys.argv变量是一个字符串的 列表 (列表会在后面的章节详细解释)。特别地,sys.argv包含了
命令行参数 的列表,即使用命令行传递给你的程序的参数。
如果你使用IDE编写运行这些程序,请在菜单里寻找一个指定程序的命令行参数的方法。
这里,当我们执行python using_sys.py we are arguments的时候,我们使用python命令运行
using_sys.py模块,后面跟着的内容被作为参数传递给程序。Python为我们把它存储在sys.argv变
量中。
记住,脚本的名称总是sys.argv列表的第一个参数。所以,在这里,'using_sys.py'是sys.argv
[0]、'we'是sys.argv[1]、'are'是sys.argv[2]以及'arguments'是sys.argv[3]。注意,Python从0开始计
数,而非从1开始。
sys.path包含输入模块的目录名列表。我们可以观察到sys.path的第一个字符串是空的——这个
空的字符串表示当前目录也是sys.path的一部分,这与PYTHONPATH环境变量是相同的。这意
味着你可以直接输入位于当前目录的模块。否则,你得把你的模块放在sys.path所列的目录之
一。
字节编译的.pyc文件
输入一个模块相对来说是一个比较费时的事情,所以Python做了一些技巧,以便使输入模块更加快一些。一种方法是创建字节编译的文件 ,这些文件以.pyc作为扩展名。字节编译的文件与Python变换程序的中间状态有关。当你在下次从别的程序输入这个模块的时候,.pyc文件是十分有用的——它会快得多,因为一部分输入模块所需的处理已经完成了。另外,这些字节编译的文件也是与平台无关的。
from..import语句
如果你想要直接输入argv变量到你的程序中(避免在每次使用它时打sys.),那么你可以使用from sys import argv语句。如果你想要输入所有sys模块使用的名字,那么你可以使用from sys import *语句。
模块的__name__
每个模块都有一个名称,在模块中可以通过语句来找出模块的名称。这在一个场合特别有用
——就如前面所提到的,当一个模块被第一次输入的时候,这个模块的主块将被运行。假如我
们只想在程序本身被使用的时候运行主块,而在它被别的模块输入的时候不运行主块,我们该
怎么做呢?这可以通过模块的__name__属性完成。
九、数据结构
简介
数据结构基本上就是——它们是可以处理一些数据的结构 。或者说,它们是用来存储一组相关数据的。
在Python中有三种内建的数据结构——列表、元组和字典。
列表
list是处理一组有序项目的数据结构,即你可以在一个列表中存储一个序列的项目。假想你有一个购物列表,上面记载着你要买的东西,你就容易理解列表了。在Python中,列表中的项目应该包括在方括号中,每个项目之间用逗号分割。
一旦你创建了一个列表,你可以添加、删除或是搜索列表中的项目。由于你可以增加或删除项目,我们说列表是可变的数据类型,即这种类型是可以被改变的。
shoplist = ['apple', 'mango', 'carrot', 'banana'] print 'I have', len(shoplist),'items to purchase.' print 'These items are:', # Notice the comma at end of the line for item in shoplist: print item, print ' I also have to buy rice.' shoplist.append('rice') print 'My shopping list is now', shoplist print 'I will sort my list now' shoplist.sort() print 'Sorted shopping list is', shoplist print 'The first item I will buy is', shoplist[0] olditem = shoplist[0] del shoplist[0] print 'I bought the', olditem print 'My shopping list is now', shoplist
结果:
I have 4 items to purchase. These items are: apple mango carrot banana I also have to buy rice. My shopping list is now ['apple', 'mango', 'carrot', 'banana', 'rice'] I will sort my list now Sorted shopping list is ['apple', 'banana', 'carrot', 'mango', 'rice'] The first item I will buy is apple I bought the apple My shopping list is now ['banana', 'carrot', 'mango', 'rice']
在shoplist中,我们只存储了购买的东西的名字字符串。但是,在列表中添加任何种类的对象 包括数甚至其他列表。
我们使用列表的sort方法来对列表排序。需要理解的是,这个方法影响列表本身,这与字符串工作的方法不同。这就是我们所说的列表是可变的而字符串是不可变的。
注意,我们在print语句的结尾使用了一个 逗号 来消除每个print语句自动打印的换行符。shoplist[0],python从0计数。
元组
元组和列表十分类似,只不过元组和字符串一样是不可变的,即你不能修改元组。元组通过圆括号中用逗号分割的项目定义。元组通常用在使语句或用户定义的函数能够安全地采用一组值的时候,即被使用的元组的值不会改变。
zoo = ('wolf', 'elephant', 'penguin') print 'Number of animals in the zoo is', len(zoo) new_zoo = ('monkey', 'dolphin', zoo) print 'Number of animals in the new zoo is', len(new_zoo) print 'All animals in new zoo are', new_zoo print 'Animals brought from old zoo are', new_zoo[2] print 'Last animal brought from old zoo is', new_zoo[2][2]
结果:
Number of animals in the zoo is 3 Number of animals in the new zoo is 3 All animals in new zoo are ('monkey', 'dolphin', ('wolf', 'elephant', 'penguin')) Animals brought from old zoo are ('wolf', 'elephant', 'penguin') Last animal brought from old zoo is penguin
len函数可以用来获取元组的长度,变量zoo是一个元组,这表明元组也是一个序列。
新动物园new_zoo元组包含了一些已经在那里的动物和从老动物园带过来的动物。
注意:列表之中的列表不会失去它的身份,同样元组中的元组,或列表中的元组,或元组中的列表等等都是如此。
我们可以通过一对方括号来指明某个项目的位置从而来访问元组中的项目,就像我们对列表的用法一样。这被称作索引运算符。我们使用new_zoo[2]来访问new_zoo中的第三个项目。我们使用new_zoo[2][2]来访问new_zoo元组的第三个项目的第三个项目。
对于含有0个或1个项目的元组,一个空的元组由一对空的圆括号组成,如myempty = ()。然而,含有单个元素的元组就不那么简单了。你必须在第一个(唯一一个)项目后跟一个逗号,这样Python才能区分元组和表达式中一个带圆括号的对象。即如果你想要的是一个包含项目2的元组的时候,你应该指明singleton = (2 , )。
另外,元组最通常的用法是用在打印语句中。
age = 22 name = 'Swaroop' print '%s is %d years old' % (name, age) print 'Why is %s playing with that python?' % name
结果:
Swaroop is 22 years old Why is Swaroop playing with that python?
%字符串具备定制的功能。定制让输出满足某种特定的格式。定制可以是%s表示字符串或%d表示整数。元组必须按照相同的顺序来对应这些定制。
在第二个print语句中,我们使用了一个定制,后面跟着%符号后的单个项目——没有圆括号。这只在字符串中只有一个定制的时候有效。
字典
字典是dict类的实例/对象,类似于你通过联系人名字查找地址和联系人详细情况的地址簿,即,我们把键(名字)和值(详细情况)联系在一起。
键值对在字典中以这样的方式标记:d = {key1 : value1, key2 : value2 }。注意它们的键/值对用冒号分割,而各个对用逗号分割,所有这些都包括在花括号中。
注意:键必须是唯一的,就像如果有两个人恰巧同名的话,你无法找到正确的信息。只能使用不可变的对象(比如字符串)来作为字典的键,但是可以把不可变或可变的对象作为字典的值。字典中的键/值对是没有顺序的。如果想要一个特定的顺序,那么应该在使用前自己对它们排序。
# 'ab' is short for 'a'ddress'b'ook ab = { 'Swaroop' : 'swaroopch@byteofpython.info', 'Larry' : 'larry@wall.org', 'Matsumoto' : 'matz@ruby-lang.org', 'Spammer' : 'spammer@hotmail.com' } print "Swaroop's address is %s" % ab['Swaroop'] # Adding a key/value pair ab['Guido'] = 'guido@python.org' # Deleting a key/value pair del ab['Spammer'] print ' There are %d contacts in the address-book ' % len(ab) for name, address in ab.items(): print 'Contact %s at %s' % (name, address) if 'Guido' in ab: # OR ab.has_key('Guido') print " Guido's address is %s" % ab['Guido']
结果:
Swaroop's address is swaroopch@byteofpython.info There are 4 contacts in the address-book Contact Swaroop at swaroopch@byteofpython.info Contact Matsumoto at matz@ruby-lang.org Contact Larry at larry@wall.org Contact Guido at guido@python.org Guido's address is guido@python.org
我们使用已经介绍过的标记创建了字典ab,然后使用索引操作符来指定键,从而使用键/值对。
字典的items方法,会使用字典中的每个键/值对。它返回一个元组的列表,其中每个元组都包含一对项目——键与对应的值。我们抓取这个对,然后分别赋给for..in循环中的变量name和address,然后在for-块中打印这些值。
我们可以使用in操作符来检验一个键/值对是否存在,或者使用dict类的has_key方法。
序列
列表、元组和字符串都是序列,但是序列是什么。序列的两个主要特点是索引操作符和切片操作符。索引操作符让我们可以从序列中抓取一个特定项目;切片操作符让我们能够获取序列的一个切片,即一部分序列。
shoplist = ['apple', 'mango', 'carrot', 'banana'] # Indexing or 'Subscription' operation print 'Item 0 is', shoplist[0] print 'Item 1 is', shoplist[1] print 'Item 2 is', shoplist[2] print 'Item 3 is', shoplist[3] print 'Item -1 is', shoplist[-1] print 'Item -2 is', shoplist[-2] # Slicing on a list print 'Item 1 to 3 is', shoplist[1:3] print 'Item 2 to end is', shoplist[2:] print 'Item 1 to -1 is', shoplist[1:-1] print 'Item start to end is', shoplist[:] # Slicing on a string name = 'swaroop' print 'characters 1 to 3 is', name[1:3] print 'characters 2 to end is', name[2:] print 'characters 1 to -1 is', name[1:-1] print 'characters start to end is', name[:]
结果:
Item 0 is apple Item 1 is mango Item 2 is carrot Item 3 is banana Item -1 is banana Item -2 is carrot Item 1 to 3 is ['mango', 'carrot'] Item 2 to end is ['carrot', 'banana'] Item 1 to -1 is ['mango', 'carrot'] Item start to end is ['apple', 'mango', 'carrot', 'banana'] characters 1 to 3 is wa characters 2 to end is aroop characters 1 to -1 is waroo characters start to end is swaroop
使用索引来取得序列中的单个项目,这也被称作是下标操作:每当你用方括号中的一个数来指定一个序列的时候,Python会为你抓取序列中对应位置的项目。
Python从0开始计数。因此,shoplist[0]抓取第一个项目,shoplist[3]抓取shoplist序列中的第四个元素。
索引同样可以是负数,在那样的情况下,位置是从序列尾开始计算的。因此,shoplist[-1]表示
序列的最后一个元素而shoplist[-2]抓取序列的倒数第二个项目。
切片操作符是序列名后跟一个方括号,方括号中有一对可选的数字,并用冒号分割。注意这与
你使用的索引操作符十分相似。记住数是可选的,而冒号是必须的。
切片操作符中的第一个数(冒号之前)表示切片开始的位置,第二个数(冒号之后)表示切片
到哪里结束。如果不指定第一个数,Python就从序列首开始。如果没有指定第二个数,则
Python会停止在序列尾。注意,返回的序列从开始位置 开始 ,刚好在 结束 位置之前结束。即
开始位置是包含在序列切片中的,而结束位置被排斥在切片外。
这样,shoplist[1:3]返回从位置1开始,包括位置2,但是停止在位置3的一个序列切片,因此返
回一个含有两个项目的切片。类似地,shoplist[:]返回整个序列的拷贝。
你可以用负数做切片。负数用在从序列尾开始计算的位置。例如,shoplist[:-1]会返回除了最后
一个项目外包含所有项目的序列切片。
使用Python解释器交互地尝试不同切片指定组合,即在提示符下你能够马上看到结果。序列的
神奇之处在于你可以用相同的方法访问元组、列表和字符串。
十、解决问题—编写一个python脚本
软件开发过程
现在,我们已经走过了编写一个软件的各个环节。这些环节可以概括如下:
1. 什么(分析)
2. 如何(设计)
3. 编写(实施)
4. 测试(测试与调试)
5. 使用(实施或开发)
6. 维护(优化)
重视分析与设计。开始时实施一个简单的版本,对它进行测试与调试,使用它以确信它如预期那样地工作,然后再增加任何你想要的特性,根据需要一次次重复这个编写-测试-使用的周期。记住“软件是长出来的,而不是建造的”。
十一、面向对象的编程
简介
前面所介绍的都是根据操作数据的函数或语句块来设计程序的。这被称为面向过程的编程。还有一种把数据和功能结合起来,用称为对象的东西包裹起来组织程序的方法。这种方法称为面向对象的编程理念。在大多数时候你可以使用过程性编程,但是有些时候当你想要编写大型程序或是寻求一个更加合适的解决方案的时候,你就得使用面向对象的编程技术。
注意:类的域和方法被列在一个缩进块中。
self
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称,但是在调用这个方法的时候你不为这个参数赋值,Python会提供这个值。这个特别的变量指对象本身,按照惯例它的名称是self,相当于java中的this。
你不需要给self赋值,Python会给它赋值。例如:
假如你有一个类称为MyClass和这个类的一个实例MyObject。当你调用这个对象的方法MyObject.method(arg1, arg2)的时候,这会由Python自动转为MyClass.method(MyObject, arg1,arg2)——这就是self的原理了。
类
创建一个类
class Person: pass # An empty block p = Person() print p
结果:
<__main__.Person instance at 0x0168B6C0>
我们使用class语句后跟类名,创建了一个新的类。这后面跟着一个缩进的语句块形成类体。在这个例子中,我们使用了一个空白块,它由pass语句表示。
我们简单地打印了这个变量的类型。它告诉我们我们已经在__main__模块中有了一个Person类的实例,并且将存储对象的计算机内存地址也打印了出来。
对象的方法
我们已经讨论了类/对象可以拥有像函数一样的方法,这些方法与函数的区别只是一个额外的self变量。
class Person: def sayHi(self): print 'Hello, how are you?' p = Person() p.sayHi() # This short example can also be written as Person().sayHi()
结果:
Hello, how are you?
这里我们看到了self的用法。注意sayHi方法没有任何参数,但仍然在函数定义时有self。
__init__方法
__init__方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的初始化,类似于JAVA中的构造器 。
注意,这个名称的开始和结尾都是双下划线。
class Person: def __init__(self, name): self.name = name def sayHi(self): print 'Hello, my name is', self.name p = Person('Swaroop') p.sayHi() # This short example can also be written as Person('Swaroop').sayHi()
结果:
Hello, my name is Swaroop
类与对象的变量
有两种类型的域 ——类的变量和对象的变量,它们根据是类还是对象拥有这个变量而区分。
类的变量由一个类的所有对象(实例)共享使用。只有一个类变量的拷贝,所以当某个对象对类的变量做了改动的时候,这个改动会反映到所有其他的实例上。
对象的变量由类的每个对象/实例拥有。因此每个对象有自己对这个域的一份拷贝,即它们不是共享的,在同一个类的不同实例中,虽然对象的变量有相同的名称,但是是互不相关的。
class Person: '''Represents a person.''' population = 0 def __init__(self, name): '''Initializes the person's data.''' self.name = name print '(Initializing %s)' % self.name # When this person is created, he/she # adds to the population Person.population += 1 def __del__(self): '''I am dying.''' print '%s says bye.' % self.name Person.population -= 1 if Person.population == 0: print 'I am the last one.' else: print 'There are still %d people left.' % Person.population def sayHi(self): '''Greeting by the person. Really, that's all it does.''' print 'Hi, my name is %s.' % self.name def howMany(self): '''Prints the current population.''' if Person.population == 1: print 'I am the only person here.' else: print 'We have %d persons here.' % Person.population swaroop = Person('Swaroop') swaroop.sayHi() swaroop.howMany() kalam = Person('Abdul Kalam') kalam.sayHi() kalam.howMany() swaroop.sayHi() swaroop.howMany()
结果:
(Initializing Swaroop) Hi, my name is Swaroop. I am the only person here. (Initializing Abdul Kalam) Hi, my name is Abdul Kalam. We have 2 persons here. Hi, my name is Swaroop. We have 2 persons here. Abdul Kalam says bye. There are still 1 people left. Swaroop says bye. I am the last one.
这里,population属于Person类,因此是一个类的变量。name变量属于对象(它使用self赋值)因此是对象的变量。
__init__方法用一个名字来初始化Person实例。在这个方法中,我们让population增加1,这是因为我们增加了一个人。同样可以发现,self.name的值根据每个对象指定,这表明了它作为对象的变量的本质。
Person.__doc__和Person.sayHi.__doc__来分别访问类与方法的文档字符串。
方法__del__,它在对象消逝的时候被调用。对象消逝即对象不再被使用,它所占用的内存将返回给系统作它用。在这个方法里面,我们只是简单地把Person.population减1。
当对象不再被使用时,__del__方法运行,但是很难保证这个方法究竟在 什么时候运行。如果你想要指明它的运行,你就得使用del语句。
注意:Python中所有的类成员(包括数据成员)都是公共的 ,所有的方法都是有效的 。
如果你使用的数据成员名称以双下划线前缀,比如__privatevar,Python的名称管理体系会有效地把它作为私有变量。
如果某个变量只想在类或对象中使用,就应该以单下划线前缀,保护型变量。
而其他的名称都将作为公共的,可以被其他类/对象使用。
继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。
一个子类型在任何需要父类型的场合可以被替换成父类型,即对象可以被视作是父类的实例,这种现象被称为多态现象。
父类也称基本类或超类,子类也称衍生类或导出类。
class SchoolMember: '''Represents any school member.''' def __init__(self, name, age): self.name = name self.age = age print '(Initialized SchoolMember: %s)' % self.name def tell(self): '''Tell my details.''' print 'Name:"%s" Age:"%s"' % (self.name, self.age), class Teacher(SchoolMember): '''Represents a teacher.''' def __init__(self, name, age, salary): SchoolMember.__init__(self, name, age) self.salary = salary print '(Initialized Teacher: %s)' % self.name def tell(self): SchoolMember.tell(self) print 'Salary: "%d"' % self.salary class Student(SchoolMember): '''Represents a student.''' def __init__(self, name, age, marks): SchoolMember.__init__(self, name, age) self.marks = marks print '(Initialized Student: %s)' % self.name def tell(self): SchoolMember.tell(self) print 'Marks: "%d"' % self.marks t = Teacher('Mrs. Shrividya', 40, 30000) s = Student('Swaroop', 22, 75) print # prints a blank line members = [t, s] for member in members: member.tell() # works for both Teachers and Students
结果:
(Initialized SchoolMember: Mrs. Shrividya) (Initialized Teacher: Mrs. Shrividya) (Initialized SchoolMember: Swaroop) (Initialized Student: Swaroop) Name:"Mrs. Shrividya" Age:"40" Salary: "30000" Name:"Swaroop" Age:"22" Marks: "75"
为了使用继承,我们把基本类的名称作为一个元组跟在定义类时的类名称之后。如果在继承元组中列了一个以上的类,那么它就被称作多重继承 。
注意Python不会自动调用基本类的constructor,你得亲自专门调用它。
十二、输入/输出
可以使用使用raw_input从用户那里得到输入,然后用print打印一些结果。
对于输出,可以使用多种多样的str(字符串)类。例如,你能够使用rjust方法返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串。
另一个常用的输入/输出类型是处理文件。创建、读和写文件的能力是许多程序所必需的。
文件
可以通过创建一个file类的对象来打开一个文件,分别使用file类的read、readline或write方法来恰当地读写文件。对文件的读写能力依赖于你在打开文件时指定的模式。最后,当你完成对文件的操作的时候,你调用close方法来告诉Python我们完成了对文件的使用。
待续