共计 8145 个字符,预计需要花费 21 分钟才能阅读完成。
一、模块
模块能够看成是一堆函数的集合体。
一个 py 文件外部就能够放一堆函数,因而一个 py 文件就能够看成一个模块。
如果这个 py 文件的文件名为module.py,模块名则是module。
1、模块的四种模式
在 Python 中,总共有以下四种模式的模块:
自定义模块:如果你本人写一个 py 文件,在文件内写入一堆函数,则它被称为自定义模块,即应用 python 编写的.py 文件
第三方模块:已被编译为共享库或 DLL 的 C 或 C ++ 扩大,如 requests
内置模块:应用 C 编写并链接到 python 解释器的内置模块,如 time
包(文件夹):把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py 文件,该文件夹称之为包)
2、为什么要用模块?
用第三方或者内置的模块是一种拿来主义,能够极大地晋升开发效率。
自定义模块,将咱们本人程序中用到的公共性能,写入一个 python 文件,而后程序的各局部组件能够通过导入的形式来援用自定义模块的性能。
二、如何用模块
个别咱们应用 import 和 from…import… 导入模块。
以下述 spam.py 内的文件代码为例。
# spam.py print('from the spam.py') money = 1000 def read1(): print('spam 模块:', money) def read2(): print('spam 模块') read1() def change(): global money money = 0
1、import 模块名
语法如下:
import module1[, module2[,... moduleN]
import 导入的模块,拜访须要加前缀。
import 首次导入模块产生了 3 件事:
以模块为准发明一个模块的名称空间
执行模块对应的文件,将执行过程中产生的名字都丢到模块的名称空间
在以后执行文件中拿到一个模块名
留神:模块的反复导入会间接援用之前发明好的后果,不会反复执行模块的文件。
# run.py import spam # from the spam.py import spam money = 111111 spam.read1() # 'spam 模块:1000' spam.change() print(spam.money) # 0 print(money) # 111111
导入重命名:smt 变量指向 span 模块的名称空间
# run.py import spam as sm money = 111111 sm.money sm.read1() # 'spam 模块:1000' sm.read2 sm.change() print(money) # 1000
导入多个模块
import spam, time, os # 举荐应用下述形式 import spam import time import os
2、from 模块名 import 具体的函数
语法如下:
from modname import name1[, name2[, ... nameN]]
这个申明不会把整个模块导入到以后的命名空间中,它只会将模块里的一个或多个函数引入进来。
from…import… 导入的模块,拜访不须要加前缀。
from…import… 首次导入模块产生了 3 件事:
以模块为准发明一个模块的名称空间
执行模块对应的文件,将执行过程中产生的名字都丢到模块的名称空间
在以后执行文件的名称空间中拿到一个名字,该名字间接指向模块中的某一个名字,意味着能够不必加任何前缀而间接应用
长处:不必加前缀,代码更加精简
毛病:容易与以后执行文件中名称空间中的名字抵触
# run.py from spam import money
from spam import money,read1 money = 10 print(money) # 10
rom … import * 语句:导入文件内所有的性能:
# spam.py __all__ = ['money', 'read1'] # 只容许导入 'money' 和 'read1'
# run.py from spam import * # 导入 spam.py 内的所有性能,但会受限制于__all__ money = 111111 read1() # 'spam 模块:1000' change() read1() # 'spam 模块:0' print(money) # 111111
3、循环导入
以下状况会呈现循环导入:
# m1.py
print('from m1.py')
from m2 import x
y = 'm1'
# m2.py
print('from m2.py')
from m1 import y
x = 'm2'
能够应用函数定义阶段只辨认语法的个性解决循环导入的问题,或从实质上解决循环导入的问题,然而最好的解决办法是不要呈现循环导入。
计划一:
# m1.py
print('from m1.py')
def func1():
from m2 import x
print(x)
y = 'm1'
# m2.py
print('from m2.py')
def func1():
from m1 import y
print(y)
x = 'm2'
计划二:
5、# m1.py
print('from m1.py')
y = 'm1'
from m2 import x
# m2.py
print('from m2.py')
x = 'm2'
from m1 import y
4、dir() 函数
内置的函数 dir() 能够找到模块内定义的所有名称。以一个字符串列表的模式返回:
dir(sys) ['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe', '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions']
如果没有给定参数,那么 dir() 函数会列举出以后定义的所有名称:
a = [1, 2, 3, 4, 5] import fibo fib = fibo.fib print(dir()) # 失去一个以后模块中定义的属性列表 # ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys'] b = 5 # 建设一个新的变量 'a' print(dir()) # ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b'] del b # 删除变量名 a print(dir()) # ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a']
三、模块搜寻门路
1、导入模块时查找模块的程序
1、先从内存中曾经导入的模块中寻找
如果咱们在运行 run.py 文件的时候,疾速删除 mmm.py 文件,咱们会发现文件会持续运行,而不会报错,因为 mmm 曾经被导入内存当中。如果咱们再一次运行 run.py 时会报错,因为 mmm.py 曾经被删除了。
# test.py import m1 # 从 m1.py 文件中导入的, 而后会生成 m1 模块的名称空间 import time # 删除 m1.py 文件,m1 模块的名称空间依然存在 time.sleep(10) import m1 # 不报错, 肯定不是从文件中获取了 m1 模块, 而是从内存中获取的
2、内置的模块
验证先从内置中找,不会先找自定义的 time.py 文件。
# time.py print('from time.py') # run.py import time print(time) #
3、环境变量 sys.path 中找(强调:sys.path 的第一个值是以后执行文件的所在的文件夹)
import sys for n in sys.path: print(n) # C:\PycharmProjects\untitled\venv\Scripts\python.exe C:/PycharmProjects/untitled/hello.py # C:\PycharmProjects\untitled # C:\PycharmProjects\untitled # C:\Python\Python38\python38.zip # C:\Python\Python38\DLLs # C:\Python\Python38\lib # C:\Python\Python38 # C:\PycharmProjects\untitled\venv # C:\PycharmProjects\untitled\venv\lib\site-packages
如果 mmm.py 在 C:\PycharmProjects\untitled\day16 门路下,而执行文件门路为 C:\PycharmProjects\untitled,如果一般导入肯定会报错,咱们能够把 C:\PycharmProjects\untitled\day16 增加到环境变量 sys.path 中,避免报错。
# run.py import sys sys.path.append(r'C:\PycharmProjects\untitled\day16') print(sys.path) import mmm mmm.f1()
2、搜寻门路以执行文件为准
假如咱们有如下目录构造的文件,文件内代码别离是:
而 hello 和 spam.py 不是同目录下的,因而 run.py 的环境变量无奈间接找到 m2, 须要从文件夹导入
from aa import spam print(spam.money)
四、Python 文件的两种用处
一个模块被另一个程序第一次引入时,其主程序将运行。如果咱们想在模块被引入时,模块中的某一程序块不执行,咱们能够用__name__属性来使该程序块仅在该模块本身运行时执行。
python 文件总共有两种用处,一种是执行文件;另一种是被当做模块导入。
每个模块都有一个__name__属性,当其值是 ’__main__’ 时,表明该模块本身在运行,否则是被引入。
1、当 run.py 运行的时候,aaa.py 被当做援用模块,它的__name__ == ‘aaa’(模块名),会执行 aaa.py 中的 f1()。
# aaa.py x = 1 def f1(): print('from f1') f1() # run.py import aaa
2、aaa.py 被当做可执行文件时,加上__name__ == ‘__main__’,独自运行 aaa.py 才会执行 aaa.py 中的 f1()。run.py 运行时能够避免执行 f1()。
# aaa.py x = 1 def f1(): print('from f1') if __name__ == '__main__': f1()
五、包
包是一种治理 Python 模块命名空间的模式,包的实质就是一个含有.py的文件的文件夹。
包采纳 ” 点模块名称 ”。比方一个模块的名称是 A.B,那么他示意一个包 A 中的子模块 B。
目录只有蕴含一个叫做 __init__.py 的文件才会被认作是一个包。
在导入一个包的时候,Python 会依据 sys.path 中的目录来寻找这个包中蕴含的子目录。
导入包产生的三件事:
创立一个包的名称空间
因为包是一个文件夹,无奈执行包,因而执行包下的.py 文件,将执行过程中产生的名字寄存于包名称空间中(即包名称空间中寄存的名字都是来自于.py)
在以后执行文件中拿到一个名字 aaa,aaa 是指向包的名称空间的
导入包就是在导入包下的.py,导入 m1 就是导入 m1 中的__init__。
1、两种形式导入:
import … :
import item.subitem.subsubitem 这种导入模式,除了最初一项,都必须是包,而最初一项则能够是模块或者是包,然而不能够是类,函数或者变量的名字。
from … import…:
当应用 from package import item 这种模式的时候,对应的 item 既能够是包外面的子模块(子包),或者包外面定义的其余名称,比方函数,类或者变量。
2、import 导入包内的模块
import 能够每次只导入一个包外面的特定模块,他必须应用全名去拜访。
import aaa.bbb.m3 print(aaa.bbb.m3.func3())
import 形式不能导入函数、变量:import aaa.bbb.m3.f3 谬误
3、from import 形式:
导入模块内具体的模块
这种形式不须要那些简短的前缀进行拜访
from aaa.bbb import m3 print(m3.func3())
导入模块内具体的性能
这种形式不须要那些简短的前缀进行拜访
from aaa.bbb.m3 import func3 print(func3())
4、相对导入和绝对导入
相对导入:
# aaa/.py from aaa.m1 import func1 from aaa.m2 import func2
绝对导入:
. 代表以后被导入文件所在的文件夹
.. 代表以后被导入文件所在的文件夹的上一级
… 代表以后被导入文件所在的文件夹的上一级的上一级
from .m1 import func1 from .m2 import func2
5、from…import *
导入语句遵循如下规定:如果包定义文件 init__.py 存在一个叫做 __all 的列表变量,那么在应用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。
这里有一个例子,在:file:sounds/effects/__init__.py 中蕴含如下代码:
__all__ = ["echo", "surround", "reverse"]
这示意当你应用 from sound.effects import * 这种用法时,你只会导入包外面这三个子模块。
六、软件开发的目录标准
为了进步程序的可读性与可维护性,咱们应该为软件设计良好的目录构造,这与标准的编码格调等同重要,简而言之就是把软件代码分文件目录。假如你要写一个 ATM 软件,你能够依照上面的目录构造治理你的软件代码:
ATM/ |-- core/ | |-- src.py # 业务外围逻辑代码 | |-- api/ | |-- api.py # 接口文件 | |-- db/ | |-- db_handle.py # 操作数据文件 | |-- db.txt # 存储数据文件 | |-- lib/ | |-- common.py # 共享性能 | |-- conf/ | |-- settings.py # 配置相干 | |-- bin/ | |-- run.py # 程序的启动文件,个别放在我的项目的根目录下,因为在运行时会默认将运行文件所在的文件夹作为 sys.path 的第一个门路,这样就省去了解决环境变量的步骤 | |-- log/ | |-- log.log # 日志文件 | |-- requirements.txt # 寄存软件依赖的内部 Python 包列表,详见 https://pip.readthedocs.io/en/1.1/requirements.html |-- README # 我的项目阐明文件
settings.py
# settings.py import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DB_PATH = os.path.join(BASE_DIR, 'db', 'db.txt') LOG_PATH = os.path.join(BASE_DIR, 'log', 'user.log') # print(DB_PATH) # print(LOG_PATH)
common.py
# common.py import time from conf import settings def logger(msg): current_time = time.strftime('%Y-%m-%d %X') with open(settings.LOG_PATH, mode='a', encoding='utf-8') as f: f.write('%s %s' % (current_time, msg))
src.py
# src.py from conf import settings from lib import common def login(): print('登陆') def register(): print('注册') name = input('username>>:') pwd = input('password>>:') with open(settings.DB_PATH, mode='a', encoding='utf-8') as f: f.write('%s:%s\n' % (name, pwd)) # 记录日志。。。。。。common.logger('%s 注册胜利' % name) print('注册胜利') def shopping(): print('购物') def pay(): print('领取') def transfer(): print('转账') func_dic = {'1': login, '2': register, '3': shopping, '4': pay, '5': transfer,} def run(): while True: print("""1 登陆 2 注册 3 购物 4 领取 5 转账 6 退出""") choice = input('>>>:').strip() if choice == '6': break if choice not in func_dic: print('输出谬误命令,傻叉') continue func_dic[choice]()
run.py
# run.py import sys import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR) from core import src if __name__ == '__main__': src.run()