• 简明Python教程(面向对象)


    前言:学习《简明 Python 教程》Swaroop, C. H. 著 沈洁元 译 www.byteofpython.info 摘录,方便以后使用查阅。

    本译本作为原书的派生作品,依照创作公用约定(署名-非派生作品-非商业用途)发布。转载编辑传播请署上作者名字<沈洁元>,就可以免费复制、分发和展示本译本。未得到作者/译作者的允许,禁止把本译本用于商业目的,也不能再在本译本的基础上修改、派生新的作品。
    如果对本书和译本有任何批评和建议,十分欢迎与译本作者联系:orion_val@163.com。

    面向对象的编程

    定义

    面向过程的 编程:根据操作数据的函数或语句块来设计程序。
    面向对象的 编程:把数据和功能结合起来用称为对象的东西包裹起来组织程序的方法。编写大型程序或是寻求一个更加合适的解决方案的时候,就得使用面向对象的编程技术。

    对象面向对象编程的两个主要方面。
    **类:**创建一个新类型,可以包含变量(域)、函数(方法)等。
    **对象:**是类的实例。

    举例:一个int类型的变量,存储整数的变量是int类的实例(对象)。比如,(int类型)Integ=100,Integ就是一个int类的对象(实例)。

    给C/C++/Java/C#程序员的注释
    注意,即便是整数也被作为对象(属于int类)。这和C++、Java(1.5版之前)把整数纯粹作为类型是不同的。

    对象可以使用普通的 属于 对象的变量存储数据。属于一个对象或类的变量被称为域。 对象也可以使用 属 于 类的函数来具有功能。这样的函数被称为类的方法域和方法可以合称为类的属性。

    域有两种类型——属于每个实例/类的对象属于类本身。它们分别被称为实例变量类变量。(后面继承讲到:类本身的域,叫共享域;每个实例/对象的域,叫私有域。)

    类使用class关键字创建。类的域和方法被列在一个缩进块中。

    self

    类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称,但是在调用这个方法的时候你不为这个参数赋值,Python会提供这个值。这个特别的变量指对象本身,按照惯例它的名称是self。

    • PS理解:类的方法(函数)第一个参数是对象本身,名称是self。

    虽然可以给这个参数任何名称,但是 强烈建议 使用self这个名称——其他名称都是不赞成使用的。使用一个标准的名称有很多优点——你的程序读者可以迅速识别它,如果使用self的话,还有些IDE(集成开发环境)也可以帮助你。

    Python中的self等价于C++中的self指针。

    你一定很奇怪Python如何给self赋值以及为何不需要给它赋值。举一个例子会使此变得清晰。假如你有一个类称为MyClass和这个类的一个实例MyObject。当你调用这个对象的方法MyObject.method(arg1, arg2)的时候,这会由Python自动转为MyClass.method(MyObject, arg1, arg2)——这就是self的原理了。
    这也意味着如果有一个不需要参数的方法,还是得给这个方法定义一个self参数。

    创建一个类:

    例 创建一个类
    #!/usr/bin/python
    # Filename: simplestclass.py
    
    class Person:
        pass # An empty block
    
    p = Person()
    print p
    
    #输出
    $ python simplestclass.py
    <__main__.Person instance at 0xf6fcb18c>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用class语句后跟类名,创建了一个新的类。这后面跟着一个缩进的语句块形成类体。在这个例子中,使用了一个空白块,它由pass语句表示。
    接下来,使用类名后跟一对圆括号来创建一个对象/实例。为了验证,简单地打印了这个变量的类型。它告诉我们已经在__main__模块中有了一个Person类的实例
    可以注意到存储对象的计算机内存地址也打印了出来

    对象的方法

    类/对象可以拥有像函数一样的方法这些方法与函数的区别只是一个额外的self变量。

    #例 使用对象的方法
    #!/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()
    
    #输出
    $ python method.py
    Hello, how are you?
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    self的用法:注意sayHi方法没有任何参数,但仍然在函数定义时有self。

    __init__方法

    在Python的类中有很多方法的名字有特殊的重要意义。__init__方法是其一。
    ** __init__方法在类的一个对象被建立时,马上运行** 。 这个方法可以用来对对象做一些希望的 初始化 。注意,这个名称的开始和结尾都是双下划线

    #例 使用__init__方法
    #!/usr/bin/python
    # Filename: class_init.py
    
    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()
    
    #输出
    $ python class_init.py
    Hello, my name is Swaroop
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    把__init__方法定义为取一个参数name(以及普通的参数self)。在这个__init__里,只是创建一个新的域,也称为name。注意它们是两个不同的变量,尽管它们有相同的名字。点号能够区分它们。

    最重要的是,没有专门调用__init__方法,只是在创建一个类的新实例的时候,把参数包括在圆括号内跟在类名后面,从而传递给__init__方法。这是这种方法的重要之处。

    现在,我们能够在我们的方法中使用self.name域。这在sayHi方法中得到了验证。

    类与对象的方法

    类与对象的数据部分:它们只是与类和对象的名称空间 绑定 的普通变量,即这些名称只在这些类与对象的前提下有效。ps:理解即是类或对象的变量。

    两种类型的 域 ——类的变量对象的变量它们根据是类还是对象 拥有 这个变量而区分
    类的变量:一个类的所有对象(实例)共享使用只有一个类变量的拷贝,所以当某个对象对类的变量做了改动的时候,这个改动会反映到所有其他的实例上。 – 共享
    对象的变量:类的每个对象/实例拥有。因此每个对象有自己对这个域的一份拷贝,即它们不是共享的,在同一个类的不同实例中,虽然对象的变量有相同的名称,但是是互不相关的。 --私有独立

    #例 使用类与对象的变量
    #!/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    #对象的变量/域
            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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    说明类与对象的变量的本质:这里,population属于Person类,因此是一个类的变量。name变量属于对象(它使用self赋值)因此是对象的变量。
    __init__方法用一个名字来初始化Person实例。在这个方法中,让population增加1,这是因为增加了一个人。同样可以发现,self.name的值根据每个对象指定,这表明了它作为对象的变量的本质。
    记住,只能使用self变量来参考同一个对象的变量和方法。这被称为 属性参考

    docstring对于类和方法同样有用:可以在运行时使用Person.__doc__和Person.sayHi.__doc__来分别访问类与方法的文档字符串。

    就如同__init__方法一样,还有一个特殊的方法__del__,它在对象消逝的时候被调用。对象消逝即对象不再被使用,它所占用的内存将返回给系统作它用。在这个方法里面,只是简单地把Person.population减1。
    当对象不再被使用时,__del__方法运行,但是很难保证这个方法究竟在 什么时候 运行。如果想要指明它的运行,就得使用del语句,就如同我们在以前的例子中使用的那样。

    给C++/Java/C#程序员的注释
    Python中所有的类成员(包括数据成员)都是 公共的 ,所有的方法都是 有效的 。只有一个例外:如果你使用的数据成员名称以 双下划线前缀 比如 privatevar,Python的名称管理体系会有效地把它作为私有变量。这样就有一个惯例,如果某个变量只想在类或对象中使用,就应该以单下划线前缀。而其他的名称都将作为公共的,可以被其他类/对象使用。记住这只是一个惯例,并不是Python所要求的(与双下划线前缀不同)。同样,注意__del 方法与 destructor 的概念类似。

    继承

    面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过 继承 机制。继承完全可以理解成类之间的 类型和子类型 关系。

    举例:

    问题:
    假设想要写一个程序来记录学校之中的教师和学生情况。他们有一些共同属性,比如姓名、年龄和地址。他们也有专有的属性,比如教师的薪水、课程和假期,学生的成绩和学费。
    分析:
    可以为教师和学生建立两个独立的类来处理它们,但是这样做的话,如果要增加一个新的共有属性,就意味着要在这两个独立的类中都增加这个属性。这很快就会显得不实用。
    一个比较好的方法是创建一个共同的类称为SchoolMember然后让教师和学生的类 继承 这个共同的类。即它们都是这个类型(类)的子类型,然后我们再为这些子类型添加专有的属性。
    使用这种方法有很多优点。如果我们增加/改变了SchoolMember中的任何功能,它会自动地反映到子类型之中。例如,要为教师和学生都增加一个新的身份证域,那么只需简单地把它加到SchoolMember类中。然而,在一个子类型之中做的改动不会影响到别的子类型。另外一个优点是可以把教师和学生对象都作为SchoolMember对象来使用,这在某些场合特别有用,比如统计学校成员的人数。一个子类型在任何需要父类型的场合可以被替换成父类型,即对象可以被视作是父类的实例,这种现象被称为多态现象
    另外,我们会发现在 重用 父类的代码的时候,无需在不同的类中重复它。而如果使用独立的类的话,我们就不得不这么做了。
    在上述的场合中,SchoolMember类被称为 基本类 或 超类 。而Teacher和Student类被称为 导出类 或 子类

    使用继承:

    #例 使用继承
    #!/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.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
    
    #输出
    $ 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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    为了使用继承,把基本类的名称作为一个元组跟在定义类时的类名称之后(子类名称后括号内–基类的名称)。然后,注意到基本类
    的__init__方法专门使用self变量调用,这样就可以初始化对象的基本类部分
    。这一点十分重要——Python不会自动调用基本类的constructor,得亲自专门调用它。

    还观察到在方法调用之前加上类名称前缀,然后把self变量及其他参数传递给它。

    注意,在使用SchoolMember类的tell方法的时候,把Teacher和Student的实例仅仅作为SchoolMember的实例。

    另外,在这个例子中,调用了子类型的tell方法,而不是SchoolMember类的tell方法。可以这样来理解,**Python总是首先查找对应类型的方法,在这个例子中就是如此。如果它不能在导出类中找到对应的方法,它才开始到基本类中逐个查找。**基本类是在类定义的时候,在元组之中指明的。

    一个术语的注释—— 如果在继承元组中列了一个以上的类,那么它就被称作 多重继承 。

    小结:本章,已经研究了类和对象的多个内容以及与它们相关的多个术语。通过本章,可以了解面向对象的编程的优点和缺陷。Python是一个高度面向对象的语言。

    输入/输出

    输入:使用raw_input语句
    输出:使用print语句,也可以使用多种多样的str(字符串)类。例如,能够使用rjust方法来得到一个按一定宽度右对齐的字符串。利用help(str)获得更多详情。

    另一个常用的输入/输出类型是处理文件。创建、读和写文件的能力是许多程序所必需的,本章探索如何实现这些功能。

    文件

    通过创建一个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 = file('poem.txt', 'w') # open for 'w'riting
    f.write(poem) # write text to file
    f.close() # close the file
    
    f = file('poem.txt')
    # if no mode is specified, 'r'ead mode is assumed by default
    
    while True:
        line = f.readline()
        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 using_file.py
    Programming is fun
    When the work is done
    if you wanna make your work also fun:
        use Python!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    首先,通过指明希望打开的文件和模式来创建一个file类的实例。模式可以为读模式(‘r’)、写模式(‘w’)或追加模式(‘a’)。事实上还有多得多的模式可以使用,可以使用**help(file)**来了解它们的详情。

    首先用写模式打开文件,然后使用file类的write方法来写文件,最后用close关闭这个文件。

    接下来,再一次打开同一个文件来读文件。如果没有指定模式,读模式会作为默认的模式。在一个循环中,使用readline方法读文件的每一行。这个方法返回包括行末换行符的一个完整行。所以,当一个 空的 字符串被返回的时候,即表示文件末已经到达了,于是停止循环。
    注意,因为从文件读到的内容已经以换行符结尾,所以在print语句上使用逗号来消除自动换行

    储存器

    Python提供一个标准的模块,称为pickle。使用它可以在一个文件中储存任何Python对象,之后又可以把它完整无缺地取出来。这被称为 持久地 储存对象

    有另一个模块称为cPickle,它的功能和pickle模块完全相同,只不过它是用C语言编写的,因此要快得多(比pickle快1000倍)。可以使用它们中的任一个,而在这里将使用cPickle模块。记住,把这两个模块都简称为pickle模块

    例 储存与取储存
    #!/usr/bin/python
    # Filename: pickling.py
    
    import cPickle as p
    #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
    f = file(shoplistfile, 'w')
    p.dump(shoplist, f) # dump the object to a file
    f.close()
    
    del shoplist # remove the shoplist
    
    # Read back from the storage
    f = file(shoplistfile)
    storedlist = p.load(f)
    print storedlist
    
    
    #输出
    $ python pickling.py
    ['apple', 'mango', 'carrot']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    首先,使用import…as语法。这是一种便利方法,以便于可以使用更短的模块名称。在这个例子中,它还让能够通过简单地改变一行就切换到另一个模块(cPickle或者pickle)!在程序的其余部分的时候,简单地把这个模块称为p。

    为了在文件里储存一个对象,首先以写模式打开一个file对象,然后调用储存器模块的dump函数,把对象储存到打开的文件中。这个过程称为 储存接下来,使用pickle模块的load函数的返回来取回对象。这个过程称为 取储存

    小结

    本章学习了多种类型的输入/输出,及文件处理和使用储存器模块。

    异常

    程序中出现某些 异常的 状况的时候,异常就发生了。例如,当想要读某个文件的时候,而那个文件不存在。或者在程序运行的时候,不小心把它删除了。上述这些情况可以使用异常来处理。

    错误

    一个简单的print语句。假如把print误拼为Print,注意大写,这样Python会 引发 一个语法错误。

    >>> Print 'Hello World'
        File "", line 1
            Print 'Hello World'
          					  ^
    SyntaxError: invalid syntax
    
    >>> print 'Hello World'
    Hello World
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以观察到有一个SyntaxError被引发,并且检测到的错误位置也被打印了出来。这是这个错误的错误处理器 所做的工作。

    try…except

    读取用户的一段输入。按Ctrl-d,看一下会发生什么。

    >>> s = raw_input('Enter something --> ')
    Enter something --> Traceback (most recent call last):
        File "", line 1, in ?
    EOFError
    
    • 1
    • 2
    • 3
    • 4

    Python引发了一个称为EOFError的错误,这个错误基本上意味着它发现一个不期望的 文件尾 (由Ctrl -d表示)
    接下来,将学习如何处理这样的错误。

    处理异常:
    可以使用try…except语句来处理异常把通常的语句放在try-块中,而把错误处理语句放在except-块中。

    例 处理异常
    #!/usr/bin/python
    # Filename: try_except.py
    
    import sys
    
    try:
        s = raw_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'
    
    
    #输出
    $ python try_except.py
    Enter something -->
    Why did you do an EOF on me?
    
    $ python try_except.py
    Enter something --> Python is exceptional!
    Done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    把所有可能引发错误的语句放在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 = raw_input('Enter something --> ')
        if len(s) < 3:
            raise ShortInputException(len(s), 3)
        # Other work can continue as usual here
    except EOFError:
        print '\nWhy did you do an EOF on me?'
    except ShortInputException, x:
        print 'ShortInputException: The input was of length %d, \
            was expecting at least %d' % (x.length,x.atleast)
    else:
        print 'No exception was raised.'
    
    
    输出
    $ python raising.py
    Enter something -->
    Why did you do an EOF on me?
    
    $ python raising.py
    Enter something --> ab
    ShortInputException: The input was of length 2, was expecting at least 3
    
    $ python raising.py
    Enter something --> abc
    No exception was raised.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    **创建自己的异常类型,其实可以使用任何预定义的异常/错误。**这个新的异常类型是ShortInputException类。它有两个域——length是给定输入的长度,atleast则是程序期望的最小长度。
    在except从句中,提供了错误类和用来表示错误/异常对象的变量。这与函数调用中的形参和实参概念类似。在这个特别的except从句中,使用异常对象的length和atleast域来为用户打印一个恰当的消息。

    try…finally

    假如在读一个文件的时候,希望在无论异常发生与否的情况下都关闭文件,该怎么做呢?这可以使用finally块来完成。
    注意,在一个try块下,可以同时使用except从句和finally块。如果要同时使用它们的话,需要把一个嵌入另外一个

    例 使用finally
    #!/usr/bin/python
    # Filename: finally.py
    
    import time
    
    try:
        f = file('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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    进行通常的读文件工作,但是有意在每打印一行之前用time.sleep方法暂停2秒钟。这样做的原因是让程序运行得慢一些(Python由于其本质通常运行得很快)。在程序运行的时候,按Ctrl-c中断/取消程序。可以观察到KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭。

    小结

    本章学习了try…except和try…finally语句的用法。还学习了如何创建自己的异常类型和如何引发异常

    Python标准库

    介绍

    Python标准库是随Python附带安装的,它包含大量极其有用的模块。熟悉Python标准库是十分重要的,因为如果熟悉这些库中的模块,那么大多数问题都可以简单快捷地使用它们来解决。

    可以在Python附带安装的文档的“库参考”一节中了解Python标准库中所有模块的完整内容。

    sys模块

    sys模块包含系统对应的功能。
    sys.argv列表,它包含命令行参数。

    #例 使用sys.argv
    #!/usr/bin/python
    # Filename: cat.py
    
    import sys
    
    def readfile(filename):
        '''Print a file to the standard output.'''
        f = file(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 cat.py
    No action specified.
    
    $ python cat.py --help
    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
    
    $ python cat.py --version
    Version 1.2
    
    $ python cat.py --nonsense
    Unknown option.
    
    $ python cat.py poem.txt
    Programming is fun
    When the work is done
    if you wanna make your work also fun:
        use Python!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    这个程序用来模范Linux/Unix用户熟悉的cat命令。只需要指明某些文本文件的名字,这个程序会把它们打印输出。

    在Python程序运行的时候,即不是在交互模式下,在sys.argv列表中总是至少有一个项目。它就是当前运行的程序名称,作为sys.argv[0](由于Python从0开始计数)。其他的命令行参数在这个项目之后。
    为了使这个程序对用户更加友好,提供了一些用户可以指定的选项来了解更多程序的内容。使用第一个参数来检验程序是否被指定了选项。如果使用了–version选项,程序的版本号将被打印出来。类似地,如果指定了–help选项,提供一些关于程序的解释。使用sys.exit函数退出
    正在运行的程序。和以往一样,可以看一下help(sys.exit)来了解更多详情。如果没有指定任何选项,而是为程序提供文件名的话,它就简单地打印出每个文件地每一行,按照命令行中的顺序一个文件接着一个文件地打印。

    顺便说一下,名称cat是 concatenate 的缩写,它基本上表明了程序的功能——它可以在输出打印一个文件或者把两个或两个以上文件连接/级连在一起打印。

    更多sys的内容:
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    sys.stdin、sys.stdout和sys.stderr它们分别对应你的程序的标准输入、标准输出和标准错误流。

    os模块

    模块包含普遍的操作系统功能。如果希望程序能够与平台无关的话,这个模块是尤为重要的。
    即它允许一个程序在编写后不需要任何改动,也不会发生任何问题,就可以在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')
    
    • 1
    • 2
    • **os.path.isfile()os.path.isdir()**函数分别检验给出的路径是一个文件还是目录。类似地,**os.path.existe()**函数用来检验给出的路径是否真地存在。

    可以利用Python标准文档去探索更多有关这些函数和变量的详细知识。也可以使用help(sys)等等。

    小结

    本章学习Python标准库中的sys模块和os模块的一部分功能。利用Python标准文档去学习这两个模块以及其他模块的更多内容。

    更多Python的内容

    特殊的方法

    在类中有一些特殊的方法具有特殊的意义,比如__init__和__del__方法。
    一般说来,特殊的方法都被用来模仿某个行为。例如,如果想要为你的类使用x[key]这样的索引操作(就像列表和元组一样),那么只需要实现__getitem__()方法就可以了。想一下,Python就是对list类这样做的!

    表15.1 一些特殊的方法(如果想要知道所有的特殊方法,可以在《Python参考手册》中找到一个庞大的列表。)
    名称 说明
    init(self,…) 这个方法在新建对象恰好要被返回使用之前被调用。
    del(self) 恰好在对象要被删除之前调用。
    str(self) 在我们对对象使用print语句或是使用str()的时候调用。
    lt(self,other) 当使用 小于 运算符(<)的时候调用。类似地,对于所有的运算符(+,>等等)都有特殊的方法。
    getitem(self,key) 使用x[key]索引操作符的时候调用。
    len(self) 对序列对象使用内建的len()函数的时候调用。

    单语句块

    每一个语句块是通过它的缩进层次与其它块区分开来的。然而这在大多数情况下是正确的,但是并非100%的准确。如果语句块只包含一句语句,那么可以在条件语句或循环语句的同一行指明它。

    >>> flag = True
    >>> if flag: print 'Yes'
    ...
    Yes
    
    • 1
    • 2
    • 3
    • 4

    单个语句被直接使用而不是作为一个独立的块使用。虽然这样做可以使程序变得 小一些 ,但是除了检验错误之外强烈建议不要使用这种缩略方法。不使用它的一个主要的理由是一旦使用了恰当的缩进,就可以很方便地添加一个额外的语句。

    另外,注意在使用交互模式的Python解释器的时候,它会通过恰当地改变提示符来帮助输入语句。在上面这个例子中,当输入了关键字if之后,Python解释器把提示符改变为…以表示语句还没有结束。在这种情况下,按回车键用来确认语句已经完整了。然后,Python完成整个语句的执行,并且返回原来的提示符并且等待下一句输入。

    列表综合

    **通过列表综合,可以从一个已有的列表导出一个新的列表。**例如,有一个数的列表,而想要得到一个对应的列表,使其中所有大于2的数都是原来的2倍。对于这种应用,列表综合是最理想的方法。

    #例 使用列表综合
    #!/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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    为满足条件(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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    由于在args变量前有*前缀,所有多余的函数参数都会作为一个元组存储在args中。如果使用的是** 前缀,多余的参数则会被认为是一个字典的键/值对。

    lambda形式

    lambda语句被用来创建新的函数对象,并且在运行时返回它们。

    #例 使用lambda形式
    #!/usr/bin/python
    # Filename: lambda.py
    
    def make_repeater(n):
        return lambda s: s*n
        
    twice = make_repeater(2)
    print twice('word')
    print twice(5)
    
    
    #输出
    $ python lambda.py
    wordword
    10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用了make_repeater函数在运行时创建新的函数对象,并且返回它。**lambda语句用来创建函数对象。**本质上,lambda需要一个参数,后面仅跟单个表达式作为函数体,而表达式的值被这个新建的函数返回。注意,即便是print语句也不能用在lambda形式中,只能使用表达式。

    exec和eval语句

    exec语句用来执行储存在字符串或文件中的Python语句。
    例如,可以在运行时生成一个包含Python代码的字符串,然后使用exec语句执行这些语句。下面是一个简单的例子。

    >>> exec 'print "Hello World"'
    Hello World
    
    • 1
    • 2

    eval语句用来计算存储在字符串中的有效Python表达式。

    >>> eval('2*3')
    6
    
    • 1
    • 2

    assert语句

    assert语句用来声明某个条件是真的。
    例如,如果非常确信某个你使用的列表中至少有一个元素,而想要检验这一点,并且在它非真的时候引发一个错误,那么assert语句是应用在这种情形下的理想语句。当assert语句失败的时候,会引发一个AssertionError。

    >>> mylist = ['item']
    >>> assert len(mylist) >= 1
    >>> mylist.pop()
    'item'
    >>> assert len(mylist) >= 1
    Traceback (most recent call last):
        File "", line 1, in ?
    AssertionError
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    repr函数

    repr函数用来取得对象的规范字符串表示。反引号(也称转换符)可以完成相同的功能。 注意,在大多数时候有eval(repr(object)) == object。

    >>> i = []
    >>> i.append('item')
    >>> `i`
    "['item']"
    >>> repr(i)
    "['item']"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    基本上,repr函数和反引号用来获取对象的可打印的表示形式。可以通过定义类的__repr__方法来控制对象在被repr函数调用的时候返回的内容。

    探索

    图形软件

    使用Python的GUI库——需要使用这些库来用Python语言创建自己的图形程序。使用GUI库和它们的Python绑定,可以创建自己的IrfanView、Kuickshow软件或者任何别的类似的东西。绑定能够使用Python语言编写程序,而使用的库本身是用C、C++或者别的语言编写的。

    有许多可供选择的使用Python的GUI:

    PyQt

    Qt工具包的Python绑定。Qt工具包是构建KDE的基石。Qt,特别是配合Qt Designer和出色的Qt文档之后,它极其易用并且功能非常强大。可以在Linux下免费使用它,但是如果在Windows下使用它需要付费。使用PyQt,可以在Linux/Unix上开发免费的(GPL约定的)软件,而开发具产权的软件则需要付费。
    PyQt资源:
    《使用Python语言的GUI编程:Qt版》 http://www.opendocs.org/pyqt/
    官方主页:https://riverbankcomputing.com/pyqt/index.php

    PyGTK

    GTK+工具包的Python绑定。GTK+工具包是构建GNOME的基石。GTK+在使用上有很多怪癖的地方,不过一旦习惯了,可以非常快速地开发GUI应用程序。Glade图形界面设计器是必不可少的,而文档还有待改善。GTK+在Linux上工作得很好,而它的Windows接口还不完整。可以使用GTK+开发免费和具有产权的软件。
    官方主页:http://www.pygtk.org/

    wxPython

    wxWidgets工具包的Python绑定。wxPython有与它相关的学习方法。它的可移植性极佳,可以在Linux、Windows、Mac甚至嵌入式平台上运行。有很多wxPython的IDE,其中包括GUI设计器以及如SPE(Santi’s Python Editor)和wxGlade那样的GUI开发器:
    SPE(Santi’s Python Editor):http://spe.pycs.net/
    wxGlade:https://wxglade.sourceforge.net/
    可以使用wxPython开发免费和具有产权的软件,请查阅官方主页:https://www.wxpython.org/

    TkInter

    现存最老的GUI工具包之一。如果使用过IDLE,它就是一个TkInter程序。在PythonWare.org(http://www.pythonware.com/library/tkinter/introduction/index.htm)上的TkInter文档是十分透彻的。TkInter具备可移植性,可以在Linux/Unix和Windows下工作。重要的是,TkInter是标准Python发行版的一部分。

    其他:
    获取更多选择,请参阅Python.org上的GUI编程wiki页http://www.python.org/cgi-bin/moinmoin/GuiProgramming

    GUI工具概括

    并没有单一的标准Python GUI工具。建议你根据情况在上述工具中选择一个。首要考虑的因素是否愿意为GUI工具付费。其次考虑的是想让程序运行在Linux下、Windows下还是两者都要。第三个考虑因素根据Linux下的KDE用户还是GNOME用户而定。

    进阶学习

    Python标准库

    http://docs.python.org/
    一个丰富的库,在大多数时候,可以在这个库中找到你所需的东西。这被称为Python的“功能齐全”理念。强烈建议在开始开发大型Python程序之前浏览一下Python标准文档。

    Python.org

    https://www.python.org/
    ——Python编程语言的官方主页。可以在上面找到Python语言和解释器的最新版本。另外还有各种邮件列表活跃地讨论Python的各方面内容。

    comp.lang.python

    讨论Python语言的世界性新闻组。可以把疑惑和询问贴在这个新闻组上。可以使用Google群(http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&group=comp.lang.python)在线访问这个新闻组,或加入作为新闻组镜像的邮件列表(https://mail.python.org/mailman/listinfo/python-list)。

    《Python实用大全》

    http://aspn.activestate.com/ASPN/Python/Cookbook/
    一个极有价值的秘诀和技巧集合,它帮助解决某些使用Python的问题。这是每个Python用户必读的一本书。

    《迷人的Python》

    http://gnosis.cx/publish/tech_index_cp.html
    David Mertz编著的一系列优秀的Python相关文章。

    《深入理解Python》

    http://www.diveintopython.org/
    给有经验的Python程序员的一本很优秀的书。如果已经完整地阅读了本书,那么强烈建议接下来阅读《深入理解Python》。它覆盖了包括XML处理、单元测试和功能性编程在内的广泛的主题。

    Jython

    http://www.jython.org/
    用Java语言实现的Python解释器。这意味着可以用Python语言编写程序而同时使用Java库!Jython是一个稳定成熟的软件。如果也是一个Java程序员,强烈建议尝试一下Jython。

    IronPython

    https://www.hugedomains.com/domain_profile.cfm?d=caddr.com
    用C#语言实现的Python解释器,可以运行在.NET、Mono和DotGNU平台上。这意味着可以用Python语言编写程序而使用.NET库以及其他由这三种平台提供的库!IronPython还只是一个前期alpha测试软件,现在还只适合用来进行试验。Jim Hugunin,IronPython的开发者,已经
    加入了微软公司,将在将来全力开发一个完整版本的IronPython。Lython是Python语言的Lisp前段。它类似于普通的Lisp语言,会被直接编译为Python字节码,这意味着它能与我们普通的Python代码协同工作。
    另外还有很多很多的Python资源。其中比较有趣的有Daily Python-URL!,它使保持与Python的最新进展同步。另外还有Vaults of Parnassus、ONLamp.com PythonDevCenter、dirtSimple.org、Python Notes等等。

    其他python资源

    Daily Python-URL! :http://www.pythonware.com/daily/
    Vaults of Parnassus:http://www.vex.net/parnassus/
    ONLamp.com Python DevCenter:https://www.oreilly.com/radar/
    dirtSimple.org:https://dirtsimple.org/
    Python Notes:http://pythonnotes.blogspot.com/
    等等。

    附录

  • 相关阅读:
    使用原生div制作table表格
    FPGA HLS 基于stream的池化单元 hls优化&约束
    GB28181协议-SIP协议详解
    NFTFi赛道版图概览
    Nginx配置文件详解
    了解string以及简单模拟实现(c++)
    【GESP考级C++】1级样题 闰年统计
    Apollo与TypeScript:强大类型检查在前端开发中的应用
    第十三篇-Tesla P40+ModelScope+Gradio+QWen-14B-Int4
    第4章 决策树
  • 原文地址:https://blog.csdn.net/qq_42041303/article/details/131114761