2-1 迭代器与生成器

  • 迭代器是一个可以记住遍历的位置的对象。

  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

  • 迭代器有两个基本的方法:iter()next()

  • 字符串,列表或元组对象都可用于创建迭代器

  • ```python

    e.g.1

    list=[1,2,3,4]
    it = iter(list) # 创建迭代器对象
    print (next(it)) # 输出迭代器的下一个元素
    1
    print (next(it))
    2

    e.g.2

    list=[1,2,3,4]
    it = iter(list) # 创建迭代器对象
    for x in it:

    print (x, end=" ")
    

    e.g.3

    import sys # 引入 sys 模块

    list=[1,2,3,4]
    it = iter(list) # 创建迭代器对象

    while True:

    try:
        print (next(it))
    except StopIteration:
        sys.exit()
    
    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

    ## 1. 迭代器的创建

    - 把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 。

    - __iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
    - __next__() 方法(Python 2 里是 next())会返回下一个迭代器对象。

    - e.g.

    - ```python
    class MyNumbers:
    def __iter__(self):
    self.a = 1
    return self

    def __next__(self):
    x = self.a
    self.a += 1
    return x

    myclass = MyNumbers()
    myiter = iter(myclass)

    print(next(myiter))
    print(next(myiter))
    print(next(myiter))
    print(next(myiter))
    print(next(myiter))

2. Stoplteration

  • StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

  • e.g.

    • ```python
      class MyNumbers:
      def iter(self):

      self.a = 1
      return self
      

      def next(self):

      if self.a <= 20:
        x = self.a
        self.a += 1
        return x
      else:
        raise StopIteration
      

      myclass = MyNumbers()
      myiter = iter(myclass)

      for x in myiter:
      print(x)

      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

      ## 3. 生成器

      - 在 Python 中,使用了 yield 的函数被称为生成器(generator)。

      - 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

      - 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

      - 调用一个生成器函数,返回的是一个迭代器对象。

      - e.g.

      - ```python
      import sys

      def fibonacci(n): # 生成器函数 - 斐波那契
      a, b, counter = 0, 1, 0
      while True:
      if (counter > n):
      return
      yield a
      a, b = b, a + b
      counter += 1
      f = fibonacci(10) # f 是一个迭代器,由生成器返回生成

      while True:
      try:
      print (next(f), end=" ")
      except StopIteration:
      sys.exit()

2-2 函数

  • 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段

1. 函数的定义

img

规则:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()
  • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号 : 起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。

语法:

1
2
def 函数名(参数列表):
函数体

2. 参数传递

python 函数的参数传递:

  • 不可变类型:类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。
  • 可变类型:类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响

3. 参数

以下是调用函数时可使用的正式参数类型:

  • 必需参数、关键字参数、默认参数、不定长参数

1)必须参数

  • 必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
1
2
3
4
5
6
7
8
#可写函数说明
def printme( str ):
"打印任何传入的字符串"
print (str)
return

# 调用 printme 函数,不加参数会报错
printme()

2) 关键字参数

  • 关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
  • 使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
1
2
3
4
5
6
7
8
9
#可写函数说明
def printinfo( name, age ):
"打印任何传入的字符串"
print ("名字: ", name)
print ("年龄: ", age)
return

#调用printinfo函数
printinfo( age=50, name="runoob" )

3) 默认参数

  • 调用函数时,如果没有传递参数,则会使用默认参数。
1
2
3
4
5
6
7
8
9
10
11
#可写函数说明
def printinfo( name, age = 35 ):
"打印任何传入的字符串"
print ("名字: ", name)
print ("年龄: ", age)
return

#调用printinfo函数
printinfo( age=50, name="runoob" )
print ("------------------------")
printinfo( name="runoob" )

4) 不定长参数

  • 你可能需要一个函数能处理比当初声明时更多的参数。
  • 加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
# 可写函数说明
def printinfo( arg1, *vartuple ):
"打印任何传入的参数"
print ("输出: ")
print (arg1)
print (vartuple)

# 调用printinfo 函数
printinfo( 70, 60, 50 )

