Python 模塊和包


Python 模塊和包

image

1. 模塊簡介

1.1 什么是模塊

編寫較長程序時,建議用文本編輯器代替解釋器,執行文件中的輸入內容,這就是編寫 腳本 。隨着程序越來越長,為了方便維護,最好把腳本拆分成多個文件。編寫腳本還一個好處,不同程序調用同一個函數時,不用每次把函數復制到各個程序。為實現這些需求,Python 把各種定義存入一個文件,在腳本或解釋器的交互式實例中使用。這個文件就是 模塊

模塊即是一系列功能的結合體。

1.2 為什么使用模塊

提高了代碼的可維護性,不用重復造輪子提升開發效率.

1.3 模塊的分類

  1. 內置模塊(使用python解釋器直接可以導入)
  2. 第三方模塊(使用pip安裝)
  3. 自定義

1.4 模塊的表現形式

  • 使用python編寫的代碼(.py文件),就是平時寫的一個python文件

  • 已被編譯為共享庫或DLL的C或C++擴展

  • 包好一組模塊的包(文件夾)

    包其實就是多個py文件(模塊)的集合

    包里面通常會含有一個__init__.py文件(在python3中這個文件可以沒有)

  • 使用C編寫並鏈接到python解釋器的內置模塊

2. import句式

導入模塊使用關鍵字importpy文件名,不要加.py.

示例:

# 導入內置模塊
>>> import time
>>> time.time()  # 直接使用
1637651203.9467623
#導入自定義
# 代碼文件:foo.py 
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

