Python模塊和包

1、模塊

一、模塊的定義:

一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。

二、爲何要使用模塊

隨着程序的發展,功能愈來愈多,爲了方便管理,咱們一般將程序分紅一個個的文件,這樣作程序的結構更清晰,方便管理。這時咱們不只僅能夠把這些文件當作腳本去執行,還能夠把他們當作模塊來導入到其餘的模塊中,實現了功能的重複利用

三、如何使用模塊

3.1 import

樣例文件:spam.py,文件名spam.py,模塊名spam
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#spam.py
print('from the spam.py')

money=10000

def read1():
    print('spam->read1->money',money)

def read2():
    print('spam->read2')
    read1()

def change():
    global money
    money=0

if __name__ == '__main__':
    print("文件被當作腳本執行")

模塊能夠包含可執行的語句和函數的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執行(import語句是能夠在程序中的任意位置使用的,且針對同一個模塊很import屢次,爲了防止你重複導入,python的優化手段是:第一次導入後就將模塊名加載到內存了,後續的import語句僅是對已經加載大內存中的模塊對象增長了一次引用,不會從新執行模塊內的語句),以下python

test.py
import spam
import spam
import spam

'''
打印結果爲
from the spam.py
'''

3.1.2 每一個模塊都是一個獨立的名稱空間,定義在這個模塊中的函數,把這個模塊的名稱空間當作全局名稱空間,這樣咱們在編寫本身的模塊時,就不用擔憂咱們定義在本身模塊中全局變量會在被導入時,與使用者的全局變量衝突

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
import spam
money=10
print(spam.money)   #這個是調用的spam下的money變量

def read1():
    print("=======")

spam.read1()

money=1
spam.change()
print(money)   #這個結果是本地的money


執行結果爲
from the spam.py
10000
spam->read1->money 10000
1

3.1.3 總結:首次導入模塊spam時會作三件事:

一、爲源文件(spam模塊)建立新的名稱空間,在spam中定義的函數和方法如果使用到了global時訪問的就是這個名稱空間。
二、在新建立的命名空間中執行模塊中包含的代碼,見初始導入import spam
三、建立名字spam來引用該命名空間mysql

這個名字和變量名沒什麼區別,都是‘第一類的’,且使用spam.名字的方式能夠訪問spam.py文件中定義的名字,spam.名字與test.py中的名字來自兩個徹底不一樣的地方。

3.1.4 爲模塊名起別名,至關於m1=1;m2=m1

import spam as sp
sp.read1()
print(sp.money)

執行結果爲:
from the spam.py
spam->read1->money 10000
10000

3.2 from....import

3.2.1 對比import spam,會將源文件的名稱空間'spam'帶到當前名稱空間中,使用時必須是spam.名字的方式而from 語句至關於import,也會建立新的名稱空間,可是將spam中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就能夠了

from spam import read1,read2,change,money #這樣在當前位置直接使用read1和read2就行了,執行時,仍然以spam.py文件全局名稱空間
change()   
read2()

若是當前有重名read1或者read2,那麼會有覆蓋效果。sql

from spam import read1
def read1():
    print('==========')
read1()

打印結果爲:
from the spam.py
==========

3.2.2 from spam import * 把spam中全部的不是如下劃線(_)開頭的名字都導入到當前位置,大部分狀況下咱們的python程序不該該使用這種導入方式,由於*你不知道你導入什麼名字,頗有可能會覆蓋掉你以前已經定義的名字。並且可讀性極其的差,在交互式環境中導入時沒有問題。

from spam import *   ##將模塊spam中全部的名字都導入到當前名稱空間
print(money)
print(read1)
print(read2)
print(change)

打印結果爲:
from the spam.py
10000
<function read1 at 0x000000000089CC80>
<function read2 at 0x000000000089CBF8>
<function change at 0x000000000089CD08>

能夠是使用__all__來控制*api

__all__=['read1','money']  #再使用*導入時,只能有這兩個方法能夠調用
print('from the spam.py')

money=10000

def read1():
    print('spam->read1->money',money)

def read2():
    print('spam->read2')
    read1()

def change():
    global money
    money=0

if __name__ == '__main__':
    print("文件被當作腳本執行")

3.2.3 考慮到性能的緣由,每一個模塊只被導入一次,放入字典sys.module中,若是你改變了模塊的內容,你必須重啓程序,python不支持從新加載或卸載以前導入的模塊。