输出:
70
(60, 50)
  • 还有一种就是参数带两个星号 **,参数会以字典的形式导入。
1
2
3
4
5
6
7
8
9
10
11
12
13
# 可写函数说明
def printinfo( arg1, **vardict ):
"打印任何传入的参数"
print ("输出: ")
print (arg1)
print (vardict)

# 调用printinfo 函数
printinfo(1, a=2,b=3)

输出:
1
{'a': 2, 'b': 3}
  • 声明函数时,参数中星号 * 可以单独出现,后参数要求为关键字形参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def f(a,b,*,c):
return a+b+c
f(1,2,3)

# 报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were given


f(1,2,c=3) # 正常

输出:
6

5)匿名函数

Python 使用 lambda 来创建匿名函数。

所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

  • lambda 只是一个表达式,函数体比 def 简单很多。
  • lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。
  • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
  • 虽然 lambda 函数看起来只能写一行,却不等同于 C 或 C++ 的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 语法
lambda [arg1 [,arg2,.....argn]]:expression

# e.g.1
x = lambda a : a + 10
print(x(5))

输出
15

# e.g.2
# 可写函数说明
sum = lambda arg1, arg2: arg1 + arg2

# 调用sum函数
print ("相加后的值为 : ", sum( 10, 20 ))
print ("相加后的值为 : ", sum( 20, 20 ))

输出
相加后的值为 : 30
相加后的值为 : 40
  • 可以将匿名函数封装在一个函数内,这样可以使用同样的代码来创建多个匿名函数。
1
2
3
4
5
6
7
8
9
10
11
12
def myfunc(n):
return lambda a : a * n

mydoubler = myfunc(2)
mytripler = myfunc(3)

print(mydoubler(11))
print(mytripler(11))

输出
22
33

6) 强制位置参数

Python3.8 新增了一个函数形参语法 / 用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。

在以下的例子中,形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 和 f 要求为关键字形参:

1
2
3
4
5
6
7
8
9
10
11
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)

正确:
f(10, 20, 30, d=40, e=50, f=60)

错误:
f(10, b=20, c=30, d=40, e=50, f=60)
# b 不能使用关键字参数的形式
f(10, 20, 30, 40, 50, f=60)
# e 必须使用关键字参数的形式

2-3 输入和输出

1. 输出格式美化

1) str()

  • str(): 函数返回一个用户易读的表达形式。
1
2
>>> str(1/7)
'0.14285714285714285'

2) repr()

  • repr(): 产生一个解释器易读的表达形式。

  • ```python

    s = ‘Hello, Runoob’
    repr(s)
    “‘Hello, Runoob’”

    repr() 函数可以转义字符串中的特殊字符

    … hello = ‘hello, runoob\n’
    hellos = repr(hello)
    print(hellos)
    ‘hello, runoob\n’

    repr() 的参数可以是 Python 的任何对象

    … repr((x, y, (‘Google’, ‘Runoob’)))
    “(32.5, 40000, (‘Google’, ‘Runoob’))”

    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

    ### 3)just() / zfill()

    - rjust() / ljust() / center()
    - 字符串靠右/左/居中, 并在填充空格对齐
    - zfill()
    - 在数字的左边填充 0

    ### 4) str.format()

    ```python
    >>> print('{1} 和 {0}'.format('Google', 'Runoob'))
    Runoob 和 Google

    >>> print('站点列表 {0}, {1}, 和 {other}。'.format('Google', 'Runoob', other='Taobao'))
    站点列表 Google, Runoob, 和 Taobao。

    # !a (使用 ascii()), !s (使用 str()) 和 !r (使用 repr()) 可以用于在格式化某个值之前对其进行转化
    >>> print('常量 PI 的值近似为: {}。'.format(math.pi))
    常量 PI 的值近似为: 3.141592653589793。
    >>> print('常量 PI 的值近似为: {!r}。'.format(math.pi))
    常量 PI 的值近似为: 3.141592653589793。

    # 在 : 后传入一个整数, 可以保证该域至少有这么多的宽度
    >>> table = {'Google': 1, 'Runoob': 2, 'Taobao': 3}
    >>> for name, number in table.items():
    ... print('{0:10} ==> {1:10d}'.format(name, number))
    ...
    Google ==> 1
    Runoob ==> 2
    Taobao ==> 3
  • 如果你有一个很长的格式化字符串, 而你不想将它们分开, 那么在格式化时通过变量名而非位置会是很好的事情。

    • ```python

      最简单的就是传入一个字典, 然后使用方括号 [] 来访问键值 :

      table = {‘Google’: 1, ‘Runoob’: 2, ‘Taobao’: 3}
      print(‘Runoob: {0[Runoob]:d}; Google: {0[Google]:d}; Taobao: {0[Taobao]:d}’.format(table))
      Runoob: 2; Google: 1; Taobao: 3

      也可以通过在 table 变量前使用 ** 来实现相同的功能:

      table = {‘Google’: 1, ‘Runoob’: 2, ‘Taobao’: 3}
      print(‘Runoob: {Runoob:d}; Google: {Google:d}; Taobao: {Taobao:d}’.format(**table))
      Runoob: 2; Google: 1; Taobao: 3

      1
      2
      3
      4
      5
      6
      7
      8

      ### 5) 旧式字符串格式化

      - **%** 操作符也可以实现字符串格式化。 它将左边的参数作为类似 **sprintf()** 式的格式化字符串, 而将右边的代入, 然后返回格式化后的字符串

      ```python
      >>> print('常量 PI 的值近似为:%5.3f。' % math.pi)
      常量 PI 的值近似为:3.142。