# 導入    
>>> import foo
>>> foo.
foo.hello(  foo.name    
>>> foo.name
'Hans'
>>> foo.hello(foo.name)
Hello, Hans
>>> foo.hello("Jack")  
Hello, Jack
          
# 同一個模塊多次導入
# 代碼文件:boo.py
print("hello")

# 導入        
>>> import boo  # 第一次導入,會執行里面的代碼。
hello
>>> import boo		# 第二次導入,不會執行
>>> import boo		# 第二次導入,不會執行
>>> import boo		# 第二次導入,不會執行
# 多次導入相同模塊 只會執行一次

模塊首次導入發生了什么?(以導入boo.py中導入foo.py為例)

# foo.py
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

# boo.py 
import foo
print("hello world")

foo.name
foo.hello(foo.name)
foo.hello("Jack")

# 執行結果:
hello world
Hello, Hans
Hello, Jack
  1. 運行導入文件(boo.py)產生該文件的全局名稱空間
  2. 運行foo.py
  3. 產生foo.py全局名稱空間 運行foo.py文件內代碼 將產生的名字全部存檔於foo.py名稱空間
  4. 在導入文件名稱空間產生一個foo的名字指向foo.py全局名稱空間

image

import方法導入模塊后就可以使用模塊中的所有的變量名或函數名,而且絕對不會沖突,因為調用的時候已經指定了要使用哪個包中的那個變量或函數

3. from...import...句式

from...import...句式為從哪個包或模塊中導入哪個模塊或功能。

示例:

# foo.py代碼:
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

def hi():
    print("Hi, world")
    
# boo.py代碼:    
from foo import hi  #在boo中只使用foo的hi功能
print("hello world")

hi()

# 執行結果:
hello world
Hi, world

# 代碼 boo.py 
from foo import hi
from foo import hi
from foo import hi
執行結果:
from foo
# from...import...多次導入也只會導入一次

使用from...import...導入:

  1. 先產生執行文件的全局名稱空間
  2. 執行模塊文件 產生模塊的全局名稱空間
  3. 將模塊中執行之后產生的名字全部存檔於模塊名稱空間中
  4. 在執行文件中有一個hi執行模塊名稱空間中hi指向的值

導入

# foo.py 代碼
print("from foo")
name = 'Hans'

def hello(name):
    print("Hello, %s" % name)

def hi():
    print("Hi, world")

# boo.py 代碼
    
from foo import hi
print("hello world")

def hi():
    print("from boo hi")

hi()
# 執行結果:
from foo
hello world
from boo hi   # 發現執行hi()的結果為boo.py中的函數不是從foo.py中導入進來的hi

from...import...指定的導入某個名字

在使用的時候直接寫名字即可 但是當前名稱空間有相同名字的時候,就會產生沖突 使用的就變成了當前名稱空間

4. 導入方式的擴展

4.1 使用別名

# import導入
>>> import foo as f  # 把foo定義別名為f,這時只能調用f,如果再調用foo就會報錯,說foo沒有定義
>>> f.name
'Hans'

# from ... import ...導入
>>> from foo import hi as h		# 把hi定義為別名為h,調用的時候直接使用h即可,同理hi也不能使用
>>> h()
Hi, world

4.2 連續導入

# import導入
>>> import sys
>>> import os
# 上面的導入方式可以寫成下面:
>>> import sys, os  # 這種方式和上面的方式功能是一樣的

# from ... import ...導入
>>> from foo import hello
>>> from foo import hi
# 上面的導入方式可以寫成下面:
>>> from foo import hello, hi  # 這種方式和上面的方式功能是一樣的

import使用連續導入多個模塊時,如果多個模塊功能相似或者屬於同一個系列時推薦使用。

如果功能不同並且不屬於一個系列 那么推薦分行導入

4.3 通用導入

如果使用from ... import ...方式導入一個模塊里全部功能時,最基本的方法是依次導入
>>> from foo import hello, hi, name 
# 或
>>> from foo import hello
>>> from foo import hi
>>> from foo import name 

#可以使用* 號把一個模塊里的全部功能都導入
>>> from foo import * 


# 如果一個模塊里有三個功能,在使用from ... import ... 想讓人用其中兩個可以使用__all__
# 代碼:
print("from foo")
name = 'Hans'

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

__all__ = ['hi', 'play']  # 在被導入的模塊文件中可以使用__all__指定可以被導入使用的名字

# 執行:
>>> from foo import *
from foo
>>> hi()
from foo Hi
>>> play()
from foo play
>>> hello("Hans")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'hello' is not define

4.4 判斷py文件是作為模塊文件還是執行文件

可以使用函數自帶的__name__方法

# foo.py 代碼 
print("from foo")
name = 'Hans'

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

print("__name__: %s" % __name__)

# 執行如果:
from foo
__name__: __main__
# 如果foo.py是直接執行時。__name__為 __main__  

# 如果foo.py 當成模塊在別的文件里導入時:
# importTest.py  代碼
import foo

#執行結果:
from foo
__name__: foo   
# 如果foo.py文件是被當做模塊導入則返回模塊名

# 
# 一個py文件當成模塊被導入時,它會直接執行py文件里的全部代碼,可以利用__name__來判斷它是否被當成模塊導入,如果是則不執行
# 代碼 foo.py
def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

if __name__ == '__main__':
    print("from foo")
    name = 'Hans'
# 代碼 importTest.py 
import foo
# 執行結果:

#之前導入的時候會直接打印: from foo

5. 模塊導入的順序

模塊導入的順序:

  1. 先從內存中查找
  2. 再去內置模塊中查找
  3. 最后去sys.path系統路徑查找(自定義模塊)

如果都沒有查找到則報錯

# 1.在內存中查找
# foo.py 代碼:

def hello(name):
    print("from foo. Hello, %s" % name)

def hi():
    print("from foo Hi")

def play():
    print("from foo play")

if __name__ == '__main__':
    print("from foo")
    name = 'Hans'
# importTest.py 代碼:

from foo import hello
import time 
print("Hello")
time.sleep(10)
hello("time")

# 執行結果:
Hello
				#在time.sleep(10)的時候把foo.py刪除,這時foo.py已經加載到內存中,所以下面依然執行
from foo. Hello, time

#如果再執行則會報錯

# 2.再去內置模塊中查找
# 可以自己定義一個和內置模塊同名的模塊,看看導入的是誰

# 自己編寫:time.py 代碼:
print("time...")

# boo.py 代碼: 
import time
print(time)

# 執行結果:   
<module 'time' (built-in)>
# 發現time為內置的模塊,所以在給py文件命名的時候不要與內置模塊名沖突

# sys.path系統路徑查找
>>> import sys
>>> sys.path  
['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages/cloud_init-17.1-py3.6.egg', '/usr/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages']

# ''為當前目錄,然后依次查找

當某個自定義模塊查找不到的時候解決方案:

  1. 自己手動將該模塊所在的路徑添加到sys.path中

    # 查看當前目錄
    [root@hans_tencent_centos82 tmp]# pwd
    /tmp
    [root@hans_tencent_centos82 tmp]# python3
    >>> import foo
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ModuleNotFoundError: No module named 'foo'
    # 提示沒有foo模塊。
    # 查找foo.py模塊在哪
    [root@hans_tencent_centos82 module]# pwd
    /tmp/module
    [root@hans_tencent_centos82 module]# ls -lrt foo.py 
    -rw-r--r-- 1 root root 202 Nov 23 16:54 foo.py
    # foo.py在/tmp/module目錄下。
    # /tmp/module加入到sys.path
    >>> import sys
    >>> sys.path.append('/tmp/module')
    >>> import foo
    >>> sys.path
    ['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/local/lib64/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages/cloud_init-17.1-py3.6.egg', '/usr/lib/python3.6/site-packages', '/usr/lib64/python3.6/site-packages', '/tmp/module']
    # 可以看到 '/tmp/module'添加到sys.path
    
  2. 使用from...import...句式

    from 文件夾名稱.文件夾名稱 import 模塊名
    from 文件夾名稱.模塊名稱 import 名字

    # from 文件夾名稱.文件夾名稱 import 模塊名
    
    #  foo.py在/tmp/module目錄下。
    # 當前目錄為/tmp
    # 使用from...import...
    # 執行結果:
    >>> from module import foo
    >>> foo.hi()
    from foo Hi
    
    #當前在/tmp下,而foo.py在/tmp/module/test/下
    [root@hans_tencent_centos82 tmp]# ls -lrt /tmp/module/test/foo.py 
    -rw-r--r-- 1 root root 202 Nov 23 16:54 /tmp/module/test/foo.py
    >>> from module.test import foo
    >>> foo.play()
    from foo play
    
    # from 文件夾名稱.模塊名稱 import 名字
    
    #只導入foo模塊中的一個功能:
    >>> from module.test.foo import play
    >>> play()
    from foo play
    

6. 絕對導入與相對導入

  • 絕對導入

    始終按照執行文件所在的sys.path查找模塊,為了防止報錯路徑加到sys.path路徑下。

    import os
    import sys 
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))  # 使用os.path.dirname取得項目根目錄的絕對路徑,把項目根目錄加入到sys.path,其中os.path.dirname具體嵌套幾層要看執行文件的位置。
    from src import test1
    
    # os.path.abspath(__file__)  執行文件的絕對路徑,如:/tmp/module/test/bin/run.py
    # os.path.dirname 返回文件路徑:/tmp/module/test/bin
    
  • 相對導入

    句點符(.)
    .表示當前文件路徑
    ..表示上一層文件路徑

    # 目錄結構,bin目錄下為執行文件, src為要調用的相關包
    [root@hans_tencent_centos82 test]# ls -lrt
    drwxr-xr-x 2 root root 4096 Nov 24 15:28 bin
    drwxr-xr-x 3 root root 4096 Nov 24 15:29 src
    
    [root@hans_tencent_centos82 test]# ls -lrt bin/
    -rw-r--r-- 1 root root 104 Nov 24 15:28 run.py
        
    #  run.py
    import os
    import sys 
    sys.path.append(os.path.dirname(os.getcwd()))
    from src import test1
    test1.foo1()
    
    [root@hans_tencent_centos82 test]# ls -lrt src/
    -rw-r--r-- 1 root root   42 Nov 24 15:24 test2.py
    -rw-r--r-- 1 root root   79 Nov 24 15:29 test1.py
       
    # test1.py
    import test2
    def foo1():
        print("from test1.foo1")
        test2.foo2()
        
    # test2.py  
    def foo2():
        print("from test2.foo2")
        
    #執行結果
    [root@hans_tencent_centos82 bin]# python3 run.py 
    Traceback (most recent call last):
      File "run.py", line 4, in <module>
        from src import test1
      File "/tmp/module/test/src/test1.py", line 2, in <module>
        import test2
    ModuleNotFoundError: No module named 'test2'
    # 會報找不到test2
    # 可以使用相對導入解決
    # 修改test1.py的代碼:
    from . import test2 
    def foo1():
        print("from test1.foo1")
        test2.foo2()
    
    #執行結果
    [root@hans_tencent_centos82 bin]# python3 run.py 
    from test1.foo1
    from test2.foo2
    
    # 現在又有一個新問題,就是如果直接執行test1.py的時候,它依然會報錯:
    # test1.py 代碼:
    from . import test2 
    def foo1():
        print("from test1.foo1")
        test2.foo2()
    foo1()
    
    # 執行結果:
    [root@hans_tencent_centos82 src]# python3 test1.py   
    Traceback (most recent call last):
      File "test1.py", line 1, in <module>
        from . import test2 
    ImportError: cannot import name 'test2'
    # 相對導入只能用在模塊文件中,不能在執行文件中使用
    

    注意:相對導入只能用在模塊文件中,不能在執行文件中使用

    推薦使用絕對導入,這樣不管項目在哪里執行都可以執行。