3.4 模塊的搜索路徑

模塊的查找順序是:內存中已經加載的模塊->內置模塊->sys.path路徑中包含的模塊

2、包

包是一種經過使用‘.模塊名’來組織python模塊名稱空間的方式。不管是import形式仍是from...import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提升警覺:這是關於包纔有的導入語法,
包的本質就是一個包含__init__.py文件的目錄。
包A和包B下有同名模塊也不會衝突,如A.a與B.a來自倆個命名空間
glance/                   #Top-level package

├── __init__.py      #Initialize the glance package

├── api                  #Subpackage for api

│   ├── __init__.py

│   ├── policy.py

│   └── versions.py

├── cmd                #Subpackage for cmd

│   ├── __init__.py

│   └── manage.py

└── db                  #Subpackage for db

    ├── __init__.py

    └── models.py
複製代碼
#文件內容

#policy.py
def get():
    print('from policy.py')

#versions.py
def create_resource(conf):
    print('from version.py: ',conf)

#manage.py
def main():
    print('from manage.py')

#models.py
def register_models(engine):
    print('from models.py: ',engine)

2.1 注意事項

1.關於包相關的導入語句也分爲import和from ... import ...兩種,可是不管哪一種,不管在什麼位置,在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,不然非法。能夠帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。函數

2.對於導入後,在使用時就沒有這種限制了,點的左邊能夠是包,模塊,函數,類(它們均可以用點的方式調用本身的屬性)。性能

3.對比import item 和from item import name的應用場景:
若是咱們想直接使用name那必須使用後者。測試

2.2 import

在包glance文件中測試優化

import glance.db.models
glance.db.models.register_models('mysql')

打印結果爲:
from models.py:  mysql

2.3 from...import

須要注意的是from後import導入的模塊,必須是明確的一個不能帶點,不然會有語法錯誤,如:from a import b.c是錯誤語法spa

咱們在與包glance同級別的文件中測試code

from glance.db import models   #把文化做爲模塊調用
models.register_models('mysql')
 
from glance.db.models import register_models   #模塊的方法調用過來
register_models('mysql')

from models.py:  mysql
from models.py:  mysql

init.py文件

不論是哪一種方式,只要是第一次導入包或者是包的任何其餘部分,都會依次執行包下的__init__.py文件,這個文件能夠爲空,可是也能夠存放一些初始化包的代碼。

2.5 from glance.api import
在講模塊時,咱們已經討論過了從一個模塊內導入全部
,此處咱們研究從一個包導入全部*。

此處是想從包api中導入全部,實際上該語句只會導入包api下__init.py文件中定義的名字,咱們能夠在這個文件中定義_all:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
#在api  的__init__.py中定義
x=10

def func():
    print('from api.__init.py')

__all__=['x','func','policy']

此時咱們在於glance同級的文件中執行from glance.api import *就導入__all__中的內容(versions仍然不能導入)。

from glance.api import * #這個*表明的是glance.api下的__init__.py文件中的__all__中定義的方法
print(x)    #init中定義的x變量
func()      #init中定義的func函數
policy.get() #policy裏的get函數

打印結果爲:
10
from api.__init.py
from policy.py

2.6絕對導入和相對導入

最頂級包glance是寫給別人用的,而後在glance包內部也會有彼此之間互相導入的需求,這時候就有絕對導入和相對導入兩種方式:

絕對導入:以glance做爲起始

相對導入:用.或者..的方式最爲起始(只能在一個包中使用,不能用於不一樣目錄內)

例如:咱們在glance/api/version.py中想要導入glance/cmd/manage.py

from glance.cmd import manage  #絕對導入
manage.main()

from ..cmd import manage       #至關導入
manage.main()

特別須要注意的是:能夠用import導入內置或者第三方模塊,可是要絕對避免使用import來導入自定義包的子模塊,應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。

2.7單獨導入包

單獨導入包名稱時不會導入包中全部包含的全部子模塊,如

#在與glance同級的test.py中
import glance
glance.cmd.manage.main()

'''
執行結果:
AttributeError: module 'glance' has no attribute 'cmd'

'''

解決方法:

1 #glance/__init__.py
2 from . import cmd
3 
4 #glance/cmd/__init__.py
5 from . import manage
#glance的同級目錄下的test.py
import glance
glance.cmd.manage.main()
相關文章
相關標籤/搜索