2. 读取键盘输入

  • Python 提供了 input() 内置函数从标准输入读入一行文本,默认的标准输入是键盘
1
2
3
4
5
str = input("请输入:");
print ("你输入的内容是: ", str)

请输入:菜鸟教程
你输入的内容是: 菜鸟教程

2-4 读和写文件

  • open() 将会返回一个 file 对象
    • open(filename, mode)
    • filename:包含了你要访问的文件名称的字符串值。
    • mode:决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)。

不同模式打开文件的完全列表:

模式 描述
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

img

模式 r r+ w w+ a a+
+ + + +
+ + + + +
创建 + + + +
覆盖 + +
指针在开始 + + + +
指针在结尾 + +

1) 文件对象的方法

f.read()

  • 读取一个文件的内容,调用 f.read(size), 这将读取一定数目的数据, 然后作为字符串或字节对象返回
  • size 是一个可选的数字类型的参数。
    • 当 size 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。
1
2
3
4
5
6
7
8
# 打开一个文件
f = open("/tmp/foo.txt", "r")

str = f.read()
print(str)

# 关闭打开的文件
f.close()

f.readline

  • f.readline() 会从文件中读取单独的一行。换行符为 ‘\n’。
  • f.readline() 如果返回一个空字符串, 说明已经已经读取到最后一行。
1
2
3
4
5
6
7
8
# 打开一个文件
f = open("/tmp/foo.txt", "r")

str = f.readline()
print(str)

# 关闭打开的文件
f.close()

f.readlines

  • f.readlines() 将返回该文件中包含的所有行。
  • 如果设置可选参数 sizehint, 则读取指定长度的字节, 并且将这些字节按行分割。
1
2
3
4
5
6
7
8
# 打开一个文件
f = open("/tmp/foo.txt", "r")

str = f.readlines()
print(str)

# 关闭打开的文件
f.close()
  • 另一种方式迭代文件对象

    • 这个方法很简单, 但是并没有提供一个很好的控制。 因为两者的处理机制不同, 最好不要混用。
1
2
3
4
5
6
7
8
# 打开一个文件
f = open("/tmp/foo.txt", "r")

for line in f:
print(line, end='')

# 关闭打开的文件
f.close()

f.write()

  • f.write(string) 将 string 写入到文件中, 然后返回写入的字符数。
1
2
3
4
5
6
7
# 打开一个文件
f = open("/tmp/foo.txt", "w")

num = f.write( "Python 是一个非常好的语言。\n是的,的确非常好!!\n" )
print(num)
# 关闭打开的文件
f.close()
  • 如果要写入一些不是字符串的东西,那么需要先进行转换。