7. 循環導入

不允許出現循環導入

真要出現了,一般解決方法(就是明知道有循環導入了還是讓它運行,一錯再錯方法):

  1. 調換順序
    將彼此導入的句式放在代碼的最后
  2. 函數形式
    將導入的句式放入函數體代碼 等待所有的名字加載完畢之后再調用

8.模塊導入總結:

在程序中涉及到多個文件之間導入模塊的情況,一律按照執行文件所在的路徑為准

推薦使用絕對導入,這樣不管項目在哪里執行都可以執行。

9. 常用模塊

9.1 collections 模塊

這個模塊實現了特定目標的容器,以提供Python標准內建容器 dict , list , set , 和 tuple 的替代選擇

# namedtuple 命名元組(相當於給元組起個名字)
#  namedtuple('名稱',[名字1,名字2,...])
#  namedtuple('名稱','名字1 名字2 ...')
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(10, 20)
>>> p
Point(x=10, y=20)
>>> p.x
10
>>> p.y
20
>>> p.x +  p.y
30

>>> Point = namedtuple('Point', 'x, y, z')  
>>> p = namedtuple('Point', 'x, y, z')     
>>> Point = namedtuple('Point', 'x, y, z')
>>> p = Point(1, 2, 3)
>>> p
Point(x=1, y=2, z=3)

9.1.1 OrderedDict

字典是無序的,使用OrderedDict可以保存了他們被添加的順序

>>> from collections import OrderedDict
>>> dict1 = OrderedDict([("name","Hans"), ("age",18)])
>>> dict1
OrderedDict([('name', 'Hans'), ('age', 18)])
>>> dict1['hobby'] = "read"
>>> dict1
OrderedDict([('name', 'Hans'), ('age', 18), ('hobby', 'read')])
>>> 
9.1.2 defaultdict

為字典查詢提供一個默認值

>>> from collections import defaultdict
>>> list_num = [1, 2, 3, 4, 5, 6, 7, 8]
>>> myDict = defaultdict(list)    
>>> myDict['a']=list_num
>>> myDict
defaultdict(<class 'list'>, {'a': [1, 2, 3, 4, 5, 6, 7, 8]})

>>> for i in list_num:           
...     if i > 6:                
...         myDict['a'].append(i)
...     else:
...         myDict['b'].append(i)
... 
>>> myDict
defaultdict(<class 'list'>, {'b': [1, 2, 3, 4, 5, 6], 'a': [7, 8]}
9.1.3 Counter

提供了計數功能

# 統計字符串每個字符出現的次數
>>> from collections import Counter
>>> str1 = 'ABCDDABCEDFHIJMOOP'
>>> res = Counter(str1)
>>> res
Counter({'D': 3, 'A': 2, 'B': 2, 'C': 2, 'O': 2, 'E': 1, 'F': 1, 'H': 1, 'I': 1, 'J': 1, 'M': 1, 'P': 1})
9.1.4 deque

雙端隊列,兩端快速添加(append)和彈出(pop)

# 隊列: queue 先進先出:FIFO
>>> import queue
>>> q = queue.Queue()   # 初始化隊列,Queue()說明沒有元素個數限制
>>> q.put(1)   # 向隊列加入元素
>>> q.put(2)
>>> q.put(3)
>>> q.put(4)
>>> q.put(5)
>>> q.get()  # 從隊列取元素
1
>>> q.get()
2
>>> q.get()
3
>>> q.get()
4
>>> q.get()
5
>>> q.get()  # 從隊列取元素如果沒有,則會阻塞

# 雙端隊列 deque
>>> from collections import deque
>>> dq = deque([1, 2, 3, 4, 5])  # 初始化隊列
>>> dq
deque([1, 2, 3, 4, 5])
>>> dq.append(6)  # 從右邊追加元素
>>> dq
deque([1, 2, 3, 4, 5, 6])
>>> dq.appendleft(0)  # 從左邊追加元素
>>> dq
deque([0, 1, 2, 3, 4, 5, 6])

>>> dq.pop()  # 從右邊彈出元素
6
>>> dq.popleft() # 從左邊彈出元素
0

collections詳細說明請看官方文檔:

collections 模塊

9.2 time模塊

時間的訪問和轉換.

時間三種表現形式

  1. 時間戳(秒數)
  2. 結構化時間(一般是給機器看的)
  3. 格式化時間(一般是給人看的)

三種時間是可以相互轉換的

python-time時間模塊_元組
>>> import time
>>> time.time()  # 時間戳(自1970年1月1日0時0分0秒到現在的秒數)
1606304157.1071541
# 格式化時間
>>> time.strftime('%Y-%m-%d')  # 當前日期
'2020-11-25'
>>> time.strftime('%Y-%m-%d %H:%M:%S')   # 當前日期和時間 
'2020-11-25 18:57:49'
>>> time.gmtime()    # UTC時間
time.struct_time(tm_year=2020, tm_mon=11, tm_mday=25, tm_hour=11, tm_min=0, tm_sec=54, tm_wday=3, tm_yday=329, tm_isdst=0)
>>> 
>>> time.strftime('%Y-%m-%d-%H-%M-%S', time.gmtime())  # 格式化時間
'2020-11-25-11-03-20'

>>> time.localtime()  # 當地時間
time.struct_time(tm_year=2020, tm_mon=11, tm_mday=25, tm_hour=19, tm_min=1, tm_sec=3, tm_wday=3, tm_yday=329, tm_isdst=0
>>> time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime()) # 格式化時間
'2020-11-25-19-03-55'
                 
# 把時間戳轉成可識別時間
# 先轉成time.localtime()
# 再使用time.strftime()轉成可識別時間
>>> time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time()))
'2020-11-25-19-07-50'
                 
