回目录
#!/usr/bin/python # Filename: simplestclass.py class Person: pass # An empty block p = Person() # 创建类对象 print(p) 输出 $ python simplestclass.py <__main__.Person instance at 0xf6fcb18c>
我们使用class语句后跟类名,创建了一个新的类。这后面跟着一个缩进的语句块形成类体。在这个例子中,我们使用了一个空白块,它由pass语句表示。
接下来,我们使用类名后跟一对圆括号来创建一个对象/实例。(我们将在下面的章节中学习更多的如何创建实例的方法)。为了验证,我们简单地打印了这个变量的类型。它告诉我们我们已经在__main__模块中有了一个Person类的实例。可以注意到存储对象的计算机内存地址也打印了出来。
在类方法的定义时, 需要在第一个参数位置添加self, 而在调用时不需要为该参数赋值, Python自动为我们添加类对象的指针给self.(这和C++的是一样的)
这个特别的变量指对象本身,按照惯例它的名称是self。
例11.2 使用对象的方法
#!/usr/bin/python # Filename: method.py class Person: def sayHi(self): # 定义类方法, 注意这个self print 'Hello, how are you?' p = Person() p.sayHi() # This short example can also be written as Person().sayHi() (源文件:code/method.py) 输出 $ python method.py Hello, how are you?
这里我们看到了self的用法。注意sayHi方法没有任何参数,但仍然在函数定义时有self。
__init__方法在类的一个对象被建立时,马上运行, 相当于C++中构造函数。你可以在里面做一些初始化工作 。注意,这个名称的开始和结尾都是双下划线。
使用__init__方法
例11.3 使用__init__方法
#!/usr/bin/python # Filename: class_init.py class Person: def __init__(self, name): # 类方法定义时, 第一个参数都是self self.name = name # 初始化工作 # 注意name不需要在类中声明, 直接赋值就代表声明了 def sayHi(self): print('Hello, my name is', self.name) p = Person('Swaroop') # 使用时不需要填入self p.sayHi() # This short example can also be written as Person('Swaroop').sayHi() 输出 $ python class_init.py Hello, my name is Swaroop
我们把__init__方法定义为取一个参数name(以及普通的参数self)。
类与对象的变量只是与类和对象的名称空间 绑定 的普通变量,即这些名称只在这些类与对象的前提下有效。
有两种类型的域 ——类的变量和对象的变量,它们根据是类还是对象拥有这个变量而区分。
类的变量相当于C++类的静态变量. 对象的变量相当于C++类的非静态变量.
使用类与对象的变量
例11.4 使用类与对象的变量
#!/usr/bin/python # Filename: objvar.py class Person: '''Represents a person.''' # 这个干嘛的 population = 0 # 这个就是类的变量了 def __init__(self, name): # 类构造函数 '''Initializes the person's data.''' self.name = name # self.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() 输出 $ python objvar.py (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.
Python中所有的类成员(包括数据成员)都是 公共的 ,所有的方法都是 有效的 。
只有一个例外:如果你使用的数据成员名称以 双下划线前缀 比如__privatevar,Python的名称管理体系会有效地把它作为私有变量。
这样就有一个惯例,如果某个变量只想在类或对象中使用,就应该以单下划线前缀。而其他的名称都将作为公共的,可以被其他类/对象使用。记住这只是一个惯例,并不是Python所要求的(与双下划线前缀不同)。
例11.5 使用继承
#!/usr/bin/python # Filename: inherit.py 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的 self.salary = salary print('(Initialized Teacher: %s)' % self.name) def tell(self): # 重载了父类的tell方法 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的 self.marks = marks print('(Initialized Student: %s)' % self.name) def tell(self): # 重载了父类的tell方法 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: # 循环调用tell member.tell() # works for both Teachers and Students 输出 $ python inherit.py (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"
为了使用继承,我们把基本类的名称作为一个元组跟在定义类时的类名称之后。然后,我们注意到基本类的__init__方法专门使用self变量调用,这样我们就可以初始化对象的基本类部分。这一点十分重要——Python不会自动调用基本类的constructor,你得亲自专门调用它。这里是需要self传递给父类的__init__函数的
我们还观察到我们在方法调用之前加上类名称前缀,然后把self变量及其他参数传递给它。
注意,在我们使用SchoolMember类的tell方法的时候,我们把Teacher和Student的实例仅仅作为SchoolMember的实例。
Python总是首先查找子类的方法, 如果它不能在导出类中找到对应的方法,它才开始到基本类中逐个查找。基本类是在类定义的时候,在元组之中指明的。
你可以通过创建一个file类的对象来打开一个文件,分别使用file类的read、readline或write方法来恰当地读写文件。对文件的读写能力依赖于你在打开文件时指定的模式。最后,当你完成对文件的操作的时候,你调用close方法来告诉Python我们完成了对文件的使用。
#!/usr/bin/python # Filename: using_file.py poem = '''\ Programming is fun When the work is done if you wanna make your work also fun: use Python! ''' f = open('poem.txt', 'w') # open for 'w'riting 打开文件写方式 f.write(poem) # write text to file写入文本到文件 f.close() # close the file关闭文件 f = open('poem.txt')#再次打开文件 # if no mode is specified, 'r'ead mode is assumed by default while True: line = f.readline() # 循环读取文件, 知道读取到EOF文件尾 if len(line) == 0: # Zero length indicates EOF break print(line) # Notice comma to avoid automatic newline added by Python f.close() # close the file
Python提供一个标准的模块,称为pickle。使用它你可以在一个文件中储存任何Python对象,之后你又可以把它完整无缺地取出来。这被称为 持久地 储存对象。
还有另一个模块称为cPickle,它的功能和pickle模块完全相同,只不过它是用C语言编写的,因此要快得多(比pickle快1000倍)。你可以使用它们中的任一个,而我们在这里将使用cPickle模块。记住,我们把这两个模块都简称为pickle模块。
#!/usr/bin/python # Filename: pickling.py #import cPickle as p 命名空间使用as来重命名 import pickle as p shoplistfile = 'shoplist.data' # the name of the file where we will store the object shoplist = ['apple', 'mango', 'carrot'] # Write to the file 写入Python对象到文件 f = open(shoplistfile, 'wb') p.dump(shoplist, f) # dump the object to a file f.close() del shoplist # remove the shoplist # Read back from the storage 重新读取 f = open(shoplistfile, 'rb') storedlist = p.load(f) print(storedlist) 输出 $ python pickling.py ['apple', 'mango', 'carrot']
#!/usr/bin/python # Filename: try_except.py import sys try: s = input('Enter something --> ') except EOFError: # 这里指定捕捉指定类型的异常 print('\nWhy did you do an EOF on me?') sys.exit() # exit the program except: # 捕捉所有类型的异常. print('\nSome error/exception occurred.') # here, we are not exiting the program print('Done')
我们把所有可能引发错误的语句放在try块中,然后在except从句/块中处理所有的错误和异常。except从句可以专门处理单一的错误或异常,或者一组包括在圆括号内的错误/异常。如果没有给出错误或异常的名称,它会处理 所有的 错误和异常。对于每个try从句,至少都有一个相关联的except从句。
如果某个错误或异常没有被处理,默认的Python处理器就会被调用。它会终止程序的运行,并且打印一个消息,我们已经看到了这样的处理。你还可以让try..catch块关联上一个else从句。当没有异常发生的时候,else从句将被执行。我们还可以得到异常对象,从而获取更多有个这个异常的信息。
你可以使用raise语句 引发 异常。你还得指明错误/异常的名称和伴随异常 触发的 异常对象。你可以引发的错误或异常应该分别是一个Error或Exception类的直接或间接导出类。
#!/usr/bin/python # Filename: raising.py # 定义一个异常子类 class ShortInputException(Exception): '''A user-defined exception class.''' def __init__(self, length, atleast): Exception.__init__(self) self.length = length self.atleast = atleast try: s = input('Enter something --> ') if len(s) < 3: raise ShortInputException(len(s), 3) # 如果输入长度小于3, 使用 raise 抛出一个异常 # Other work can continue as usual here except EOFError: print('\nWhy did you do an EOF on me?') except ShortInputException as x: # 注意x 代表异常类对象的名称 print('ShortInputException: The input was of length %d, \ was expecting at least %d' % (x.length, x.atleast)) else: print('No exception was raised.')
在一个try块下,你可以同时使用except从句和finally块。如果你要同时使用它们的话,需要把一个嵌入另外一个。
#!/usr/bin/python # Filename: finally.py import time try: f = open('poem.txt') while True: # our usual file-reading idiom line = f.readline() if len(line) == 0: break time.sleep(2) print(line) finally: f.close() print('Cleaning up...closed the file') 输出 $ python finally.py Programming is fun When the work is done Cleaning up...closed the file Traceback (most recent call last): File "finally.py", line 12, in ? time.sleep(2) KeyboardInterrupt
我们进行通常的读文件工作,但是我有意在每打印一行之前用time.sleep方法暂停2秒钟。这样做的原因是让程序运行得慢一些(Python由于其本质通常运行得很快)。在程序运行的时候,按Ctrl-c中断/取消程序。
我们可以观察到KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭.
也就是说finally块总是会执行的, 无论异常有没有发生.
sys模块包含系统对应的功能。
例14.1 使用sys.argv
#!/usr/bin/python # Filename: cat.py import sys def readfile(filename): '''Print a file to the standard output.''' f = open(filename) while True: line = f.readline() if len(line) == 0: break print(line) # notice comma f.close() # Script starts from here if len(sys.argv) < 2: print('No action specified.') sys.exit() if sys.argv[1].startswith('--'): option = sys.argv[1][2:] # fetch sys.argv[1] but without the first two characters if option == 'version': print('Version 1.2') elif option == 'help': print('''\ This program prints files to the standard output. Any number of files can be specified. Options include: --version : Prints the version number --help : Display this help''') else: print('Unknown option.') sys.exit() else: for filename in sys.argv[1:]: readfile(filename)
在Python程序运行的时候,即不是在交互模式下,在sys.argv列表中总是至少有一个项目。它就是当前运行的程序名称,作为sys.argv[0](由于Python从0开始计数)。其他的命令行参数在这个项目之后。
为了使这个程序对用户更加友好,我们提供了一些用户可以指定的选项来了解更多程序的内容。我们使用第一个参数来检验我们的程序是否被指定了选项。如果使用了--version选项,程序的版本号将被打印出来。类似地,如果指定了--help选项,我们提供一些关于程序的解释。我们使用sys.exit函数退出正在运行的程序。和以往一样,你可以看一下help(sys.exit)来了解更多详情。
如果没有指定任何选项,而是为程序提供文件名的话,它就简单地打印出每个文件地每一行,按照命令行中的顺序一个文件接着一个文件地打印。
顺便说一下,名称cat是 concatenate 的缩写,它基本上表明了程序的功能——它可以在输出打印一个文件或者把两个或两个以上文件连接/级连在一起打印。
sys.version字符串给你提供安装的Python的版本信息。
sys.version_info元组则提供一个更简单的方法来使你的程序具备Python版本要求功能。
[swaroop@localhost code]$ python >>> import sys >>> sys.version '2.3.4 (#1, Oct 26 2004, 16:42:40) \n[GCC 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)]' >>> sys.version_info (2, 3, 4, 'final', 0)
对于有经验的程序员,sys模块中其他令人感兴趣的项目有sys.stdin、sys.stdout和sys.stderr它们分别对应你的程序的标准输入、标准输出和标准错误流。
这个模块包含普遍的操作系统功能。如果你希望你的程序能够与平台无关的话,这个模块是尤为重要的。即它允许一个程序在编写后不需要任何改动,也不会发生任何问题,就可以在Linux和Windows下运行。一个例子就是使用os.sep可以取代操作系统特定的路径分割符。
下面列出了一些在os模块中比较有用的部分。它们中的大多数都简单明了。
os.name字符串指示你正在使用的平台。比如对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix'。
os.getcwd()函数得到当前工作目录,即当前Python脚本工作的目录路径。
os.getenv()和os.putenv()函数分别用来读取和设置环境变量。
os.listdir()返回指定目录下的所有文件和目录名。
os.remove()函数用来删除一个文件。
os.system()函数用来运行shell命令。
os.linesep字符串给出当前平台使用的行终止符。例如,Windows使用'\r\n',Linux使用'\n'而Mac使用'\r'。
os.path.split()函数返回一个路径的目录名和文件名。
>>> os.path.split('/home/swaroop/byte/code/poem.txt')
('/home/swaroop/byte/code', 'poem.txt')
os.path.isfile()和os.path.isdir()函数分别检验给出的路径是一个文件还是目录。类似地,os.path.existe()函数用来检验给出的路径是否真地存在。
你可以利用Python标准文档去探索更多有关这些函数和变量的详细知识。你也可以使用help(sys)等等。
特殊的方法(我的理解就是框架方法了, 设定好了框架, 你在上面填充好就可以了)
在类中有一些特殊的方法具有特殊的意义,比如__init__和__del__方法,它们的重要性我们已经学习过了。
一般说来,特殊的方法都被用来模仿某个行为。例如,如果你想要为你的类使用x[key]这样的索引操作(就像列表和元组一样),那么你只需要实现__getitem__()方法就可以了。想一下,Python就是对list类这样做的!
下面这个表中列出了一些有用的特殊方法。如果你想要知道所有的特殊方法,你可以在《Python参考手册》中找到一个庞大的列表。
表15.1 一些特殊的方法
名称 说明
__init__(self,...) 这个方法在新建对象恰好要被返回使用之前被调用。 (构造函数)
__del__(self) 恰好在对象要被删除之前调用。 (析构函数)
__str__(self) 在我们对对象使用print语句或是使用str()的时候调用。
__lt__(self,other) 当使用 小于 运算符(<)的时候调用。类似地,对于所有的运算符(+,>等等)都有特殊的方法。
__getitem__(self,key) 使用x[key]索引操作符的时候调用。 相当于操作符[]
__len__(self) 对序列对象使用内建的len()函数的时候调用。
每一个语句块是通过它的缩进层次与其它块区分开来的(相当于C++中的花括号)。但是, 如果你的语句块只包含一句语句,那么你可以在条件语句或循环语句的同一行指明它。(此时不需要通过缩进也可以分开代码块)
>>> flag = True
>>> if flag: print 'Yes'
...
Yes
但这可能是一种不好的习惯, 尽量少用.
通过列表综合,可以从一个已有的列表导出一个新的列表。
例如,你有一个数的列表,而你想要得到一个对应的列表,使其中所有大于2的数都是原来的2倍。
对于这种应用,列表综合是最理想的方法。
例15.1 使用列表综合
#!/usr/bin/python # Filename: list_comprehension.py listone = [2, 3, 4] listtwo = [2 * i for i in listone if i > 2] # 注意这里的格式 print(listtwo) 输出 $ python list_comprehension.py [6, 8]
它如何工作
这里我们为满足条件(if i > 2)的数指定了一个操作(2*i),从而导出一个新的列表。注意原来的列表并没有发生变化。在很多时候,我们都是使用循环来处理列表中的每一个元素,而使用列表综合可以用一种更加精确、简洁、清楚的方法完成相同的工作。
当要使函数接收元组或字典形式的参数的时候,有一种特殊的方法,它分别使用*和**前缀。这种方法在函数需要获取可变数量的参数的时候特别有用。
>>> def powersum(power, *args): ... '''Return the sum of each argument raised to specified power.''' ... total = 0 ... for i in args: ... total += pow(i, power) ... return total ... >>> powersum(2, 3, 4) 25 >>> powersum(2, 10) 100
由于在args变量前有*前缀,所有多余的函数参数都会作为一个元组存储在args中。如果使用的是**前缀,多余的参数则会被认为是一个字典的键/值对。
lambda语句被用来创建新的函数对象,并且在运行时返回它们。
例15.2 使用lambda形式
#!/usr/bin/python # Filename: lambda.py def make_repeater(n): return lambda s: s*n # lambda twice = make_repeater(2) # 设置 n是2 print(twice('word')) # word是lambda的s print(twice(5)) 输出 $ python lambda.py wordword 10
它如何工作
这里,我们使用了make_repeater函数在运行时创建新的函数对象,并且返回它。lambda语句用来创建函数对象。本质上,lambda需要一个参数,后面仅跟单个表达式作为函数体,而表达式的值被这个新建的函数返回。注意,即便是print语句也不能用在lambda形式中,只能使用表达式。
exec语句用来执行储存在字符串或文件中的Python语句。例如,我们可以在运行时生成一个包含Python代码的字符串,然后使用exec语句执行这些语句。下面是一个简单的例子。
>>> exec('print "Hello World"') Hello World
eval语句用来计算存储在字符串中的有效Python表达式。
下面是一个简单的例子。
>>> eval('2*3') 6
这两个语句强大, 相当于字符串也可以作为命令执行, 在C++中必须自己写代码实现的, 而对于解析性的语言, 这是很简单的事情.
现在我的理解结合 lambda来试一下:
#!/usr/bin/python # Filename: lambda1.py def make_repeater(n): return lambda s: s*n twice = make_repeater(2) print(twice(exec('print("word")'))) print(twice(5))
会发生异常
(C++中也有这个东西, 调试版时有效, 就是用来验证用的)
assert语句用来声明某个条件是真的。例如,如果你非常确信某个你使用的列表中至少有一个元素,而你想要检验这一点,并且在它非真的时候引发一个错误,那么assert语句是应用在这种情形下的理想语句。当assert语句失败的时候,会引发一个AssertionError。
>>> mylist = ['item'] >>> assert len(mylist) >= 1 >>> mylist.pop() 'item' >>> assert len(mylist) >= 1 Traceback (most recent call last): File "<stdin>", line 1, in ? AssertionError
repr函数用来取得对象的规范字符串表示。反引号(也称转换符,在键盘左上角与~同一个键)可以完成相同的功能。注意,在大多数时候有eval(repr(object)) == object。
>>> i = [] >>> i.append('item') >>> `i` "['item']" >>> repr(i) "['item']"
基本上,repr函数和反引号用来获取对象的可打印的表示形式。你可以通过定义类的__repr__方法来控制你的对象在被repr函数调用的时候返回的内容。