f.tell()

  • f.tell() 返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。

f.seek()

  • 如果要改变文件当前的位置, 可以使用 f.seek(offset, from_what) 函数
    • from_what 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾
      • seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
      • seek(x,1) : 表示从当前位置往后移动x个字符
      • seek(-x,2):表示从文件的结尾往前移动x个字符
1
2
3
4
5
6
7
8
9
10
11
>>> f = open('/tmp/foo.txt', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5) # 移动到文件的第六个字节
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # 移动到文件的倒数第三字节
13
>>> f.read(1)
b'd'

f.close()

  • 在文本文件中 (那些打开文件的模式下没有 b 的), 只会相对于文件起始位置进行定位。
  • 当你处理完一个文件后, 调用 f.close() 来关闭文件并释放系统的资源,如果尝试再调用该文件,则会抛出异常。
1
2
3
4
5
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file
  • 当处理一个文件对象时, 使用 with 关键字是非常好的方式。在结束后, 它会帮你正确的关闭文件。 而且写起来也比 try - finally 语句块要简短
1
2
3
4
>>> with open('/tmp/foo.txt', 'r') as f:
... read_data = f.read()
>>> f.closed
True

2) pickle 模块

  • python的pickle模块实现了基本的数据序列和反序列化。
    • 能够实现任意对象与文本之间的相互转化,也可以实现任意对象与二进制之间的相互转化
  • 提供函数:
    1. dumps():将 Python 中的对象序列化成二进制对象,并返回;
    2. loads():读取给定的二进制对象数据,并将其转换为 Python 对象;
    3. dump():将 Python 中的对象序列化成二进制对象,并写入文件;
    4. load():读取指定的序列化数据文件,并返回对象。
  • dumps 和 loads 实现基于内存的 Python 对象与二进制互转;
  • dump 和 load 实现基于文件的 Python 对象与二进制互转。

pickle.dumps()

  • 此函数用于将 Python 对象转为二进制对象,其语法格式如下:
  • dumps(obj, protocol=None, *, fix_imports=True)

  • 此格式中各个参数的含义为:

    • obj:要转换的 Python 对象;
    • protocol:pickle 的转码协议,取值为 0、1、2、3、4,其中 0、1、2 对应 Python 早期的版本,3 和 4 则对应 Python 3.x 版本及之后的版本。未指定情况下,默认为 3。
    • 其它参数:为了兼容 Python 2.x 版本而保留的参数,Python 3.x 中可以忽略。
1
2
3
4
5
6
7
8
import pickle
tup1 = ('I love Python', {1,2,3}, None)
#使用 dumps() 函数将 tup1 转成 p1
p1 = pickle.dumps(tup1)
print(p1)

输出
b'\x80\x03X\r\x00\x00\x00I love Pythonq\x00cbuiltins\nset\nq\x01]q\x02(K\x01K\x02K\x03e\x85q\x03Rq\x04N\x87q\x05.'

pickle.loads()

  • 此函数用于将二进制对象转换成 Python 对象,其基本格式如下:

  • loads(data, *, fix_imports=True, encoding='ASCII', errors='strict')

1
2
3
4
5
6
7
8
9
import pickle
tup1 = ('I love Python', {1,2,3}, None)
p1 = pickle.dumps(tup1)
#使用 loads() 函数将 p1 转成 Python 对象
t2 = pickle.loads(p1)
print(t2)

输出
('I love Python', {1, 2, 3}, None)
  • 在使用 loads() 函数将二进制对象反序列化成 Python 对象时,会自动识别转码协议,所以不需要将转码协议当作参数传入

pickle.dump()

  • 此函数用于将 Python 对象转换成二进制文件,其基本语法格式为:

  • dump (obj, file,protocol=None, *, fix mports=True)

其中各个参数的具体含义如下:

  • obj:要转换的 Python 对象。
    • file:转换到指定的二进制文件中,要求该文件必须是以”wb”的打开方式进行操作。
    • protocol:和 dumps() 函数中 protocol 参数的含义完全相同,因此这里不再重复描述。
    • 其他参数:为了兼容以前 Python 2.x版本而保留的参数,可以忽略。