# 轉格式化時間轉成趕時間戳
>>> currteTime = time.strftime('%Y%m%d%H%M%S') # 當前時間'20201125193557'
>>> strut_currte = time.strptime(currteTime,'%Y%m%d%H%M%S')  # 轉成結構化時間 time.struct_time(tm_year=2020, tm_mon=11, tm_mday=25, tm_hour=19, tm_min=35, tm_sec=57, tm_wday=2, tm_yday=330, tm_isdst=-1)
>>> time.mktime(strut_currte) # 轉成時間戳
1606304157.0           

time詳細說明請看官方文檔:

time

9.3 datetime模塊

提供用於處理日期和時間

>>> import datetime
>>> datetime.date.today()  # 當天日期
datetime.date(2020, 11, 25)
>>> print(datetime.date.today())
2020-11-25

>>> datetime.datetime.today() # 當天日期時間
datetime.datetime(2020, 11, 25, 19, 43, 42, 985477)
>>> print(datetime.datetime.today())
2020-11-25 19:45:04.465346
>>> res  = datetime.datetime.today()
>>> res.year # 年
2020
>>> res.month # 月
11
>>> res.day  # 日
25

# 時間差timedelta
>>> currTime = datetime.datetime.today()  
>>> time_delta = datetime.timedelta(days=2)   
>>> currTime
datetime.datetime(2020, 11, 25, 19, 49, 7, 204322)
>>> time_delta
datetime.timedelta(2)
>>> currTime - time_delta
datetime.datetime(2020, 11, 23, 19, 49, 7, 204322)
>>> currTime + time_delta
datetime.datetime(2020, 11, 27, 19, 49, 7, 204322)

datetime詳細說明請看官方文檔:

datetime

9.4 random 模塊

該模塊實現了各種分布的偽隨機數生成器。

>>> import random
>>> random.random()    # 它在半開放區間 [0.0,1.0) 內均勻生成隨機浮點數
0.251295850082761
>>> random.random()
0.11710038974330583

>>> random.randint(1, 10)  # 隨機生成1到10之間的整數(包含1和10)
9
>>> random.randint(1, 10)
3
>>> random.uniform(1,5) # 隨機生成小數
2.902251945296318

>>> random.choice('abc')  # 隨機在給的內容內返回一個元素
'b'
>>> random.choice([1, 2, 3])
1
>>> random.sample('abcdef', 3)  # 隨機返回指定的個數, 並組織成列表形式, 用於無重復的隨機抽樣
['b', 'e', 'c']
>>> random.sample(['a', 'b', 1, 2,'c'], 4)
['c', 'a', 'b', 2]
>>> list_num =[1, 2, 3, 4, 5, 6, 7, 8, 9]      
>>> random.shuffle(list_num) # 打亂容器元素的順序
>>> list_num
[6, 1, 5, 7, 9, 4, 3, 2, 8]

# 練習隨機生成驗證碼(包含大小寫字母和數字)
import random

def random_code(n):
    code=''
    for i in range(n):
        randomInt = str(random.randint(0,9))
        randomUpper = chr(random.randint(65,90))
        randomLower = chr(random.randint(97,122))
        res = random.choice([randomInt,randomUpper,randomLower])
        code += res

    return code

num = input("想生成幾位驗證碼:").strip()
if num.isdigit:
    num = int(num)
    res = random_code(num)
    print(res)
# 執行結果:
[root@hans_tencent_centos82 tmp]# python3 d.py
想生成幾位驗證碼:3
2xV
[root@hans_tencent_centos82 tmp]# python3 d.py
想生成幾位驗證碼:5
lyF3k
[root@hans_tencent_centos82 tmp]# python3 d.py
想生成幾位驗證碼:8
6pJ41AA4

random詳細說明請看官方文檔:

random

9.5 os 模塊

該模塊提供了一種使用與操作系統相關的功能的便捷式途徑(主要和操作系統 相關)。

>>> os.mkdir('test')  # 創建空目錄,如果目錄存在會報錯FileExistsError,而且不能聯級創建目錄
>>> os.makedirs('test/a')  # 聯級創建目錄空目錄,如果目錄會報錯FileExistsError
>>> os.mkdir('test')  # 刪除空目錄,必須是空目錄,里面不能有任何文件
>>> os.removedirs('test/a')  # 遞歸刪除目錄,必須是空目錄
>>> os.getcwd()  # 獲取當前所在絕對路徑,類似pwd
'/tmp'
>>> os.path.join(P, "module")  # 路徑拼接
'/tmp/module'
>>> os.listdir("/tmp")  # 顯示指定路徑下的文件名稱(列表的形式)
['m.py', 'c.py', 'v.py', 'x.py', 'stargate.lock', 'o.py', 'n.py', 'j.py', 'g.py', 'r.py', 's.py', 'systemd-private-483254a5abf24463b5ea620ec843438e-chronyd.service-MQAQof', 'l.py', 'p.py', 'module', 'web.txt', 'k.py', 'b.py', 'z.py', 'q.py', 'y.py', 't.py', 'a.py', 'd.py', 'u.py']

