Python教程学习 (4)

参考:

6. 模块 — Python 3.12.0 文档

7. 输入与输出 — Python 3.12.0 文档

模块

拆分的包含 Python 定义和语句的子文件就是Python中的模块。文件名是模块名加后缀名.py。使用模块时,使用"import 文件名",其中文件名不带".py"。

如果经常使用某个函数,可以把它赋值给局部变量。

>>> fib = fibo.fib
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

模块详解

每个模块都有自己的私有命名空间,它会被用作模块中定义的所有函数的全局命名空间。 因此,模块作者可以在模块内使用全局变量而不必担心与用户的全局变量发生意外冲突。

还有一种 import 语句的变化形式可以将来自某个模块的名称直接导入到导入方模块的命名空间中。下面例子中,“导入方”就是这个对话框所代表的解释器自身。注意:这条语句不会将所导入的模块的名称引入到局部命名空间中(因此在本示例中,fibo 将是未定义的名称)。

>>> from fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

还有一种变体可以导入模块内定义的所有名称:from fibo import *。这种方式会导入所有不以下划线(_)开头的名称。大多数情况下,不要用这个功能,这种方式向解释器导入了一批未知的名称,可能会覆盖已经定义的名称。

模块名后使用 as 时,直接把 as 后的名称与导入模块绑定。

>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

可以和from组合使用:from fibo import fib as fibonacci

在我的windows机器上,python解释器是操作系统自带的,而我自己的python文件不想和Windows系统文件放在一起。因此需要使用以下命令将我自己的python文件路径加入到sys.path中。具体方法如下:

>>> import sys
>>> sys.path.append('C:\\D\\03_programTry\\pyExec') # using escape character
>>> print(sys.path)
['', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.11_3.11.1008.0_x64__qbz5n2kfra8p0\\python311.zip', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.11_3.11.1008.0_x64__qbz5n2kfra8p0\\DLLs', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.11_3.11.1008.0_x64__qbz5n2kfra8p0\\Lib', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.11_3.11.1008.0_x64__qbz5n2kfra8p0', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.11_3.11.1008.0_x64__qbz5n2kfra8p0\\Lib\\site-packages', 'C:\\D\\03_programTry\\pyExec']
>>> from fibo import fib as f
>>> f(200)
0 1 1 2 3 5 8 13 21 34 55 89 144


可以看到我的工作路径:“C:\D\03_programTry\pyExec”被添加到了sys.path最后,于是可以使用这个路径下定义的fibo.py模块了。sys.path后面还会专门讲到。

以脚本方式执行模块

TODO: 这个部分没有试验成功

模块搜索路径

当导入一个名为 spam 的模块时,解释器首先会搜索具有该名称的内置模块。 这些模块的名称在 sys.builtin_module_names 中列出。 如果未找到,它将在变量 sys.path 所给出的目录列表中搜索名为 spam.py 的文件。

初始化后,Python 程序可以更改 sys.path(前面的例子中展示了如何在sys.path中添加我自己的工作目录“C:\D\03_programTry\pyExec”)。脚本所在的目录先于标准库所在的路径被搜索。这意味着,脚本所在的目录如果有和标准库同名的文件,那么加载的是该目录里的,而不是标准库的。这一般是一个错误,除非这样的替换是你有意为之。

“已编译的” Python 文件

为了快速加载模块,Python 把模块的编译版本缓存在 __pycache__ 目录中,文件名为 module.version.pyc,version 对编译文件格式进行编码,一般是 Python 的版本号。

在我的电脑上,可以发现工作路径下多了这个文件

标准模块

sys.ps1和sys.ps2变量定义主提示符和辅助提示符的显示形式,我们可以手动改变这些显示符号。下面的例子展示了将主提示符改为'C> ',将辅提示符改为'---'的行为。

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> sys.ps2 = '---'
C> n = 0
C> while n < 10:
---  print(n, end = ' ')
---  n = n + 1
---else:
---  print()
---
0 1 2 3 4 5 6 7 8 9
C>

dir()函数

内置函数dir()用于显示某个模块中所有的定义。返回结果是经过排序的字符串列表。

C> import fibo, sys
C> dir(fibo)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2']

没有参数时,dir() 列出当前已定义的名称。可以看到,自己定义的列表a也在这个dir()的输出列表中。个人感觉,dir()有一点类似c编译时产生的符号表。

C> a = [1, 2, 3, 4, 5]
C> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'fibo', 'n', 'sys']

dir() 不会列出内置函数和变量的名称。这些内容的定义在标准模块 builtins 中。可以import builtins之后,显示地调用dir(builtins)查看内置函数和变量。