1
2
3
4
5
import pickle
tup1 = ('I love Python', {1,2,3}, None)
#使用 dumps() 函数将 tup1 转成 p1
with open ("a.txt", 'wb') as f: #打开文件
pickle.dump(tup1, f) #用 dump 函数将 Python 对象转成二进制对象文件
pickle.load()
  • 此函数和 dump() 函数相对应,用于将二进制对象文件转换成 Python 对象。该函数的基本语法格式为:
  • load(file, *, fix_imports=True, encoding='ASCII', errors='strict')

  • 其中,file 参数表示要转换的二进制对象文件(必须以 “rb” 的打开方式操作文件),其它参数只是为了兼容 Python 2.x 版本而保留的参数,可以忽略。

1
2
3
4
5
6
7
8
9
10
11
import pickle
tup1 = ('I love Python', {1,2,3}, None)
#使用 dumps() 函数将 tup1 转成 p1
with open ("a.txt", 'wb') as f: #打开文件
pickle.dump(tup1, f) #用 dump 函数将 Python 对象转成二进制对象文件
with open ("a.txt", 'rb') as f: #打开文件
t3 = pickle.load(f) #将二进制文件对象转换成 Python 对象
print(t3)

输出
('I love Python', {1, 2, 3}, None)

2-5 File(文件)方法

1) open()方法

  • 用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError

    • 使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法
  • open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。

    1
    open(file, mode='r')
    • 完整的语法格式为:
    1
    open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
    • 参数说明:

      • file: 必需,文件路径(相对或者绝对路径)。

      • mode: 可选,文件打开模式

      • buffering: 设置缓冲

      • encoding: 一般使用utf8

      • errors: 报错级别

      • newline: 区分换行符

      • closefd: 传入的file参数类型

      • opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。

  • mode 参数

模式 描述
t 文本模式 (默认)。
x 写模式,新建一个文件,如果该文件已存在则会报错。
b 二进制模式。
+ 打开一个文件进行更新(可读可写)。
U 通用换行模式(Python 3 不支持)。
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
  • 默认为文本模式,如果要以二进制模式打开,加上b

2) file 对象

file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数:

序号 方法及描述
1 file.close()关闭文件。关闭后文件不能再进行读写操作。
2 file.flush()刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
3 file.fileno()返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。
4 file.isatty()如果文件连接到一个终端设备返回 True,否则返回 False。
5 file.next()Python 3 中的 File 对象不支持 next() 方法。返回文件下一行。
6 file.read([size])从文件读取指定的字节数,如果未给定或为负则读取所有。
7 file.readline([size])读取整行,包括 “\n” 字符。
8 file.readlines([sizeint])读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
9 file.seek(offset[, whence])移动文件读取指针到指定位置
10 file.tell()返回文件当前位置。
11 file.truncate([size])从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 windows 系统下的换行代表2个字符大小。
12 file.write(str)将字符串写入文件,返回的是写入的字符长度。
13 file.writelines(sequence)向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。

2-6 OS文件/ 目录方法

菜鸟教程:OS文件/ 目录方法

2-7 错误和异常

1. 异常处理

1) try/except

img

1
2
3
4
5
6
while True:
try:
x = int(input("请输入一个数字: "))
break
except ValueError:
print("您输入的不是数字,请再次尝试输入!")
  • try 语句按照如下方式工作;

    • 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)。
    • 如果没有异常发生,忽略 except 子句,try 子句执行后结束。
    • 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。
    • 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。
  • 一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组

    • ```python
      except (RuntimeError, TypeError, NameError):
      pass
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17

      - 最后一个except子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。

      - ```python
      import sys

      try:
      f = open('myfile.txt')
      s = f.readline()
      i = int(s.strip())
      except OSError as err:
      print("OS error: {0}".format(err))
      except ValueError:
      print("Could not convert data to an integer.")
      except:
      print("Unexpected error:", sys.exc_info()[0])
      raise

2) try/except…else