>>> os.remove('web.txt') # 刪除一個文件

>>> os.getcwd()
'/tmp'
>>> os.chdir('/etc')  # 切換目錄,類似cd
>>> os.getcwd()     
'/etc'

>>> os.path.exists('/tmp')  # 查看路徑是否存在,存在返回True,不存在返回False
True
>>> os.path.exists('/tmp/web.txt')
False

>>> os.path.isfile('/tmp/a.py')   # 判斷是否是個文件,是返回True,不是返回False
True
>>> os.path.isfile('/tmp/module')   
False

>>> os.path.isdir('/tmp/module')  # 判斷是否是個目錄,是返回True,不是返回False   
True
>>> os.path.isdir('/tmp/a.py')  
False

>>> os.path.getsize('t.py')  # 查看文件的大小,以字節為單位
813

>>> os.system("ls") # 調用系統命令

os詳細說明請看官方文檔:

os

9.6 sys 模塊

該模塊提供了一些變量和函數。這些變量可能被解釋器使用,也可能由解釋器提供。這些函數會影響解釋器(和python解釋器相關)。

>>> import sys
>>> sys.path # 打印python查找路徑
>>> sys.version # 打印python解釋器版本
'3.6.8 (default, Mar 19 2021, 05:13:41) \n[GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]'
>>> sys.platform # 打印操作系統平台,win為win32,linux為linux,mac為darwin
'linux'

# sys.argv # 傳遞給 Python 腳本的命令行參數
import sys
print(sys.argv)
[root@hans_tencent_centos82 ~]# python3 /tmp/d.py  # 如果不跟任何參數,則返回執行腳本路徑,argv[0] 為腳本的名稱
['/tmp/d.py']
[root@hans_tencent_centos82 ~]#python3  /tmp/d.py 1 2 3
['/tmp/d.py', '1', '2', '3']
# sys.argv 把傳遞的參數放到一個列表中,索引值0為腳本名稱,1為第一個參數

# 寫一個腳本只能接受兩個參數:
import sys

num = sys.argv

if len(num)-1 == 2:
    print("第一個參數是:%s" % sys.argv[1])
    print("第二個參數是:%s" % sys.argv[2])
else:
    print("必須傳兩個值")
# 執行結果:
[root@hans_tencent_centos82 ~]# python3  /tmp/sysargv.py  # 不傳值
必須傳兩個值
[root@hans_tencent_centos82 ~]# python3  /tmp/sysargv.py  a b # 傳兩個值
第一個參數是:a
第二個參數是:b
[root@hans_tencent_centos82 ~]# python3  /tmp/sysargv.py  a b c # 傳三個值
必須傳兩個值

sys詳細說明請看官方文檔:

sys

9.7 subprocess模塊

子進程管理,該模塊允許你生成新的進程,連接它們的輸入、輸出、錯誤管道,並且獲取它們的返回碼。

此模塊打算代替一些老舊的模塊與功能:

os.system和os.spawn*模塊

res = subprocess.Popen('dir',
                       shell=True,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE
                       )
print('stdout',res.stdout.read().decode('gbk'))  # 獲取正確命令執行之后的結果
print('stderr',res.stderr.read().decode('gbk'))  # 獲取錯誤命令執行之后的結果
#加decode('gbk')是為了windows解析出中文,否則為unicode編碼。

# 可以使用subprocess封成函數,可以執行任何系統命令
def exec_cmd(cmd):
    CMD = subprocess.Popen(cmd,
                     shell=True,
                     stdin=subprocess.PIPE,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.PIPE)
    stdout, stderr = CMD.communicate()

    if CMD.returncode != 0:
        return CMD.returncode, stderr
    return CMD.returncode, stdout
#執行結果
res = exec_cmd("ls")
>>> res
(0, b'a.html\na.py\nboo.py\nhouses.py\nhouses.txt\nhouses_v2.py\nimportTest.py\n__pycache__\ntest\ntime.py\n')
# 0 為命令執行的返回碼,為0說明命令執行成功。
>>> res  = exec_cmd("ls aaa.py") # 查看一個不存在的文件
>>> res
(2, b"ls: cannot access 'aaa.py': No such file or directory\n")

subprocess詳細說明請看官方文檔:

subprocess

9.8 序列化json

json主要用於和別的語言傳輸數據時編碼和解碼(前后端分離)。

json兩個操作:序列化和反序列化

>>> import json
>>> dict1 = {'name':'Hans', 'age':18, 'gender':'M'}  # 定義一個字典
>>> type(dict1)
<class 'dict'>
>>> res = json.dumps(dict1)  # 使用json.dumps方法把這個字典序列化成一個json字符串。
>>> type(res)   # 類型變成了字符串
<class 'str'>
>>> res
'{"name": "Hans", "age": 18, "gender": "M"}'
# 這時res變成了一個字符串,所以不能用字典的取值方式, 這個變成json字符串的過程就叫序列化

# 把json字符串格式轉換python可以識別的字典
>>> new_dict1 = json.loads(res)   # 使用json.loads方法把這個json字符串轉成字典。
>>> type(new_dict1)
<class 'dict'>
>>> new_dict1
{'name': 'Hans', 'age': 18, 'gender': 'M'}
>>> new_dict1['name']
'Hans'
# 這時new_dict1為一個字典,這個從json字符串變成python可識別的類型過程叫作反序列


