Python进阶

2021/10/20 Python 共 17093 字,约 49 分钟

列表推导式

循环可以用来生成列表:

a = [x for x in range(4)]
a
[0, 1, 2, 3]

在循环的过程中使用 if:

a = [x for x in range(3,10) if x % 2 == 0]
a
[4, 6, 8]

2 个for 循环:

a = [(x,y) for x in range(1,3) for y in range(1,3)]
a
[(1, 1), (1, 2), (2, 1), (2, 2)]

3 个 for 循环:

a = [(x,y,z) for x in range(1,3) for y in range(1,3) for z in range(1,3)]
a
[(1, 1, 1),
 (1, 1, 2),
 (1, 2, 1),
 (1, 2, 2),
 (2, 1, 1),
 (2, 1, 2),
 (2, 2, 1),
 (2, 2, 2)]

也可以使用推导式生成集合和字典:
字典推导式

values = [10, 21, 4, 7, 12]
square_dict = {x: x**2 for x in values if x <= 10}
print(square_dict)
{10: 100, 4: 16, 7: 49}

集合推导式

values = [10, 21, 4, 7, 12]
square_set = {x**2 for x in values if x <= 10}
print(square_set)
{16, 49, 100}

编程练习

生成 1-200 之间所有能被3整除不能被5整除的数,请编写代码实现。

# 请编写你的答案
values = [i for i in range(1,201) if i%3==0 and i%5!=0]
print(values)
[3, 6, 9, 12, 18, 21, 24, 27, 33, 36, 39, 42, 48, 51, 54, 57, 63, 66, 69, 72, 78, 81, 84, 87, 93, 96, 99, 102, 108, 111, 114, 117, 123, 126, 129, 132, 138, 141, 144, 147, 153, 156, 159, 162, 168, 171, 174, 177, 183, 186, 189, 192, 198]

编程练习

请写出一段 Python 代码实现分组一个 list 里面的元素, 比如 [1,2,3,…100] 变成 [[1,2,3],[4,5,6]….],请编写代码实现。

# 请编写你的答案
values = [(i,i+1,i+2) for i in range(1,101,3)]
print(values)

[(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, 66), (67, 68, 69), (70, 71, 72), (73, 74, 75), (76, 77, 78), (79, 80, 81), (82, 83, 84), (85, 86, 87), (88, 89, 90), (91, 92, 93), (94, 95, 96), (97, 98, 99), (100, 101, 102)]

函数

定义函数

函数function,通常接收输入参数,并有返回值。

它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

def add(x, y):
    """Add two numbers"""
    a = x + y
    return a