img

  • 使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。

    • ```python

      def this_fails():

          x = 1/0
      

      try:

          this_fails()
      except ZeroDivisionError as err:
          print('Handling run-time error:', err)
      

      Handling run-time error: int division or modulo by zero

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20

      ### 3) try/finally

      ![img](https://s2.loli.net/2022/04/17/Pe8ak6xZ1pMfgYK.png)

      - finally 语句无论异常是否发生都会执行

      - ```python
      try:
      runoob()
      except AssertionError as error:
      print(error)
      else:
      try:
      with open('file.log') as file:
      read_data = file.read()
      except FileNotFoundError as fnf_error:
      print(fnf_error)
      finally:
      print('这句话,无论异常是否发生都会执行。')

2. 抛出异常

  • 使用 raise 语句抛出一个指定的异常。

  • raise语法格式如下:

    • raise [Exception [, args [, traceback]]]
    • img
  • ```python
    x = 10
    if x > 5:

    raise Exception('x 不能大于 5。x 的值为: {}'.format(x))
    

    输出
    Traceback (most recent call last):
    File “test.py”, line 3, in

    raise Exception('x 不能大于 5。x 的值为: {}'.format(x))
    

    Exception: x 不能大于 5。x 的值为: 10

    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

    # 2-8 面向对象

    ## 1. 面向对象技术简介

    - 概念
    - **类(Class):** 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
    - **方法:**类中定义的函数。
    - **类变量:**类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
    - **数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。
    - **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
    - **局部变量:**定义在方法中的变量,只作用于当前实例的类。
    - **实例变量:**在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
    - **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
    - **实例化:**创建一个类的实例,类的具体对象。
    - **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
    - 和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制。
    - Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。
    - 对象可以包含任意数量和类型的数据。

    ## 2. 面向对象操作

    ### 1) 类定义/对象

    ```python
    class Car():
    def __init__(self, make, modle, year):
    # self代表类的实例,而非类
    self.make = make
    self.modle = modle
    self.year = year
    self.mileage = 0.0

    def describe(self):
    print("{}年,{}品牌的{}产生了!".format(self.year, self.modle, self.make))

    def get_mileage(self):
    print("这个车已经跑了{:5}公里!".format(self.mileage))

    def set_mileage(self, mileage):
    # 对属性修改进行限制
    if self.mileage < mileage:
    self.mileage = mileage

2) 类继承

1
2
3
4
5
6
7
8
9
10
# ...
class Ben(Car):
def __init__(self, make, modle, year, found):
super().__init__(make, modle, year)
# or
# Car.__init__(self, make, modle, year)
self.found = found

def what(self):
print("I am {}, found by {} in {}".format(self.make, self.found, self.year))

3) 多继承

1
2
3
4
5
6
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
  • 需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

4) 类属性与方法

  • 类的私有属性
    • __private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs
  • 类的私有方法
    • __private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods

2-9 命名空间和作用域

1. 命名空间(namespace)

  • 命名空间(namespace)是从名称到对象的映射,大部分的命名空间都是通过Python字典来实现的
  • 命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。
  • 三种命名空间:
    • 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
    • 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
    • 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)
    • img

2. 作用域

  • 作用域就是一个 Python 程序可以直接访问命名空间的正文区域。
  • Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
  • 四种作用域:
    • L(Local):最内层,包含局部变量,比如一个函数/方法内部。
    • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
    • G(Global):当前脚本的最外层,比如当前模块的全局变量。
    • B(Built-in): 包含了内建的变量/关键字等,最后被搜索。
    • 规则顺序: L –> E –> G –> B
    • img

3. global和nonlocal关键字

  • 修改全局变量(global):

    • e.g.

    • ```python
      num = 1
      def fun1():

      global num  # 需要使用 global 关键字声明
      print(num) 
      num = 123
      print(num)
      

      fun1()
      print(num)

      输出
      1
      123
      123

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19

      - 修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量(nonlocal):

      - e.g.

      - ```python
      def outer():
      num = 10
      def inner():
      nonlocal num # nonlocal关键字声明
      num = 100
      print(num)
      inner()
      print(num)
      outer()

      输出
      100
      100