python小兵(11)自定义模块

自定义模块

我们今天来学习一下自定义模块(也就是私人订制),我们要自定义模块,首先就要知道什么是模块啊

一个函数封装一个功能,比如现在有一个软件,不可能将所有程序都写入一个文件,所以咱们应该分文件,组织结构要好,代码不冗余,所以要分文件,但是分文件,分了5个文件,每个文件里面可能都有相同的功能(函数),怎么办?所以将这些相同的功能封装到一个文件中.
模块就是文件,存放一堆函数,谁用谁拿。怎么拿?
比如:我要策马奔腾共享人世繁华,应该怎么样?我应该骑马,你也要去浪,你是不是也要骑马。
模块是一系列常用功能的集合体,一个py文件就是一个模块

为什么要使用模块?

1、从文件级别组织程序,更方便管理
随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用

2、拿来主义,提升开发效率
同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率,避免重复造轮子。

ps:
如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python meet.py方式去执行,此时meet.py被称为脚本script。

'''
-*- coding: utf-8 -*-
@Author  : Meet
@Software: PyCharm
@File    : meet.py
'''
print('from the meet.py')

name = 'guoboayuan'

def read1(): print('meet模块:',name) def read2(): print('meet模块') read1() def change(): global name name = 'meet'

导入

import 翻译过来是一个导入的意思

模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行
(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:
第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句),
如下 

import spam #只在第一次导入时才执行meet.py内代码,此处的显式效果是只打印一次'from the meet.py',当然其他的顶级代码也都被执行了, 只不过没有显示效果.

代码示例:

import meet
import meet
import meet
import meet
import meet 执行结果: 只打印一次 from the meet.py

  每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,
就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突

示例:

当前是meet.py

扫描二维码关注公众号,回复: 4927539 查看本文章
import meet

name = 'alex'
print(name)
print(meet.name)
'''
结果:
from the meet.py
alex
guoboayuan
'''
import meet
def read1(): print(666) meet.read1() ''' from the meet.py meet模块: guoboayuan '''
import meet
name = '日天'
meet.change()
print(name)
print(meet.name)
'''
from the meet.py
日天
宝元
'''

为模块起别名

别名其实就是一个绰号,好处可以将很长的模块名改成很短,方便使用.

import meet.py as t
t.read1()

有利于代码的扩展和优化

#mysql.py
def sqlparse(): print('from mysql sqlparse') #oracle.py def sqlparse(): print('from oracle sqlparse') #test.py db_type=input('>>: ') if db_type == 'mysql': import mysql as db elif db_type == 'oracle': import oracle as db db.sqlparse()

导入多个模块

import os,sys,json   这样写可以但是不推荐
推荐写法

import os
import sys
import json

多行导入:易于阅读 易于编辑 易于搜索 易于维护

from ... import ...

from...import...使用

from meet import name, read1
print(name)
read1()
'''
执行结果:
from the meet.py
guoboayuan
meet模块: guoboayuan
'''

from...import... 与import对比

唯一的区别就是:使用from...import...则是将spam中的名字直接导入到当前的名称空间中,所以在当前名称空间中,
直接使用名字就可以了、无需加前缀:meet.

from...import...的方式有好处也有坏处
​ 好处:使用起来方便了
​ 坏处:容易与当前执行文件中的名字冲突

示例演示:

1.执行文件有与模块同名的变量或者函数名,会有覆盖效果。

name = 'oldboy'
from meet import name, read1, read2
print(name) 
'''
执行结果:
from the meet.py
guoboayuan
'''
from meet import name, read1, read2
name = 'oldboy'
print(name)

'''
执行结果:
oldboy

'''
def read1():
    print(666) from meet import name, read1, read2 read1() ''' 执行结果: from the meet.py meet模块: guoboayuan '''
from meet import name, read1, read2
def read1(): print(666) read1() ''' 执行结果: from the meet.py 666 '''

2.当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间

#测试一:导入的函数read1,执行时仍然回到meet.py中寻找全局变量name
#test.py
from meet import read1
name = 'alex' read1() ''' 执行结果: from the meet.py meet--> read1 --> guobaoyuan '''
#测试二:导入的函数read2,执行时需要调用read1(),仍然回到meet.py中找read1()
#test.py
from meet import read2
def read1(): print('==========') read2() ''' 执行结果: from the meet.py meet --> read2 --> 'meet模块' --> read1 -->'guobaoyuan' '''

from导入的模式也支持as

from meet import read1 as read
read()

from导入的时候,一行导入多个内容

from meet import read1,read2,name

全部导入
from meet import *

#from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置

#大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

可以使用__all__来控制*(用来发布新版本),在meet.py中新增一行

__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

模块循环导入问题

模块循环/嵌套导入抛出异常的根本原因是由于在python中模块被导入一次之后,就不会重新导入,只会在第一次导入时执行模块内代码

在我们的项目中应该尽量避免出现循环/嵌套导入,如果出现多个模块都需要共享的数据,可以将共享的数据集中存放到某一个地方

在程序出现了循环/嵌套导入后的异常分析、解决方法如下(了解,以后尽量避免)

示范文件内容如下

#创建一个m1.py
print('正在导入m1')
from m2 import y x='m1' #创建一个m2.py print('正在导入m2') from m1 import x y='m2' #创建一个run.py import m1 #测试一 执行run.py会抛出异常 正在导入m1 正在导入m2 Traceback (most recent call last): File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module> import m1 File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> from m2 import y File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module> from m1 import x ImportError: cannot import name 'x' #测试一结果分析 先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1" --->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错 #测试二:执行文件不等于导入文件,比如执行m1.py不等于导入了m1 直接执行m1.py抛出异常 正在导入m1 正在导入m2 正在导入m1 Traceback (most recent call last): File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> from m2 import y File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module> from m1 import x File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module> from m2 import y ImportError: cannot import name 'y' #测试二分析 执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报错 # 解决方法: 方法一:导入语句放到最后 #m1.py print('正在导入m1') x='m1' from m2 import y #m2.py print('正在导入m2') y='m2' from m1 import x 方法二:导入语句放到函数中 #m1.py print('正在导入m1') def f1(): from m2 import y print(x,y) x = 'm1' # f1() #m2.py print('正在导入m2') def f2(): from m1 import x print(x,y) y = 'm2' #run.py import m1 m1.f1()

模块的重载(了解)

考虑到性能的原因,每个模块只被导入一次,放入字典sys.module中,如果你改变了模块的内容,你必须重启程序,python不支持重新加载或卸载之前导入的模块,

有的同学可能会想到直接从sys.module中删除一个模块不就可以卸载了吗,注意了,你删了sys.module中的模块对象仍然可能被其他程序的组件所引用,因而不会被清楚。

特别的对于我们引用了这个模块中的一个类,用这个类产生了很多对象,因而这些对象都有关于这个模块的引用。

py文件的两种功能

#编写好的一个python文件可以有两种用途:
    一:脚本,一个文件就是整个程序,用来被执行
    二:模块,文件中存放着一堆功能,用来被导入使用


#python为我们内置了全局变量__name__,
    当文件被当做脚本执行时:__name__ 等于'__main__'
    当文件被当做模块导入时:__name__等于模块名

#作用:用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码)
if __name__ == '__main__':
print('from the meet.py')

__all__ = ['name', 'read1', ]

name = 'guobaoyuan'


def read1(): print('meet模块:', name) def read2(): print('meet模块') read1() def change(): global name name = '宝元' if __name__ == '__main__': # 在模块文件中测试read1()函数 # 此模块被导入时 __name__ 就变成了文件名,if条件不成立 # 所以read1不执行 read1()

模块的搜索路径

模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块

#模块的查找顺序
1、在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用
    ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看
2、如果没有,解释器则会查找同名的内建模块
3、如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。

#需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错。 #在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。 >>> import sys >>> sys.path.append('/a/b/c/d') >>> sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索 注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找. #windows下的路径不加r开头,会语法错误 sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a') 

编译Python文件(了解)

为了提高加载模块的速度,强调强调强调:提高的是加载速度而绝非运行速度。python解释器会在__pycache__目录中下缓存每个模块编译后的版本,格式为:module.version.pyc。通常会包含python的版本号。例如,在CPython3.3版本下,spam.py模块会被缓存成__pycache__/spam.cpython-33.pyc。这种命名规范保证了编译后的结果多版本共存。

Python检查源文件的修改时间与编译的版本进行对比,如果过期就需要重新编译。这是完全自动的过程。并且编译的模块是平台独立的,所以相同的库可以在不同的架构的系统之间共享,即pyc使一种跨平台的字节码,类似于JAVA火.NET,是由python虚拟机来执行的,但是pyc的内容跟python的版本相关,不同的版本编译后的pyc文件不同,2.5编译的pyc文件不能到3.5上执行,并且pyc文件是可以反编译的,因而它的出现仅仅是用来提升模块的加载速度的,不是用来加密的。

#提示:
1.模块名区分大小写,foo.py与FOO.py代表的是两个模块
2.在速度上从.pyc文件中读指令来执行不会比从.py文件中读指令执行更快,只有在模块被加载时,.pyc文件才是更快的 3.只有使用import语句是才将文件自动编译为.pyc文件,在命令行或标准输入中指定运行脚本则不会生成这类文件.

 

猜你喜欢

转载自www.cnblogs.com/huhuxixi/p/10273041.html