《python tutorial》--python 学习第三天

时间:2022-06-20 20:52:19

一 类

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异常的工作。上述代码可以写成如下的形式,你一定非常熟悉:

?
12 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语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。

示例代码

view plainprint?
  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