zoukankan      html  css  js  c++  java
  • 《python tutorial》python 学习第三天

    一 类

    1 简介

    Python 在尽可能不增加新的语法和语义的情况下加入了类机制。这种机制是 C++ 和 Modula-3 的混合。像模块那样, Python 中的类没有在用户和定义之间建立一个绝对的 屏障,而是依赖于用户自觉的不去“破坏定义”。

    用 C++ 术语来讲,所有的类成员(包括数据成员)都是公有( public )的, 所有的成员函数都是 虚 (virtual )的。

    对象是被特化的,多个名字(在多个作用域中)可以绑定同一个对象。这相当于 其它语言中的别名。这通用有助于优化程序,因为 别名的行为在某些方面类似于指针。用那些不可 变的基本类型(数值、字符串、元组)时也可以很放心的忽视它。然而,在 Python 代码调用字典、列表之类可变对象,以及大多数涉及程序外部实体(文件、窗体等等)的类型时,这一语义就会有影响。


    2作用域和命名空间

    命名空间 是从命名到对象的映射。

    作用域的搜索顺序类似于c++,从内层到外层。
    Python 的一个特别之处在于——如果没有使用 global 语法——其赋值 操作总是在最里层的作用域。赋值不会复制数据——只是将命名绑定到对象。删除 也是如此: del x 只是从局部作用域的命名空间中删除命名 x 。类似指针。

    3 类

    (1) 类

    类支持两种操作:属性引用和实例化。
    class MyClass:
    """A simple example class"""
    i = 12345
    def f(self):
    return 'hello world'

    那么 MyClass.i 和 MyClass.f 是有效的属性引用, __doc__ ` 也是一个有效的属性,返回类的文档字符串:"A simple example class" 。

    x = MyClass()以上创建了一个新的类 实例 并将该对象赋给局部变量 x 。



    >>> class Complex:
    ...
    def __init__(self, realpart, imagpart):
    ...
    self.r = realpart
    ...
    self.i = imagpart
    ...
    >>> x = Complex(3.0, -4.5)
    >>> x.r, x.i
    (3.0, -4.5)
    init () 方法类似于构造函数,可以初始化。


    (2)实例

    实例对象唯一可用的操作就是属性引用。

    数据属性类似C++中的“数据成员”。奇怪的是,数据属性不需要声明,第一次使用时它们就会生成。类似局 部变量。

    例如,如果 x 是前面创建的 MyClass 实例,下面这段代码会打印出 16 而在堆 栈中留下多余的东西
    x.counter = 1
    while x.counter < 10:
    x.counter = x.counter * 2
    print x.counter
    del x.counter


    另一种为实例对象所接受的引用属性是 方法 。类似与C++的“成员函数”

    x.f 和 MyClass.f 不同--它是一个 方法对象 ,不是一个函数对象。


    (3)方法对象

    方法的特别之处在于实例对象作为函数的第一 个参数传给了函数。在我们的例子中,调用 x.f() 相当于 MyClass.f(x) 。通 常,以 n 个参数的列表去调用一个方法就相当于将
    方法的对象插入到参数列表 的最前面后,以这个列表去调用相应的函数

    x.f 是一个方法对象,它可以存储起来以后调用。例如
    xf = x.f
    while True:
    print xf()


    4 说明


    (1)同名的数据属性会覆盖方法属性,为了避免可能的命名冲突--这在大型程序中 可能会导致难以发现的bug --最好以某种命名约定来避免冲突。可选的约定 包括方法的首字母大写,数据属性名前缀小写(可能只是一个下划线),或者方 法使用动词而数据属性使用名词。

    (2)数据属性可以由方法引用,也可以由普通用户(客户)调用。换句话说,类不能 实现纯抽象数据类型。
    (3)通常方法的第一个参数命名为 self 。这仅仅是一个约定:对 Python 而言, self 绝对没有任何特殊含义。但是,它其实一般用来实现自身的引用。


    5继承

    (1)

    命名 BaseClassName (示例中的基类名)必须与派生类定义在一个作用域内。基类定义在另一个模块中时用表达式,
    class DerivedClassName(modname.BaseClassName):

    函数 isinstance() 用于检查实例类型: isinstance(obj, int) 只有在 obj. class其它从 int 继承 的类型

    函数 issubclass() 用于检查类继承: issubclass(bool, int) 为 True ,因为 bool 是 int 的子类。


    (2)多重继承

    查找属性: 对于旧式的类,唯一的规则顺序是深度优先,

    在 new-style 类中,必须有动态调整顺序,因为所有的多继承会有一到多个菱 形关系(指有至少一个祖先类可以从子类经由多个继承路径到达)。为了防止重复访问基类,通过动态的线性化 算法,每个类都按从左到右的顺序特别指定了顺序,每个祖先类只调用一次,这 是单调的(意味着一个类被继承时不会影响它祖先的次序)。


    6 私有变量

    (1)一个下划线开头的命名

    只能从对像内部访问的“私有”实例变量,在 Python 中不存在。然而,也有 一个变通的访问用于大多数Python 代码:以一个下划线开头的命名(例如 spam )会被处理为 API 的非公开部分(无论它是一个函数、方法或数据 成员)。它会被视为一个实现细节,无需公开。

    (2)两个下划线开头的命名

    因为有一个正当的类私有成员用途(即避免子类里定义的命名与之冲突),Python 提供了对这种结构的有限支持,称为 name mangling(命名编码) 。任何形如 spam 的标识(前面至少两个下划线,后面至多一个),被替代为 _classname__spam ,去掉前导下划线的 classname 即当前的类名。

    7 类实现结构体

    有时类似或中“结构(struct)”的数据类型 有用,它将一组已命名的数据项绑定在一起。一个空的类定义可以很好的实现
    class Employee:
    pass
    john = Employee() # Create an empty employee record
    # Fill the fields of the record
    john.name = 'John Doe'
    john.dept = 'computer lab'
    john.salary = 1000

    二  迭代器和生成器

    for是迭代器的语法糖.

    设计者主要的初衷是为了在遍历大数据量的时候,不用把大数据块全部都塞入到内存块中,从而达到节省内存的目的,当然还有可以对非线性数据进行有效的遍历,

    1  迭代器Iterators

    实际上,因为迭代操作如此普遍,Python专门将关键字for用作了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。上述代码可以写成如下的形式,你一定非常熟悉:

    1
    2
    for val in lst:
        printval

    首先Python将对关键字in后的对象调用iter函数获取迭代器,然后调用迭代器的next方法获取元素,直到抛出StopIteration异常。对迭代器调用iter函数时将返回迭代器自身,所以迭代器也可以用于for语句中,不需要特殊处理。

    常用的几个内建数据结构tuple、list、set、dict都支持迭代器,字符串也可以使用迭代操作。

    你也可以自己实现一个迭代器,如上所述,只需要在类的__iter__方法中返回一个对象,这个对象拥有一个next()方法,这个方法能在恰当的时候抛出StopIteration异常即可。但是需要自己实现迭代器的时候不多,即使需要,使用生成器会更轻松。

     


    实现了迭代器协议的就可以使用for elem lin list 操作

    迭代器仅是一容器对象,它实现了迭代器协议。它有两个基本方法:
    1)next方法
    返回容器的下一个元素
    2)__iter__方法
    返回迭代器自身

    迭代器可使用内建的iter方法创建,见例子:
    >>> i = iter('abc')
    >>> i.next()
    'a'
    >>> i.next()
    'b'
    >>> i.next()
    'c'
    >>> i.next()
    Traceback (most recent call last):
      File "<string>", line 1, in <string>
    StopIteration:


    给自己的类添加迭代器行为

    >>> class Reverse:
        "Iterator for looping over a sequence backwards"
        def __init__(self, data):
            self.data = data
            self.index = len(data)
        def __iter__(self):
            return self
        def next(self):
            if self.index == 0:
                raise StopIteration
            self.index = self.index - 1
            return self.data[self.index]

    >>> for char in Reverse('spam'):
        print char
    
    m
    a
    p
    s
    

    2  生成器

    (1)生成器是特殊的迭代器。关键字yield


    >>> defgenerator1():
    ... 

       yield 'first'
    ...    yield 'second'
    ...    yield 'third'
    ...
    >>>gen=generator1()
    >>> gen
    <generator object generator1 at0x013511C0>
    >>> gen.next()
    'first'
    >>> gen.next()
    'second'
    >>> gen.next()
    'third'
    >>> gen.next()
    Traceback (most recent call last):
      File "<stdin>",line 1, in <module>
    StopIteration

    在这个例子中,我们首先定义一个叫做generator1的生成器,它会生成三个值:“first”、“second”和“third”三个字符串。当我们创建一个新的生成器对象(gen)时,它就开始执行函数。每当你调用生成器next方法时,它会继续执行这个函数,直到生成下一个值。当生成器达到块结尾的时候,或者碰到返回语句时,它会产生一个StopIteration异常。

    生成器本质上就是迭代器,这也就是说它们可以用在很多表达式里,而不需要用序列(列表等)。例如,你可以不使用上面的代码,而改用下面的:

    >>>gen2=generator1()
    >>> for i ingen2:
    ...    print i
    ...
    first
    second
    third

    类似的,你可以一次就把生成器转换成为列表:

    >>>gen3=generator1()
    >>> list(gen3)
    ['first', 'second', 'third']



    (2)具体说就是在函数的执行过程中,yield语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。

    示例代码

    1. >>> def fib(max):  
    2.     a, b = 01            
    3.     while a < max:  
    4.         yield a            
    5.         a, b = b, a + b  
    6.   
    7.       
    8. >>> for i in fib(1000):  
    9.     print(i)  
    10.   
    11.       
    12. 0  
    13. 1  
    14. 1  
    15. 2  
    16. 3  
    17. 5  
    18. 8  
    19. 13  
    20. 21  
    21. 34  
    22. 55  
    23. 89  
    24. 144  
    25. 233  
    26. 377  
    27. 610  
    28. 987  
    29.   
    30. >>>f = fib(1000)  
    31. >>>f.next()            #python 3.0 要写成f.__next__()  否则出错  
    32. 0  
    33. >>>f.next()  
    34. 1  
    35. >>>f.next()  
    36. 1  
    37. >>>f.next()  
    38. 2  

    #python 3.0 要写成f.__next__()  否则出错  AttributeError: 'generator' object has no attribute 'next'

    在函数fib(max)内定义了一个生成器,但是对fib(max)的调用永远只能获得一个单独的生成器对象,而不是执行函数里面的语句,这个对象(generator object)包含了函数的原始代码和函数调用的状态,这状态包括函数中变量值以及当前的执行点——函数在yield语句处暂停(suspended),返回当前的值并储存函数的调用状态,当需要下一个条目(item)时,可以再次调用next,从函数上次停止的状态继续执行,知道下一个yield语句。

    生成器和函数的主要区别在于函数 return a value,生成器 yield a value同时标记或记忆 point of the yield 以便于在下次调用时从标记点恢复执行。yield 使函数转换成生成器,而生成器反过来又返回迭代器。

    有三种方式告诉循环生成器中没有更多的内容:

    执行到函数的末尾("fall off the end")
    用一个return语句(它可能不会返回任何值)
    抛出StopIteration异常

    总的来说生成器是一类特殊 迭代器。 一个产生值的函数 yield 是一种产生一个迭代器却不需要构建迭代器的精密小巧的方法

    内容参考 http://www.cnblogs.com/moinmoin/archive/2011/11/15/1990840.html


    (3)生成器表达式比完整的生成器定义更简 洁


    如下,

    >>> from math import pi, sin
    >>> sine_table = dict((x, sin(x*pi/180)) for x in range(0, 91))
    >>> unique_words = set(word
    for line in page
    for word in line.split())
    >>> valedictorian = max((student.gpa, student.name) for student in graduates)
    >>> data = 'golf'
    >>> list(data[i] for i in range(len(data)-1,-1,-1))
    ['f', 'l', 'o', 'g']


    三  异常和差错


    1 语法错误和异常

    异常一般是运行时发生的逻辑性等方面的问题,程序员自己控制throw,catch等。错误一般在编译的时候就可以发现,我们还可以用assert来检测,语法或者系统级别错误,较为严重。错误是一种100%会报错的,异常是潜在概率报错的。

    2控制异常

    (1)

    import sys
    try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
    except IOError as (errno, strerror):
    print "I/O error({0}): {1}".format(errno, strerror)
    except ValueError:
    print "Could not convert data to an integer."

    except:
    print "Unexpected error:", sys.exc_info()[0]
    raise



    首先,执行 try 子句;

    如果没有异常发生, except 子句 在 try 语句执行完毕后就被忽略了。

    如果在 try 子句执行过程中发生了异常,那么该子句其余的部分就会被忽略。 如果异常匹配于except 关键字后面指定的异常类型,就执行对应的except子 句。然后继续执行 try 语句之后的代码。如果发生了一个异常,在 except 子句中没有与之匹配的分支,它就会传递到 上一级 try 语句中。

    如果最终仍找不到对应的处理语句,它就成 为一个 未处理异常 ,终止程序运行,显示提示信息。


    (2)一个except子句可以在括号中列出多个异常的名字,例如
    ... except (RuntimeError, TypeError, NameError):
    ...
    pass

    (3)最后一个 except 子句可以省略异常名,把它当做一个通配项使用。

    一定要慎用 这种方法,因为它很可能
    会屏蔽掉真正的程序错误,使人无法发现!它也可以用 于打印一行错误信息,然后重新抛出异常(可以使
    调用者更好的处理异常)

    (4)try ... except 语句可以带有一个 else 子句 , 该子句只能出现在所有 except 子句之后。当 try 语
    句没有抛出异常时,需要执行一些代码,可以使用 这个子句。例如
    for arg in sys.argv[1:]:
    try:
    f = open(arg, 'r')
    except IOError:
    print 'cannot open', arg
    else:
    print arg, 'has', len(f.readlines()), 'lines'
    f.close()

    (5)发生异常时,可能会有一个附属值,作为异常的 参数 instance.args存在。

    就是说 异常有一个参数args用来存储信息,还有一个__str__()方法直接返回参数。这两个都是可选的。


    >>> try:
    ...
    raise Exception('spam', 'eggs')
    ... except Exception as inst:
    ...
    print type(inst)
    # the exception instance
    ...
    print inst.args
    # arguments stored in .args
    ...
    print inst
    # __str__ allows args to printed directly
    ...
    x, y = inst
    # __getitem__ allows args to be unpacked directly
    ...
    print 'x =', x
    ...
    print 'y =', y
    ...
    <type 'exceptions.Exception'>
    ('spam', 'eggs')
    ('spam', 'eggs')
    x = spam
    y = eggs

    对于未处理的异常,如果它有一个参数,那做就会作为错误信息的最后一部分 (“明细”)打印出来。


    3抛出异常

    序员可以用 raise 语句强制指定的异常发生。例如

    >>> raise NameError('HiThere')
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    NameError: HiThere


    要抛出的异常由 raise 的唯一参数标识。它必需是一个异常实例或 异常类(继承自 Exception 的类)。

    如果你需要明确一个异常是否抛出,但不想处理它, raise 语句可以让你很简单的重新抛出该异常。
    >>> try:
    ...
    raise NameError('HiThere')
    ... except NameError:
    ...
    print 'An exception flew by!'
    ...
    raise
    ...
    An exception flew by!
    Traceback (most recent call last):
    File "<stdin>", line 2, in ?
    NameError: HiThere


    4 用户自定义异常

    在程序中可以通过创建新的异常类型来命名自己的异常(Python 类的内容请参 见 Classes 类 )。异常类
    通常应该直接或间接的从 Exception 类派生,例如
    >>> class MyError(Exception):
    ...
    def __init__(self, value):
    ...
    self.value = value
    ...
    def __str__(self):
    ...
    return repr(self.value)
    ...
    >>> try:
    ...
    raise MyError(2*2)
    ... except MyError as e:
    ...
    print 'My exception occurred, value:', e.value
    ...
    My exception occurred, value: 4
    >>> raise MyError('oops!')
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    __main__.MyError: 'oops!'
    In this example, the def

    在这个例子中,:class:Exception 默认的init () 被覆盖。新的方式简单的创建 value 属性。这就替换了原来创建 args 属性的方式。


    以供异常处理句柄提取。如果一个新创建的模块中需要 抛出几种不同的错误时,一个通常的作法是为该模
    块定义一个异常基类,然后针 对不同的错误类型派生出对应的异常子类。
    class Error(Exception):
    """Base class for exceptions in this module."""
    pass
    class InputError(Error):
    """Exception raised for errors in the input.
    Attributes:
    expr -- input expression in which the error occurred
    msg -- explanation of the error
    """
    def __init__(self, expr, msg):
    self.expr = expr
    self.msg = msg

    class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.
    Attributes:
    prev -- state at beginning of transition
    next -- attempted new state
    msg -- explanation of why the specific transition is not allowed
    """
    def __init__(self, prev, next, msg):
    self.prev = prev
    self.next = next
    self.msg = msg




    5 定义清理行为---finally

    try 语句还有另一个可选的子句,目的在于定义在任何情况下都一定要执行的功 能。例如
    >>> try:
    ...
    raise KeyboardInterrupt
    ... finally:
    ...
    print 'Goodbye, world!'
    ...
    Goodbye, world!
    KeyboardInterrupt



    不管有没有发生异常, finally 子句 在程序离开 try 后都一定 会被执行。当 try 语句中发生了未被
    except 捕获的 异常(或者它发生在 except 或 else 子句中),在 finally 子句执行完后它会被重新抛
    出。 try 语句经 由 break ,:keyword:continue 或 return 语句退 出也一样会执行 finally 子句。
    >>> def divide(x, y):
    ...
    try:
    ...
    result = x / y
    ...
    except ZeroDivisionError:
    ...
    print "division by zero!"
    ...
    else:
    ...
    print "result is", result
    ...
    finally:
    ...
    print "executing finally clause"
    ...
    >>> divide(2, 1)
    result is 2
    executing finally clause
    >>> divide(2, 0)
    division by zero!
    executing finally clause
    >>> divide("2", "1")
    executing finally clause
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<stdin>", line 3, in divide
    TypeError: unsupported operand type(s) for /: 'str' and 'str'


    6 预定义清理行为


    for line in open("myfile.txt"):
    print line


    这段代码的问题在于在代码执行完后没有立即关闭打开的文件。这在简单的脚本 里没什么,但是大型应用
    程序就会出问题。 with 语句使得文件之类的对象可以 确保总能及时准确地进行清理。

    with open("myfile.txt") as f:
    for line in f:
    print line

    语句执行后,文件 f 总会被关闭,即使是在处理文件中的数据时出错也一样。 其它对象是否提供了预定义
    的清理行为要查看它们的文档。






    参考:

    python tutorial 中文版

    迭代器:http://blog.csdn.net/chszs/article/details/3852669

    深入理解迭代器:http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html

    生成器:http://www.cnblogs.com/cacique/archive/2012/02/24/2367183.html

  • 相关阅读:
    JavaScript数据结构——模仿ES6中定义的类似的Set类
    webpack简单的项目配置发生的错误
    三国
    把握中国经济的大局与动力
    人生道路
    C++
    https://计算机等级分类总结
    https://计算机四级
    https://计算机三级
    https://计算机二级
  • 原文地址:https://www.cnblogs.com/catkins/p/5270665.html
Copyright © 2011-2022 走看看