# 通過json序列化到文件
>>> dict1 = {'name': 'Hans', 'age': 18, 'gender': 'M'}
>>> with open('json.txt', 'w', encoding='utf8') as f:  
...     json.dump(dict1, f)  # 把dict1這個字典轉成json字符串后寫入文件
# 查看結果:
[root@hans_tencent_centos82 module]# cat json.txt 
{"name": "Hans", "age": 18, "gender": "M"}

# 從文件反序列化:
>>> with open('json.txt', 'r', encoding='utf8') as f:
...     nes_dict1 = json.load(f)
... 
>>> 
>>> 
>>> nes_dict1
{'name': 'Hans', 'age': 18, 'gender': 'M'}
>>> type(nes_dict1)
<class 'dict'>
>>> nes_dict1['age']
18


# 不使用ASCII轉換
>>> nDict1 = {'username': '你好', 'age': 123}                                             
>>> json.dumps(nDict1)   # 默認ensure_ascii=True,會把nDict1元素都轉為ascii碼
'{"username": "\\u4f60\\u597d", "age": 123}'
>>> 
>>> json.dumps(nDict1,ensure_ascii=False)  # 把ensure_ascii=False,則不轉換
'{"username": "你好", "age": 123}'
>>> 

可轉成json 的數據類型:

Python JSON
dict object -- 對象
list, tuple array
str string
int, float, int 和 float 派生的枚舉 number
True true
False false
None null

json詳細說明請看官方文檔:

json

9.9 hashlib 模塊

這個模塊針對許多不同的安全哈希和消息摘要算法實現了一個通用接口,包括 FIPS 安全哈希算法 SHA1, SHA224, SHA256, SHA384 和 SHA512 (定義於 FIPS 180-2) 以及 RSA 的 MD5 算法。

加密:將明文數據通過一系列算法變成密文數據(目的就是為了數據的安全)

# 1. 基礎使用
>>> import hashlib
>>> md5 = hashlib.md5()   #確定加密算法
>>> md5.update(b'hello') # update只能接受bytes類型數據,否則會報錯
>>> md5.update('hello')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Unicode-objects must be encoded before hashing
>>> md5.hexdigest()  #獲取加密之后的密文數據
'5d41402abc4b2a76b9719d911017c592'

#如果加密內容只有字母或數字,則在前面直接使用b即可。b'123' 或 b'hello'
#如果要對漢字加密,則要編碼轉成bytes類型
>>> md5.update('你好'.encode("utf8"))
>>> md5.hexdigest()
'2cfe361166078c59730c075c966bfe91'

# 2. 只要明文一樣,不管如何傳遞加密結果肯定是一樣的
>>> import hashlib
>>> md5 = hashlib.md5()
>>> md5.update(b'helloworld12345')
>>> md5.hexdigest()
'bc9c17cb5aeecefbaef22b35099369c3'
# 分開加密
>>> import hashlib
>>> md5 = hashlib.md5()
>>> md5.update(b'hello')
>>> md5.update(b'world')
>>> md5.update(b'12345')
>>> md5.hexdigest()
'bc9c17cb5aeecefbaef22b35099369c3'

# 3. 加鹽處理(在對明文數據做加密處理過程前添加一些干擾項)
>>> import hashlib      
>>> md5 = hashlib.md5()
>>> md5.update("干擾信息".encode('utf8'))  # 添加干擾信息(加鹽)
>>> md5.update(b'12345')
>>> md5.hexdigest()
'eff055831c7cff056a4c268b047b10b9'

# 直接對'12345'進行md5加密碼為:'827ccb0eea8a706c4c34a16891f84e7b'

# 4. 動態加鹽
# 當前時間 用戶名的部分 uuid(隨機字符串(永遠不會重復))
>>> import hashlib
>>> import time
>>> md5 = hashlib.md5()
>>> md5.update(str(time.time()).encode('utf8'))  # 使用當前時間時間戳做干擾項
>>> md5.update(b'12345')
>>> md5.hexdigest()
'80a3bdbebec15dbbcc6f32a5e5294a2c'

# 5. 校驗文件一致性
# md5.txt內容
hello world

>>> import hashlib
>>> md5 = hashlib.md5()
>>> with open(r'md5.txt','rb') as f:
...     for i in f:
...         md5.update(i)
... 
>>> md5.hexdigest()
'6f5902ac237024bdd0c176cb93063dc4'

# 修改內容md5.txt
hello world1

>>> import hashlib
>>> md5 = hashlib.md5()
>>> with open(r'md5.txt','rb') as f:
...     for i in f:
...         md5.update(i)
... 
>>> md5.hexdigest()
'490f5b3ce928fa8dce51d8d93f9e4124'
# 只要內容有任何一點變動,則加密的值就會變。

# 文件不是很大的情況下 可以將所有文件內部全部加密處理,驗證一致性
# 但是如果文件特別大 全部加密處理相當消耗資源 如何驗證它的一致性?
# 可以使用切片讀取的方式

import hashlib
import os

md5 = hashlib.md5()

file_size = os.path.getsize('data')