函数通常有一下几个特征:

  • 使用 def 关键词来定义一个函数。
  • def 后面是函数的名称,括号中是函数的参数,不同的参数用 , 隔开, def foo(): 的形式是必须要有的,参数可以为空;
  • 使用缩进来划分函数的内容;
  • docstring""" 包含的字符串,用来解释函数的用途,可省略;
  • return 返回特定的值,如果省略,返回 None

使用函数

使用函数时,只需要将参数换成特定的值传给函数。

Python 并没有限定参数的类型,因此可以使用不同的参数类型:

print(add(2, 3))
print(add('foo', 'bar'))
5
foobar

在这个例子中,如果传入的两个参数不可以相加,那么 Python 会将报错:

print(add(2, "foo"))

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

/tmp/ipykernel_114/517506252.py in <module>
----> 1 print(add(2, "foo"))


/tmp/ipykernel_114/3800890303.py in add(x, y)
      1 def add(x, y):
      2     """Add two numbers"""
----> 3     a = x + y
      4     return a


TypeError: unsupported operand type(s) for +: 'int' and 'str'

如果传入的参数数目与实际不符合,也会报错:

print(add(1, 2, 3))

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

/tmp/ipykernel_114/3888245736.py in <module>
----> 1 print(add(1, 2, 3))


TypeError: add() takes 2 positional arguments but 3 were given

传入参数时,Python 提供了两种选项,第一种是上面使用的按照位置传入参数,另一种则是使用关键词模式,显式地指定参数的值:

print(add(x=2, y=3))
print(add(y="foo", x="bar"))

5
barfoo

可以混合这两种模式:

print(add(2, y=3))
5

设定参数默认值

可以在函数定义的时候给参数设定默认值,例如:

def quad(x, a=1, b=0, c=0):
    return a*x**2 + b*x + c

可以省略有默认值的参数:

print(quad(2.0))
4.0

可以修改参数的默认值:

print(quad(2.0, b=3))
10.0
print(quad(2.0, 2, c=4))
12.0

这里混合了位置和指定两种参数传入方式,第二个 2 是传给 a 的。

注意,在使用混合语法时,要注意不能给同一个值赋值多次,否则会报错,例如:

print(quad(2.0, 2, a=2))
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

/tmp/ipykernel_114/2505569231.py in <module>
----> 1 print(quad(2.0, 2, a=2))


TypeError: quad() got multiple values for argument 'a'

接收不定长参数

使用如下方法,可以使函数接受不定数目的参数:

def add(x, *args):
    total = x
    for arg in args:
        total += arg
    return total

这里,*args 表示参数数目不定,可以看成一个元组,把第一个参数后面的参数当作元组中的元素。

print(add(1, 2, 3, 4))
print(add(1, 2))

10
3

这样定义的函数不能使用关键词传入参数,要使用关键词,可以这样:

def add(x, **kwargs):
    total = x
    for arg, value in kwargs.items():
        print("adding %s=%s"%(arg,value))
        total += value
    return total

这里, **kwargs 表示参数数目不定,相当于一个字典,键和值对应于键值对。

print(add(10, y=11, z=12, w=13))
adding y=11
adding z=12
adding w=13
46

再看这个例子,可以接收任意数目的位置参数和键值对参数:

def foo(*args, **kwargs):
    print(args, kwargs)

foo(2, 3, x='bar', z=10)

(2, 3) {'x': 'bar', 'z': 10}

不过要按顺序传入参数,先传入位置参数 args ,再传入关键词参数 kwargs

返回多个值

函数可以返回多个值:

def divid(a, b):
    """
    除法
    :param a: number 被除数
    :param b: number 除数
    :return: 商和余数
    """
    quotient = a // b
    remainder = a % b
    return quotient, remainder

quotient, remainder = divid(7,4)
print(quotient, remainder)
1 3

事实上,Python 将返回的两个值变成了元组:

print(divid(7,4))
(1, 3)

因为这个元组中有两个值,所以可以使用

quotient, remainder = divid(7,4)

给两个值赋值。

列表也有相似的功能:

a, b, c = [1, 2, 3]
print(a, b, c)
1 2 3

事实上,不仅仅返回值可以用元组表示,也可以将参数用元组以这种方式传入:

def divid(a, b):
    """
    除法
    :param a: number 被除数
    :param b: number 除数
    :return: 商和余数
    """
    quotient = a // b
    remainder = a % b
    return quotient, remainder

z = (7,4)
print(divid(*z))
(1, 3)

这里的*必不可少。

事实上,还可以通过字典传入参数来执行函数:

def divid(a, b):
    """
    除法
    :param a: number 被除数
    :param b: number 除数
    :return: 商和余数
    """
    quotient = a // b
    remainder = a % b
    return quotient, remainder

z = {'a':7,'b':4}
print(divid(**z))
(1, 3)

map 方法生成序列

其用法为:

map(aFun, aSeq)

将函数 aFun 应用到序列 aSeq 上的每一个元素上,返回一个列表,不管这个序列原来是什么类型。

事实上,根据函数参数的多少,map 可以接受多组序列,将其对应的元素作为参数传入函数:

def divid(a, b):
    """
    除法
    :param a: number 被除数
    :param b: number 除数
    :return: 商和余数
    """
    quotient = a // b
    remainder = a % b
    return quotient, remainder

a = (10, 6, 7)
b = [2, 5, 3]
print(list(map(divid,a,b)))
[(5, 0), (1, 1), (2, 1)]

编程练习

编写函数计算形式如 a + aa + aaa + … + aaa…aaa的表达式的值,其中 a 为小于 10 的自然数,请编写代码实现。

# 请编写你的答案
def genNum(x, n):
    ans = 0
    for i in range(n):
        ans = ans * 10 + x
    return ans

def addSum(x, n):
    listX = [x] * n
    nums = [i for i in range(1,n+1)]
    ans = list(map(genNum,listX,nums))
    print(ans)
    return sum(ans)

addSum(2, 5)

[2, 22, 222, 2222, 22222]





24690

模块和包

模块

Python 会将所有 .py 结尾的文件认定为 Python 代码文件,考虑下面的脚本 ex1.py

%%writefile ex1.py

PI = 3.1416

def sum(lst):
    """
    计算 lst 序列所有元素的和
    :param lst: 序列 e.g. [1,2,3]
    :return: lst 序列所有元素的总和
    """
    
    # 获取 lst序列第一个元素
    tot = lst[0]
    
    # 循环遍历 lst 序列剩余元素
    for value in lst[1:]:
        tot = tot + value
    return tot

w = [0, 1, 2, 3]
print(sum(w), PI)


Writing ex1.py

可以执行它:

%run ex1.py
6 3.1416

这个脚本可以当作一个模块,可以使用import关键词加载并执行它(这里要求ex1.py在当前工作目录):

import ex1

ex1
6 3.1416





<module 'ex1' from '/home/jovyan/work/1_math_and_programming_basic/ex1.py'>

在导入时,Python 会执行一遍模块中的所有内容。

ex1.py 中所有的变量都被载入了当前环境中,不过要使用

ex1.变量名

的方法来查看或者修改这些变量:

print(ex1.PI)
3.1416
ex1.PI = 3.141592653
print(ex1.PI)
3.141592653

还可以用

ex1.函数名

调用模块里面的函数:

print(ex1.sum([2, 3, 4]))
9

为了提高效率,Python 只会载入模块一次,已经载入的模块再次载入时,Python 并不会真正执行载入操作,哪怕模块的内容已经改变。

例如,这里重新导入 ex1 时,并不会执行 ex1.py 中的 print 语句:

import ex1

需要重新导入模块时,可以使用 reload 强制重新载入它,例如:

from imp import reload
reload(ex1)
6 3.1416





<module 'ex1' from '/home/jovyan/work/1_math_and_programming_basic/ex1.py'>

删除之前生成的文件:

import os
os.remove('ex1.py')

__name__ 属性

有时候我们想将一个 .py 文件既当作脚本,又能当作模块用,这个时候可以使用 __name__ 这个属性。

只有当文件被当作脚本执行的时候, __name__的值才会是 '__main__',所以我们可以:

%%writefile ex2.py

PI = 3.1416

def sum(lst):
    """ Sum the values in a list
    """
    tot = 0
    for value in lst:
        tot = tot + value
    return tot

def add(x, y):
    " Add two values."
    a = x + y
    return a

def test():
    w = [0,1,2,3]
    assert(sum(w) == 6)
    print('test passed.')

if __name__ == '__main__':
    test()

Writing ex2.py

运行文件:

%run ex2.py
test passed.

当作模块导入, test() 不会执行:

import ex2

但是可以使用其中的变量:

ex2.PI

3.1416

引入模块时可以为它设置一个别名让使用更方便:

import ex2 as e2
e2.PI
3.1416

其它导入方法

可以从模块中导入变量:

from ex2 import add, PI

使用 from 后,可以直接使用 addPI

add(2, 3)
5

或者使用 * 导入所有变量:

from ex2 import *
add(3, 4.5)
7.5

这种导入方法不是很提倡,因为如果你不确定导入的都有哪些,可能覆盖一些已有的函数。

删除文件:

import os
os.remove('ex2.py')

假设我们有这样的一个文件夹:

foo/

  • __init__.py
  • bar.py (defines func)
  • baz.py (defines zap)

这意味着 foo 是一个包,我们可以这样导入其中的内容:


from foo.bar import func
from foo.baz import zap

barbaz 都是 foo 文件夹下的 .py 文件。

导入包要求:

  • 文件夹 fooPython 的搜索路径中
  • __init__.py 表示 foo 是一个包,它可以是个空文件。

异常

写代码的时候,出现错误不可避免,即使代码语法没有问题,也可能遇到其它问题。

看下面这段代码:


import math

while True:
    text = input('> ')
    if text[0] == 'q':
        break
    x = float(text)
    y = math.log10(x)
    print("log10({0}) = {1}".format(x, y))

这段代码接收命令行的输入,当输入为数字时,计算它的对数并输出,直到输入值为 q 为止。

乍看没什么问题,然而当我们输入 0 或者负数时:

import math

while True:
    text = input('> ')
    if text[0] == 'q':
        break
    x = float(text)
    y = math.log10(x)
    print("log10({0}) = {1}".format(x, y))

>  0



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

/tmp/ipykernel_114/1491084093.py in <module>
      6         break
      7     x = float(text)
----> 8     y = math.log10(x)
      9     print("log10({0}) = {1}".format(x, y))


ValueError: math domain error

log10 函数会报错,因为不能接受非正值。

一旦报错,程序就会停止执行。如果不希望程序停止执行,并且想要捕捉异常,那么我们可以按照 try/except 语句。


import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = math.log10(x)
        print("log10({0}) = {1}".format(x, y))
    except ValueError:
        print("the value must be greater than 0")

一旦 try 块中的内容出现了异常,那么 try 块后面的内容会被忽略,Python 会寻找 except 里面有没有对应的内容,如果找到,就执行对应的块,没有则抛出这个异常。

在上面的例子中,try 抛出的是 ValueErrorexcept 中有对应的内容,所以这个异常被 except 捕捉到。

程序可以继续执行:

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = math.log10(x)
        print("log10({0}) = {1}".format(x, y))
    except ValueError:
        print("the value must be greater than 0")

>  0


the value must be greater than 0


>  9


log10(9.0) = 0.9542425094393249


>  -1


the value must be greater than 0

捕捉不同的错误类型

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print("log10({0}) = {1}".format(x, y))
    except ValueError:
        print("the value must be greater than 0")

假设我们将这里的 y 更改为 1 / math.log10(x),此时输入 1

程序执行:

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print("log10({0}) = {1}".format(x, y))
    except ValueError:
        print("the value must be greater than 0")

>  0


the value must be greater than 0


>  1



---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

/tmp/ipykernel_114/230723649.py in <module>
      7             break
      8         x = float(text)
----> 9         y = 1 / math.log10(x)
     10         print("log10({0}) = {1}".format(x, y))
     11     except ValueError:


ZeroDivisionError: float division by zero

因为我们的 except 里面并没有 ZeroDivisionError,所以会抛出这个异常,我们可以通过两种方式解决这个问题。

捕捉所有异常

except 的值改成 Exception 类,来捕获所有的异常。

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print("1 / log10({0}) = {1}".format(x, y))
    except Exception:
        print("invalid value")

>  1


invalid value


>  4


1 / log10(4.0) = 1.660964047443681

指定特定异常

这里,我们把 ZeroDivisionError 加入 except

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print("1 / log10({0}) = {1}".format(x, y))
    except (ValueError, ZeroDivisionError):
        print("invalid value")

>  0


invalid value


>  1


invalid value


>  q

或者另加处理:

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print("1 / log10({0}) = {1}".format(x, y))
    except ValueError:
        print("the value must be greater than 0")
    except ZeroDivisionError:
        print("the value must not be 1")

>  1


the value must not be 1


>  q

事实上,我们还可以将这两种方式结合起来,用 Exception 来捕捉其他的错误:

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print("1 / log10({0}) = {1}".format(x, y))
    except ValueError:
        print("the value must be greater than 0")
    except ZeroDivisionError:
        print("the value must not be 1")
    except Exception:
        print("unexpected error")

>  1


the value must not be 1


>  0


the value must be greater than 0


>  q

得到异常的具体信息

在上面的例子中,当我们输入不能转换为浮点数的字符串时,它输出的是 the value must be greater than 0,这并没有反映出实际情况。

为了得到异常的具体信息,我们将这个 ValueError 具体化:

import math

while True:
    try:
        text = input('> ')
        if text[0] == 'q':
            break
        x = float(text)
        y = 1 / math.log10(x)
        print("1 / log10({0}) = {1}".format(x, y))
    except ValueError as exc:
        if exc.args[0] == "math domain error":
            print("the value must be greater than 0")
        else:
            print("could not convert '%s' to float" % text)
    except ZeroDivisionError:
        print("the value must not be 1")
    except Exception as exc:
        print("unexpected error:", exc)

>  a


could not convert 'a' to float


>  q

同时,我们也将捕获的其他异常的信息显示出来。

这里,exc 显示的内容是异常对应的说明,例如

ValueError: could not convert string to float: a

对应的报错信息是

could not convert string to float: a

当我们使用 except Exception 时,会捕获所有的 Exception 和它派生出来的子类,但不是所有的异常都是从 Exception 类派生出来的,可能会出现一些不能捕获的情况,因此,更加一般的做法是使用这样的形式:

try:
    pass
except:
    pass

这样不指定异常的类型会捕获所有的异常,但是这样的形式并不推荐。

else

try/except 块有一个可选的关键词 else

如果使用这个子句,那么必须放在所有的 except 子句之后。else 子句将在 try 子句没有发生任何异常的时候执行。

try:
    print(1)
except:
    pass
else:
    print('else was called.')
1
else was called.

出现异常,else 不会执行。

try:
    print(1/0)
except ZeroDivisionError:
    print('divide by 0.')
else:
    print('else was called.')
divide by 0.

finally

try/except 块还有一个可选的关键词 finally

不管 try 块有没有异常, finally 块的内容总是会被执行,而且会在抛出异常前执行,因此可以用来作为安全保证,比如确保打开的文件被关闭。

try:
    print(1)
finally:
    print('finally was called.')
1
finally was called.

在抛出异常前执行:

try:
    print(1 / 0)
finally:
    print('finally was called.')
finally was called.



---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

/tmp/ipykernel_63/3900254831.py in <module>
      1 try:
----> 2     print(1 / 0)
      3 finally:
      4     print('finally was called.')


ZeroDivisionError: division by zero

如果异常被捕获了,在最后执行:

try:
    print(1 / 0)
except ZeroDivisionError:
    print('divide by 0.')
finally:
    print('finally was called.')

divide by 0.
finally was called.

异常的处理流程可参考下图:

## 警告 出现了一些需要让用户知道的问题,但又不想停止程序,这时候我们可以使用警告: 首先导入警告模块: ```python import warnings ``` 在需要的地方,我们使用 `warnings` 中的 `warn` 函数: warn(msg, WarningType = UserWarning) ```python def month_warning(m): if not 1<= m <= 12: msg = "month (%d) is not between 1 and 12" % m warnings.warn(msg, RuntimeWarning) month_warning(13) ``` /home/jovyan/.virtualenvs/basenv/lib/python3.7/site-packages/ipykernel_launcher.py:4: RuntimeWarning: month (13) is not between 1 and 12 after removing the cwd from sys.path. 有时候我们想要忽略特定类型的警告,可以使用 `warnings` 的 `filterwarnings` 函数: filterwarnings(action, category) 将 `action` 设置为 `'ignore'` 便可以忽略特定类型的警告: ```python warnings.filterwarnings(action = 'ignore', category = RuntimeWarning) month_warning(13) ``` ## 文件读写 写入测试文件: ```python %%writefile test.txt this is a test file. hello world! python is good! today is a good day. ``` Writing test.txt ### 读文件 使用 `open` 函数来读文件,使用文件名的字符串作为输入参数: ```python f = open('test.txt') ``` 默认以读的方式打开文件,如果文件不存在会报错: ```python f = open('test1.txt') ``` --------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) /tmp/ipykernel_63/2359347378.py in ----> 1 f = open('test1.txt') FileNotFoundError: [Errno 2] No such file or directory: 'test1.txt' 可以使用 `read` 方法来读入文件中的所有内容: ```python text = f.read() print(text) ``` this is a test file. hello world! python is good! today is a good day. 也可以按照行读入内容,`readlines` 方法返回一个列表,每个元素代表文件中每一行的内容: ```python f = open('test.txt') lines = f.readlines() print(lines) ``` ['this is a test file.\n', 'hello world!\n', 'python is good!\n', 'today is a good day.\n'] 使用完文件之后,需要将文件关闭。 ```python f.close() ``` 事实上,我们可以将 `f` 放在一个循环中,得到它每一行的内容: ```python f = open('test.txt') for line in f: print(line) f.close() ``` this is a test file. hello world! python is good! today is a good day. 删除刚才创建的文件: ```python import os os.remove('test.txt') ``` ### 写文件 我们使用 `open` 函数的写入模式来写文件: ```python f = open('myfile.txt', 'w') f.write('hello world!') f.close() ``` 使用 `w` 模式时,如果文件不存在会被创建,我们可以查看是否真的写入成功: ```python print(open('myfile.txt').read()) ``` hello world! 如果文件已经存在, `w` 模式会覆盖之前写的所有内容: ```python f = open('myfile.txt', 'w') f.write('another hello world!') f.close() print(open('myfile.txt').read()) ``` another hello world! 除了写入模式,还有追加模式 `a` ,追加模式不会覆盖之前已经写入的内容,而是在之后继续写入: ```python f = open('myfile.txt', 'a') f.write('... and more') f.close() print(open('myfile.txt').read()) ``` another hello world!... and more 写入结束之后一定要将文件关闭,否则可能出现内容没有完全写入文件中的情况。 还可以使用读写模式 `w+`: ```python f = open('myfile.txt', 'w+') f.write('hello world!') f.seek(6) print(f.read()) f.close() ``` world! 这里 `f.seek(6)` 移动到文件的第6个字符处,然后 `f.read()` 读出剩下的内容。 删除刚才创建的文件: ```python import os os.remove('myfile.txt') ``` ### 关闭文件 在 **Python** 中,如果一个打开的文件不再被其他变量引用时,它会自动关闭这个文件。 所以正常情况下,如果一个文件正常被关闭了,忘记调用文件的 `close` 方法不会有什么问题。 关闭文件可以保证内容已经被写入文件,而不关闭可能会出现意想不到的结果: ```python f = open('newfile.txt','w') f.write('hello world') g = open('newfile.txt', 'r') print(repr(g.read())) ``` '' 虽然这里写了内容,但是在关闭之前,这个内容并没有被写入磁盘。 使用循环写入的内容也并不完整: ```python f = open('newfile.txt','w') for i in range(30): f.write('hello world: ' + str(i) + '\n') g = open('newfile.txt', 'r') print(g.read()) f.close() g.close() ``` hello world ```python import os os.remove('newfile.txt') ``` 出现异常时候的读写: ```python f = open('newfile.txt','w') for i in range(30): x = 1.0 / (i - 10) f.write('hello world: ' + str(i) + '\n') ``` --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) /tmp/ipykernel_63/537494127.py in 1 f = open('newfile.txt','w') 2 for i in range(30): ----> 3 x = 1.0 / (i - 10) 4 f.write('hello world: ' + str(i) + '\n') ZeroDivisionError: float division by zero 查看已有内容: ```python g = open('newfile.txt', 'r') print(g.read()) f.close() g.close() ``` 可以看到,出现异常的时候,磁盘的写入并没有完成,为此我们可以使用 `try/except/finally` 块来关闭文件,这里 `finally` 确保关闭文件,所有的写入已经完成。 ```python f = open('newfile.txt','w') try: for i in range(30): x = 1.0 / (i - 10) f.write('hello world: ' + str(i) + '\n') except Exception: print("something bad happened") finally: f.close() ``` something bad happened ```python g = open('newfile.txt', 'r') print(g.read()) g.close() ``` hello world: 0 hello world: 1 hello world: 2 hello world: 3 hello world: 4 hello world: 5 hello world: 6 hello world: 7 hello world: 8 hello world: 9 ### with 方法 事实上,**Python** 提供了更安全的方法,当 `with` 块的内容结束后,**Python** 会自动调用它的`close` 方法,确保读写的安全: ```python with open('newfile.txt','w') as f: for i in range(30): x = 1.0 / (i - 10) f.write('hello world: ' + str(i) + '\n') ``` --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) /tmp/ipykernel_63/3852787149.py in 1 with open('newfile.txt','w') as f: 2 for i in range(30): ----> 3 x = 1.0 / (i - 10) 4 f.write('hello world: ' + str(i) + '\n') ZeroDivisionError: float division by zero 与 `try/exception/finally` 效果相同,但更简单。 ```python g = open('newfile.txt', 'r') print(g.read()) g.close() ``` hello world: 0 hello world: 1 hello world: 2 hello world: 3 hello world: 4 hello world: 5 hello world: 6 hello world: 7 hello world: 8 hello world: 9 所以,写文件时候要确保文件被正确关闭。 删除刚才创建的文件: ```python import os os.remove('newfile.txt') ``` ## CSV 文件和 csv 模块 标准库中有自带的 `csv` 模块处理 `csv` 格式的文件: ```python import csv ``` ### 读 csv 文件 假设我们有这样的一个文件: ```python %%file data.csv "alpha 1", 100, -1.443 "beat 3", 12, -0.0934 "gamma 3a", 192, -0.6621 "delta 2a", 15, -4.515 ``` Writing data.csv 打开这个文件,并产生一个文件 reader: ```python # 打开 data.csv 文件 fp = open("data.csv") # 读取文件“” r = csv.reader(fp) # 可以按行迭代数据 for row in r: print(row) # 关闭文件 fp.close() ``` ['alpha 1', ' 100', ' -1.443'] ['beat 3', ' 12', ' -0.0934'] ['gamma 3a', ' 192', ' -0.6621'] ['delta 2a', ' 15', ' -4.515'] 默认数据内容都被当作字符串处理,不过可以自己进行处理: ```python data = [] with open('data.csv') as fp: r = csv.reader(fp) for row in r: data.append([row[0], int(row[1]), float(row[2])]) data ``` [['alpha 1', 100, -1.443], ['beat 3', 12, -0.0934], ['gamma 3a', 192, -0.6621], ['delta 2a', 15, -4.515]] 清除刚刚创建的文件: ```python import os os.remove('data.csv') ``` ### 写 csv 文件 可以使用 `csv.writer` 写入文件,不过相应地,传入的应该是以写方式打开的文件,不过一般要用 `'wb'` 即二进制写入方式,防止出现换行不正确的问题: ```python data = [('one', 1, 1.5), ('two', 2, 8.0)] with open('out.csv', 'w') as fp: w = csv.writer(fp) w.writerows(data) ``` 显示结果: ```python ! cat 'out.csv' ``` one,1,1.5 two,2,8.0

文档信息

Search

    Table of Contents