包是通过使用“带点号模块名”来构造 Python 模块命名空间的一种方式。 例如,模块名 A.B 表示名为 A 的包中名为 B 的子模块。 就像使用模块可以让不同模块的作者不必担心彼此的全局变量名一样,使用带点号模块名也可以让 NumPy 或 Pillow 等多模块包的作者也不必担心彼此的模块名冲突。

导入包时,Python 搜索 sys.path 里的目录,查找包的子目录。

必须要有 __init__.py 文件才能让 Python 将包含该文件的目录当作包来处理。在最简单的情况下,__init__.py 可以只是一个空文件,但它也可以执行包的初始化代码或设置 __all__ 变量,这将在后面详细描述。

注意,使用 from package import item 时,item 可以是包的子模块(或子包),也可以是包中定义的函数、类或变量等其他名称。

相反,使用 import item.subitem.subsubitem 句法时,除最后一项外,每个 item 都必须是包;最后一项可以是模块或包,但不能是上一项中定义的类、函数或变量

具体例子可以查看文档中的描述。

从包中导入 *

如果包的 __init__.py 代码定义了列表 __all__,运行 from package import * 时,它就是被导入的模块名列表。形式如下:

__all__ = ["echo", "surround", "reverse"]

请注意子模块可能会受到本地定义名称的影响。 例如,如果你在 sound/effects/__init__.py 文件中添加了一个 reverse 函数,from sound.effects import * 将只导入 echosurround 这两个子模块,但 不会 导入 reverse 子模块,因为它被本地定义的 reverse 函数所遮挡:

__all__ = [
    "echo",      # refers to the 'echo.py' file
    "surround",  # refers to the 'surround.py' file
    "reverse",   # !!! refers to the 'reverse' function now !!!
]

def reverse(msg: str):  # <-- this name shadows the 'reverse.py' submodule
    return msg[::-1]    #     in the case of a 'from sound.effects import *'

可以把模块设计为用 import * 时只导出遵循指定模式的名称,但仍不提倡在产品代码中使用这种做法。

相对导入

.表示当前路径,..表示上一级路径。

主模块名永远是 "__main__" ,所以如果计划将一个模块用作 Python 应用程序的主模块,那么该模块内的导入语句必须始终使用绝对导入。

多目录中的包

包还支持一个特殊属性 __path__

这个属性的用法还有待学习 TODO

输入与输出

更复杂的输出格式

格式化输出方式

使用 格式化字符串字面值要在字符串开头的引号/三引号前添加 fF 。在这种字符串中,可以在 {} 字符之间输入引用的变量,或字面值的 Python 表达式。

>>> year = 2023
>>> event = 'Referendum'
>>> f'Results of the {year} {event}'
'Results of the 2023 Referendum'

使用 str.format() 方法

>>> yes_votes = 42_572_654
>>> no_votes = 43_132_495
>>> percentage = yes_votes / (yes_votes + no_votes)
>>> '{:-9} YES votes  {:2.2%}'.format(yes_votes, percentage)
' 42572654 YES votes  49.67%'

数字“42_572_654”和“42572654”是等效的。

快速显示变量,还可以使用 repr()str() 函数把值转化为字符串

>>> str(1/7)
'0.14285714285714285'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"

格式化字符串字面值

可以在表达式后面使用格式说明符,更好地控制格式化值的方式。

>>> import math
>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.

':' 后传递整数,为该字段设置最小字符宽度,常用于列对齐

还有一些修饰符可以在格式化前转换值。 '!a' 应用 ascii()'!s' 应用 str()'!r' 应用 repr()

>>> age = 20
>>> print(f'My age is {age!r}.')
My age is 20.
>>> print(f'My age is {age!a}.')
My age is 20.
>>> print(f'My age is {age!s}.')
My age is 20.

= 说明符可被用于将一个表达式扩展为表达式文本、等号再加表达式求值结果的形式。

>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'

字符串 format() 方法

花括号及之内的字符(称为格式字段)被替换为传递给 str.format() 方法的对象。花括号中的数字表示传递给 str.format() 方法的对象所在的位置。

>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam

str.format() 方法中使用关键字参数名引用值。

位置参数和关键字参数可以任意组合

可以打印字典,并用方括号 '[]' 访问字典的键。