read_size = [0, file_size//4, file_size//2, file_size-10]

with open(r'data', 'rb') as f:
    for i in read_size:
        f.seek(i,0)
        #res=f.read(10)
        md5.update(f.read())

print(md5.hexdigest())
# 執行結果:
6136aa8c5ce7975b784227d82cea8752
# 給文件新增空行
[root@hans_tencent_centos82 module]# echo >>data 
# 執行結果:
52035fa8b25a44507b977fe0545948f3

# 密文越長表示算法越復雜 對應的破解算法的難度越高
# 但是越復雜的算法所需要消耗的資源也就越多 密文越長基於網絡發送需要占據的數據也就越大
# 涉及到用戶密碼存儲,都要求是密文,只要用戶自己知道明文是什么,后台人員看到的都是密文化

hashlib詳細說明請看官方文檔:

hashlib

9.10 logging 模塊

這個模塊為應用與庫實現了靈活的事件日志系統的函數與類

日志級別:

級別 數值
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

logging日志模塊四大組件

logger對象:負責產生日志

filter對象:負責過濾日志(可以忽略)

handler對象:負責日志產生的位置

formatter對象:負責日志的格式

logger要綁定在handler上面,handler要綁定到formatter上面

# 基本使用:(輸出到屏幕)
>>> import logging
>>>stream_handler = logging.StreamHandler()
>>> logging.basicConfig(
...     format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
...     datefmt='%Y-%m-%d %H:%M:%S %p',
...     handlers=[stream_handler,],
...     level=logging.ERROR
... )
>>> logging.error("一條測試信息")
2021-11-29 16:40:54 PM - root - ERROR -<stdin>:  一條測試信息

# 輸入出到文件
>>> import logging
>>> logging.FileHandler(filename='log.log', mode='a', encoding='utf-8')  
>>> logging.basicConfig(
...     format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
...     datefmt='%Y-%m-%d %H:%M:%S %p',
...     handlers=[file_handler,],
...     level=logging.ERROR
... )
>>> logging.error("這是一個調試信息")
# 查看文件log.log
2021-11-29 16:54:18 PM - root - ERROR -<stdin>:  這是一個調試信息
            
# 格式說明:
"""
%(asctime)s # 創建日志的時間
%(name)s   # 日志名, logging.getLogger("測試日志")
%(levelname)s #  文本日志級別
%(levelno)s     #  數字日志記錄級別
%(pathname)s   #  執行文件的絕對路徑
%(filename)s #  執行文件名
%(module)s  # 模塊名
%(funcName)s  #  函數名
%(lineno)d  # 日志調用出的行號
%(created)f # 創建日志的時間,時間戳
%(msecs)d  # 創建時間的毫秒部分
%(relativeCreated)d  # 創建日志記錄的時間,相對於日志模塊加載的時間,通常在應用程序啟動時
%(thread)d   # 線程號
%(threadName)s  # 線程名
%(process)d     # 進程號
%(message)s'  # 具體信息
"""

日志配置文件:
簡單寫日志功能

import logging

logging.basicConfig(
    # 1、日志輸出位置:1、終端 2、文件
    # filename='test.log', # 不指定,默認打印到終端
    handlers=[logging.FileHandler(filename="test.log",encoding='utf-8')],
    # 也可以不寫handlers,直接寫filename='test.log',但是如果遇到中文有可能亂碼

    # 2、日志格式
    format='%(asctime)s - %(name)s - %(levelname)s - - %(filename)s - %(module)s:  %(message)s',

    # 3、時間格式
    datefmt='%Y-%m-%d %H:%M:%S %p',



    # 4、日志級別
    # critical => 50
    # error => 40
    # warning => 30
    # info => 20
    # debug => 10
    level=20,
)

def write_log(info):  # 調用write_log即為記錄日志
    logging.info(info)

write_log("%s" %("hello 你好"))

復雜配置寫日志功能:

import logging
import logging.config

# 設置日志輸出格式

standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]' #其中name為getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

logfile_path = 'a3.log'   # 日志存放的具體路徑
# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {    # 標准輸出格式
            'format': standard_format
        },
        'simple': {  # 簡單輸出格式
            'format': simple_format
        },
    },
    'filters': {},  # 過濾日志
    'handlers': {
        #打印到終端的日志
        'console': {
            'level': 'DEBUG',    #日志級別
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'	# 格式名要和上面formatters設置的相同
        },
        #打印到文件的日志,收集info及以上的日志
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',   # 格式名要和上面formatters設置的相同
            'filename': logfile_path,  # 日志文件
            'maxBytes': 1024*1024*5,  # 日志大小 5M
            'backupCount': 5,   # 5個文件后日志輪詢
            'encoding': 'utf-8',  # 日志文件的編碼,再也不用擔心中文log亂碼了
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置  空字符串作為鍵 能夠兼容所有的日志
        '': {
            'handlers': ['default', 'console'],  # 這里把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },  # 當鍵不存在的情況下 (key設為空字符串)默認都會使用該k:v配置
    },
}

# 使用:
logging.config.dictConfig(LOGGING_DIC)  # 自動加載字典中的配置
logger1 = logging.getLogger('xxx')  # xxx為自己定義的日志
logger1.debug('要輸出的日志信息')

logging詳細說明請看官方文檔

logging

logging.config

logging.handlers

10. 第三方模塊安裝(pip)

使用第三方包要先安裝,一般使用安裝工具為:pip.pip官網

pip要使用先要安裝:

pip --version
pip 9.0.3 from /usr/lib/python3.6/site-packages (python 3.6)
# 顯示版本號為已經安裝。
# 否則沒安裝,手動安裝

pip安裝

pip使用:

pip install 模塊名  #安裝最新版本
pip install 模塊名==版本號 # 指定版本
pip install -U 模塊名 # 升級模塊版本
pip unstall 模塊名   # 卸載模塊
pip download  模塊名  # 下載模塊
pip search 模塊名   # 已被禁用
pip show  模塊名  # 顯示安裝模塊的信息
pip list    # 查看全部安裝的模塊
pip freeze  # 以需求格式輸出已安裝的軟件包(以需求格式即:模塊名==版本號)。
 

[root@hans_tencent_centos82 module]# pip list    # 查看全部安裝的模塊
asn1crypto (0.24.0)
attrs (19.3.0)
[root@hans_tencent_centos82 module]# pip freeze  # 以需求格式輸出已安裝的軟件包。
asn1crypto==0.24.0
attrs==19.3.0
# 使用 pip freeze 導出安裝包到 requirements.txt
# 使用requirements.txt安裝包:
[root@hans_module]# pip freeze >requirements.txt  # 把安裝包導入到requirements.txt
[root@hans_module]# pip install -r requirements.txt  # 安裝requirements.txt文件里的模塊

