python模块和包

文章目录

  • 模块
    • 模块的概念
      • 新建一个模块
      • 作用域
    • 模块的导入方式
      • import 导入
      • from...import 导入
      • 动态导入模块
    • 模块的搜索顺序
    • `__name__` 属性
    • 不同级目录下的模块调用
    • 概念
    • 案例演练
    • `__init__.py`
  • 安装第三方模块
    • pip
    • conda

模块

模块的概念

模块是 Python 程序架构的一个核心概念

  • 每一个以扩展名 py 结尾的 Python 源代码文件都是一个 模块
  • 模块名 同样也是一个 标识符,需要符合标识符的命名规则
  • 在模块中定义的 全局变量函数 都是提供给外界直接使用的工具
  • 模块 就好比是 工具包,要想使用这个工具包中的工具,就需要先 导入 这个模块

新建一个模块

%%writefile produce_code.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__date__ = '2018/11/29'
__author__ = 'sunchengquan'
__mail__ = '[email protected]'
__doc__ = 'a produce code module'

import random

title = 'public'
_title = 'private'

def test():
    checkcode=''
    for i in range(5):
        current=random.randrange(0,5)
        if current == i:
            tmp=chr(random.randint(65,90))
        else:
            tmp=random.randint(0,9)
        checkcode+=str(tmp)
    return checkcode

class Test:
    pass


def _main():
    print("-------main-------")

    
if __name__=='__main__':
    _main()
    
Overwriting produce_code.py
  • 第1行和第2行是标准注释,第1行注释可以让这个produce_code.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;
  • __date__ 变量,编写模块的日期
  • __author__ 变量,把作者写进去
  • __mail__ 变量,邮件信息
  • __doc__ ,文档注释
  • import , 导入模块,后面详细介绍
  • title ,定义一个全局变量,值为‘public’
  • _title ,定义一个私有变量,值为‘private’
  • 定义一个名为test的函数
  • 定义一个名为Test的类
  • 定义一个名为_main的私有函数
  • __name__ ,被其他文件导入的,__name__ 就是 模块名,当前执行的程序 __name__ 是 __main__

执行produce_code.py

!python produce_code.py
-------main-------

作用域

上面提到的类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途, 我们自己的变量一般不要用这种变量名

正常的函数和变量名是公开的(public),可以被直接引用

类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等;

执行如下代码:

%%writefile test.py
import produce_code

print(produce_code.__date__)
print(produce_code.__author__)
print(produce_code.__mail__)
print(produce_code.__doc__)
print(produce_code.test())
print(produce_code.title)
print(produce_code._title)
print(produce_code._main())
Overwriting test.py
!python test.py
2018/11/29
sunchengquan
[email protected]
a produce code module
26838
public
private
-------main-------
None

我去,不是私有变量,私有函数不能访问吗?

换一种导入方式

%%writefile test.py
from produce_code import *

# print(__date__)
# print(__author__)
# print(__mail__)
# print(__doc__)
print(test())
print(title)
print(_title)
print(_main())
Overwriting test.py
!python test.py
646S6
public
Traceback (most recent call last):
  File "test.py", line 9, in 
    print(_title)
NameError: name '_title' is not defined

不会导入私有变量和私有函数,还有特殊变量

private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量

外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public

模块的导入方式

import 导入

  • 测试模块1.py
  • 测试模块2.py
%%writefile 测试模块1.py
# 全局变量
title = "模块1"

# 函数
def say_hello():
    print("我是 %s" % title)


# 类
class Dog(object):
    pass
Overwriting 测试模块1.py
%%writefile 测试模块2.py
# 全局变量
title = "模块2"


# 函数
def say_hello():
    print("我是 %s" % title)


# 类
class Cat(object):
    pass
Overwriting 测试模块2.py
import 测试模块1
import 测试模块2

测试模块1.say_hello()
测试模块2.say_hello()

dog = 测试模块1.Dog()
print(dog)

cat = 测试模块2.Cat()
print(cat)

我是 模块1
我是 模块2
<测试模块1.Dog object at 0x7effc7b372b0>
<测试模块2.Cat object at 0x7effb4fcfc50>

import同时指定别名

import 测试模块1 as DogModule
import 测试模块2 as CatModule

DogModule.say_hello()
CatModule.say_hello()

dog = DogModule.Dog()
print(dog)

cat = CatModule.Cat()
print(cat)
我是 模块1
我是 模块2
<测试模块1.Dog object at 0x7effb4fcfeb8>
<测试模块2.Cat object at 0x7effc7b372b0>

from…import 导入

from 测试模块1 import Dog
from 测试模块2 import say_hello

say_hello()

wangcai = Dog()
print(wangcai)


我是 模块2
<测试模块1.Dog object at 0x7effb4fc0748>

如果两个模块中的函数名相同怎么办?

## 从 模块 导入 所有工具
from 测试模块1 import *
from 测试模块2 import *

print(title)
say_hello()

wangcai = Dog()
print(wangcai)
模块2
我是 模块2
<测试模块1.Dog object at 0x7effb4f925c0>

如果 两个模块,存在 同名的函数,那么 后导入模块的函数,会 覆盖掉先导入的函数

注意

这种方式不推荐使用,因为函数重名并没有任何的提示,出现问题不好排查

from 测试模块1 import say_hello
from 测试模块2 import say_hello as module2_say_hello


say_hello()
module2_say_hello()
我是 模块1
我是 模块2

动态导入模块

  • 主要用于反射或者延迟加载模块。

建一个包,名为lib

  • __init__.py
  • test.py
!mkdir -p lib
%%writefile ./lib/__init__.py


Overwriting ./lib/__init__.py
%%writefile ./lib/test.py

class Person(object):
    def __init__(self):
        self.name = 'sunchengquan'

# if __name__ == "__main__":


obj = Person()
print(obj.name)
Overwriting ./lib/test.py

__import__

说明:

  • __import__(module)相当于import module

使用 __import__ 动态以字符串形式导入lib下的test模块

lib = __import__("lib.test")
obj = lib.test.Person()
print(obj.name)

sunchengquan
sunchengquan
%%writefile reflection.py
lib = __import__("lib.test")
print(hasattr(lib.test,'Person'))
instance = getattr(lib.test,'Person')
print(instance().name)
Writing reflection.py
!python reflection.py
sunchengquan
True
sunchengquan

import importlib

实例还是上面的lib.test模块,这里使用importlib进行动态导入(这个方法好理解,也是官方建议使用的)

import importlib    
test = importlib.import_module("lib.test")
print(test.Person().name)
sunchengquan

模块的搜索顺序

Python 的解释器在 导入模块 时,会:

  1. 搜索 当前目录 指定模块名的文件,如果有就直接导入
  2. 如果没有,再搜索 系统目录

在开发时,给文件起名,不要和 系统的模块文件 重名

%%writefile 模块的搜索顺序.py
import random

#Python中每一个模块都有一个内置属性 `__file__` 可以 查看模块的完整路径
print(random.__file__)

rand = random.randint(0, 10)

print(rand)
Overwriting 模块的搜索顺序.py
!python 模块的搜索顺序.py
/root/miniconda3/lib/python3.7/random.py
0

在当前目录下新建一个名为random的模块

%%writefile random.py

def randint(num1,num2):
    return num1+10+num2
    
    
Writing random.py
!python 模块的搜索顺序.py
!rm -rf random.py
/root/python_code_scq/python_basic/random.py
20

注意:如果当前目录下,存在一个 random.py 的文件,程序就无法正常执行了!

  • 这个时候,Python 的解释器会 加载当前目录 下的 random.py 而不会加载 系统的 random 模块

__name__ 属性

实际开发场景

  • 在实际开发中,每一个模块都是独立开发的,大多都有专人负责
  • 开发人员 通常会在 模块下方 增加一些测试代码
    • 仅在模块内使用,而被导入到其他文件中不需要执行

__name__ 属性可以做到,测试模块的代码 只在测试情况下被运行,而在 被导入时不会被执行

  • __name__Python 的一个内置属性,记录着一个 字符串
  • 如果 是被其他文件导入的__name__ 就是 模块名
  • 如果 是当前执行的程序 __name____main__

在很多 Python 文件中都会看到以下格式的代码

# 导入模块
# 定义全局变量
# 定义类
# 定义函数

# 在代码的最下方
def main():
    # ...
    pass

# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
    main()

%%writefile name_test.py
if __name__ == '__main__':
    print('程序自身在运行')
else:
    print('我来自另一模块')
Writing name_test.py
!python name_test.py
程序自身在运行
import name_test

我来自另一模块

不同级目录下的模块调用

实例演示

  • 新建目录sun
  • 在sun目录下新建一个模块module_sun.py
  • 在sun目录下新建目录module
  • 在./sun/module目录下新建一个模块main.py
!mkdir -p sun
%%writefile ./sun/module_sun.py

name='sun'
def say_hello():
    print('hello sun')


def logger():
    print('in the module sun')

def running():
    pass
Writing ./sun/module_sun.py
!mkdir -p sun/module
%%writefile ./sun/module/main.py
import module_sun
from module_sun import logger  as f

def logger():
    print('in the main')
module_sun.logger()
logger()
f()
print(module_sun.name)
module_sun.say_hello()
Overwriting ./sun/module/main.py

目录结构如下:

!tree ./sun
./sun
├── module
│   └── main.py
├── module_sun.py
└── __pycache__
    └── module_sun.cpython-37.pyc

2 directories, 3 files

运行mian.py

!python ./sun/module/main.py
Traceback (most recent call last):
  File "./sun/module/main.py", line 1, in 
    import module_sun
ModuleNotFoundError: No module named 'module_sun'

如何解决呢?

%%writefile ./sun/module/main.py
import sys,os
print("之前的环境路径",sys.path)
x=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(x)
print("之后的环境路径",sys.path)


import module_sun
from module_sun import logger  as f

def logger():
    print('in the main')
module_sun.logger()
logger()
f()
print(module_sun.name)
module_sun.say_hello()

Overwriting ./sun/module/main.py
!python ./sun/module/main.py
之前的环境路径 ['/root/python_code_scq/python_basic/sun/module', '/root/miniconda3/lib/python37.zip', '/root/miniconda3/lib/python3.7', '/root/miniconda3/lib/python3.7/lib-dynload', '/root/miniconda3/lib/python3.7/site-packages']
之后的环境路径 ['/root/python_code_scq/python_basic/sun/module', '/root/miniconda3/lib/python37.zip', '/root/miniconda3/lib/python3.7', '/root/miniconda3/lib/python3.7/lib-dynload', '/root/miniconda3/lib/python3.7/site-packages', '/root/python_code_scq/python_basic/sun']
in the module sun
in the main
in the module sun
sun
hello sun

概念

  • 是一个 包含多个模块特殊目录
  • 目录下有一个 特殊的文件 __init__.py
  • 包名的 命名方式 和变量名一致,小写字母 + _

好处

  • 使用 import 包名 可以一次性导入 所有的模块

案例演练

  1. 新建一个 message
  2. 在目录下,新建两个文件 send_messagereceive_message
  3. send_message 文件中定义一个 send 函数
  4. receive_message 文件中定义一个 receive 函数
  5. 在外部直接导入 message 的包
!mkdir -p message
%%writefile ./message/send_message.py


def send(text):
    print("正在发送 %s..." % text)

Writing ./message/send_message.py
%%writefile ./message/receive_message.py


def receive():
    return "这是来自 100xx 的短信"
    
Writing ./message/receive_message.py
%%writefile message_test.py
import message


message.send_message.send("hello")

txt = message.receive_message.receive()
print(txt)
Writing message_test.py
!python message_test.py
Traceback (most recent call last):
  File "message_test.py", line 4, in 
    message.send_message.send("hello")
AttributeError: module 'message' has no attribute 'send_message'

__init__.py

  • 要在外界使用 中的模块,需要在 __init__.py 中指定 对外界提供的模块列表
%%writefile ./message/__init__.py

print('from the package message')
from . import send_message
from . import receive_message
Overwriting ./message/__init__.py
!python message_test.py
from the package message
正在发送 hello...
这是来自 100xx 的短信
%%writefile ./message/__init__.py

#from hm_messahe import *导入模块时,只有send_message
__all__ = ['send_message'] 
from . import send_message
from . import receive_message

Overwriting ./message/__init__.py
%%writefile message_test1.py
from message import * 

send_message.send("hello")

txt = receive_message.receive()
print(txt)
Overwriting message_test1.py
!python message_test1.py
正在发送 hello...
Traceback (most recent call last):
  File "message_test1.py", line 5, in 
    txt = receive_message.receive()
NameError: name 'receive_message' is not defined

安装第三方模块

一般来说,第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称

pip

pip install pandas
pip install django==1.8.2

conda

conda install pandas

建议下载miniconda/anaconda,使用conda安装

你可能感兴趣的:(python基础知识)