这也可以通过将 table 字典作为采用 ** 标记的关键字参数传入来实现。

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
... 'Dcab: {0[Dcab]:d}'.format(table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678
>>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

手动格式化字符串

字符串对象的 str.rjust() 方法通过在左侧填充空格,对给定宽度字段中的字符串进行右对齐。

>>> for x in range(1, 11):
...   print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
...   print(repr(x**3).rjust(4))
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

同类方法还有 str.ljust()str.center()。center()对出来的效果不太好,一般使用rjust()和ljust()。

>>> for x in range(1, 11):
...   print(repr(x).center(2), repr(x*x).center(3), end=' ')
...   print(repr(x**3).center(4))
...
1   1   1
2   4   8
3   9   27
4   16  64
5   25 125
6   36 216
7   49 343
8   64 512
9   81 729
10 100 1000

另一种方法是 str.zfill() ,该方法在数字字符串左边填充零,且能识别正负号。这种打印方式常用于固定个数的数字字符,且首位需要补零的情况,比如MCC(Mobile Country Code),MNC(Mobile Network Code)等。

旧式字符串格式化方法

Python也支持c语言的字符串格式化方式

>>> import math
>>> print('The value of pi is approximately %5.3f.' % math.pi)
The value of pi is approximately 3.142.

读写文件

open()用于打开文件。

最常使用的是两个位置参数和一个关键字参数:open(filename, mode, encoding=None)。

encoding表示文件使用的字符编码,比如"utf-8"。如果没有指定 encoding ,默认的是与平台有关的。

在文本模式下读取文件时,默认把平台特定的行结束符(Unix 上为 \n, Windows 上为 \r\n)转换为 \n。在文本模式下写入数据时,默认把 \n 转换回平台特定结束符。这种操作方式在后台修改文件数据对文本文件来说没有问题,但会破坏 JPEGEXE 等二进制文件中的数据。注意,在读写此类文件时,一定要使用二进制模式。方法是在mode中加一个'b'。

在处理文件对象时,最好使用 with 关键字。优点是,子句体结束后,文件会正确关闭,即便触发异常也可以。而且,使用 with 相比等效的 try-finally 代码块要简短得多。其中,这里的“子句体”就是代码块。

如果没有使用 with 关键字,则应调用 f.close() 关闭文件,即可释放文件占用的系统资源。

警告:调用 f.write() 时,未使用 with 关键字,或未调用 f.close(),即使程序正常退出,也**可能** 导致 f.write() 的参数没有完全写入磁盘。

文件对象的方法

f.read(size) 可用于读取文件内容,它会读取一些数据,并返回字符串(文本模式),或字节串对象(在二进制模式下)。 size 是可选的数值参数。省略 sizesize 为负数时,读取并返回整个文件的内容;文件大小是内存的两倍时,会出现问题。size 取其他值时,读取并返回最多 size 个字符(文本模式)或 size 个字节(二进制模式)。如已到达文件末尾,f.read() 返回空字符串('')。

f.readline() 从文件中读取单行数据;字符串末尾保留换行符(\n),只有在文件不以换行符结尾时,文件的最后一行才会省略换行符。这种方式让返回值清晰明确;只要 f.readline() 返回空字符串,就表示已经到达了文件末尾,空行使用 '\n' 表示,该字符串只包含一个换行符。

从文件中读取多行时,可以用循环遍历整个文件对象。这种操作能高效利用内存,快速,且代码简单。

>>> for line in f:
...   print(line, end='')
...
This is the first line of the file.
Second line of the file

如需以列表形式读取文件中的所有行,可以用 list(f)f.readlines()

f.write(string) string 的内容写入文件,并返回写入的字符数。

f.tell() 返回整数,给出文件对象在文件中的当前位置,表示为二进制模式下时从文件开始的字节数,以及文本模式下的意义不明的数字。用c语言的方式来理解,就是指出文件指针当前的偏移量。

f.seek(offset, whence) 可以改变文件对象的位置。通过向参考点添加 offset 计算位置;参考点由 whence 参数指定。 whence 值为 0 时,表示从文件开头计算,1 表示使用当前文件位置,2 表示使用文件末尾作为参考点。省略 whence 时,其默认值为 0,即使用文件开头作为参考点。

>>> 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.seek()返回的是当前文件对象在文件中的位置。

在文本文件(模式字符串未使用 b 时打开的文件)中,只允许相对于文件开头搜索(使用 seek(0, 2) 搜索到文件末尾是个例外)。

使用 json 保存结构化数据

Python 允许你使用流行的数据交换格式 JSON (JavaScript Object Notation),标准库模块 json 可以接受带有层级结构的 Python 数据,并将其转换为字符串表示形式;这个过程称为 serializing。根据字符串表示形式重建数据则称为 deserializing

例如:

>>> import json
>>> print(json.dumps([1, 2, 3, {'4': 5, '6': 7}], separators=(',', ':'), indent=4))
[
    1,
    2,
    3,
    {
        "4":5,
        "6":7
    }
]

详细用法查看https://docs.python.org/zh-cn/3/library/json.html#module-json

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值