[root@hans_tencent_centos82 .pip]# pip show zipp
Name: zipp
Version: 0.6.0
Summary: Backport of pathlib-compatible object wrapper for zip files
Home-page: https://github.com/jaraco/zipp
Author: Jason R. Coombs
Author-email: jaraco@jaraco.com
License: UNKNOWN
Location: /usr/local/lib/python3.6/site-packages
Requires: more-itertools

# 修改pip源
# 由於pip源默認為國外的源:
http://mirrors.tencentyun.com/pypi/simple
# 安裝模塊時會有超時,導致安裝失敗,可修改為國內的源
# 國內pip源:
pypi 清華大學源:https://pypi.tuna.tsinghua.edu.cn/simple
pypi 豆瓣源 :http://pypi.douban.com/simple/
pypi 騰訊源:http://mirrors.cloud.tencent.com/pypi/simple
pypi 阿里源:https://mirrors.aliyun.com/pypi
# 臨時修改:
pip install 模塊名 -i https://pypi.tuna.tsinghua.edu.cn/simple

# 永久修改:
# 在登錄用戶下創建
# mkdir ~/.pip  # 創建pip的配置目錄
# touch pip.conf  # 創建pip的配置文件
# 寫入以下內容:
[global]
index-url = http://mirrors.tencentyun.com/pypi/simple
extra-index-url = https://pypi.python.org/simple   # extra-index-url可以寫多個,即增加多個源
				  http://pypi.mirrors.ustc.edu.cn/simple/
[install]
trusted-host = mirrors.tencentyun.com   
               pypi.python.org   
               pypi.mirrors.ustc.edu.cn

11. 包

image-20211130153508160

一般在一個項目中往往需要使用很多的模塊,如果將這些模塊都堆放在一起,勢必不好管理。而且,使用模塊可以有效避免變量名或函數名重名引發的沖突,但是如果模塊名重復怎么辦呢?因此,Python提出了包(Package)的概念。

包就是文件夾,只不過在該文件夾下必須存在一個名為__init__.py 的文件。

__init__.py 這個文件在Python2中是必須的,但在Python3中並不是必須的

每個包的目錄下都必須建立一個 __init__.py的模塊,可以是一個空模塊,可以寫一些初始化代碼,其作用就是告訴 Python 要將該目錄當成包來處理。__init__py不是模塊名,在 core包中的 __init__.py 文件,其模塊名就是core

包是一個包含多個模塊的文件夾,它的本質依然是模塊,因此包中也可以包含包。

11.1 創建包

包其實就是文件夾,更確切的說,是一個包含__init__.py文件的文件夾。如果想手動創建一個包,則需要兩步:

  • 創建一個文件夾,包名即為文件夾名
  • 在文件夾中創建一個__init__.py文件,在__init__.py中可以不編寫任何代碼。當然,也可以編寫一些初始化代碼

導入包的本質就是加載並執行包里的__init__.py 文件,然后將整個文件內容賦值給與包同名的變量,該變量的類型是 module

包的主要作用是包含多個模塊,因此__init__.py文件的主要作用就是導入該包內的其他模塊。

[root@hans_package]# tree myPackage
myPackage   # 包名
├── first.py 		# 相當於是模塊
├── __init__.py		# __init__.py文件
└── second.py		# 相當於是模塊

11.2 包的導入

# 1. 向 myPackage/__init__.py里寫出一些內容,然后再導入這個包
[root@hans_tencent_centos82 package]# cat  myPackage/__init__.py
print("This is a package")
# 2. 導入包
[root@hans_tencent_centos82 package]# cat test.py 
import myPackage 
# 3. 執行:
[root@hans_tencent_centos82 package]# python3 test.py 
This is a package

導入包的本質是執行包里的__init__.py 文件,因此我們完全可以在 __init__.py 文件中定義變量、函數、類等程序單元,但是不要這么做。永遠記住,__init__.py 文件的作用就是導入該包內的其他模塊。

# 1. 要在__init__.py增加包中包含的模塊
[root@hans_tencent_centos82 package]# cat myPackage/__init__.py    
from myPackage import first, second
# 2. 導入包
[root@hans_tencent_centos82 package]# cat test.py 
import myPackage 
myPackage.first.foo()
myPackage.second.boo()
# 執行結果:
[root@hans_tencent_centos82 package]# python3 test.py
from foo
from boo

# 如果直接導入包名,在__init__.py中要寫入要加載的那些模塊,否則會報錯。
# 把myPackage/__init__.py 中from myPackage import first, second 這句刪除。
# 就會報錯: AttributeError: module 'myPackage' has no attribute 'first'


# 如果__init__.py內就是沒有內容,則可以使用 from 包名 import 模塊名 句式導入
[root@hans_tencent_centos82 package]# cat test.py
from myPackage import first, second
first.foo()
second.boo()
# 執行結果:
[root@hans_tencent_centos82 package]# python3 test.py 
from foo
from boo

# 如果想直接執行包內的具體函數 可以使用 from 包名.模塊名 import 方法名 句式 
[root@hans_tencent_centos82 package]# cat test.py 
from myPackage.first import foo 
from myPackage.second import boo 

foo()
boo()

# 執行結果:
[root@hans_tencent_centos82 package]# python3  test.py   
from foo
from boo

11.3 包導入總結

包有三種導入方法:

  • import 包名

    直接導入包名,並不會將包中所有模塊全部導入到程序中,它的作用僅僅是導入並執行包下的 __init__.py 文件,所以要在__init__.py文件寫導入的模塊,所以使用包名調用的時候會報錯,使用方法: 包名.模塊名.方法名

  • from 包名 import 模塊名

    使用此語法格式導入包中模塊后,在使用其方法時不需要帶包名前綴,但需要帶模塊名前綴,使用方法:模塊名.方法名

  • from 包名.模塊名 import 方法名

    通過該方式導入的變量(函數、類),在使用時可以直接使用變量名(函數名、類名)調用。 使用方法:方法名

以上都可以使用as添加別名。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM