Python-基礎學習-第二輪

2022-08-08 06:04:45

資料型別

Python 預設擁有以下內建資料型別:

文字型別: str
數值型別: int, float, complex
序列型別: list, tuple, range
對映型別: dict
集合型別: set, frozenset
布林型別: bool
二進位制型別: bytes, bytearray, memoryview

在 Python 中,當您為變數賦值時,會設定資料型別:

範例 資料型別
x = "Hello World" str
x = 29 int
x = 29.5 float
x = 1j complex
x = ["apple", "banana", "cherry"] list
x = ("apple", "banana", "cherry") tuple
x = range(6) range
x = {"name" : "Bill", "age" : 63} dict
x = {"apple", "banana", "cherry"} set
x = frozenset({"apple", "banana", "cherry"}) frozenset
x = True bool
x = b"Hello" bytes
x = bytearray(5) bytearray
x = memoryview(bytes(5)) memoryview

如果希望指定資料型別,則您可以使用以下建構函式:

範例 資料型別
x = str("Hello World") str
x = int(29) int
x = float(29.5) float
x = complex(1j) complex
x = list(("apple", "banana", "cherry")) list
x = tuple(("apple", "banana", "cherry")) tuple
x = range(6) range
x = dict(name="Bill", age=36) dict
x = set(("apple", "banana", "cherry")) set
x = frozenset(("apple", "banana", "cherry")) frozenset
x = bool(5) bool
x = bytes(5) bytes
x = bytearray(5) bytearray
x = memoryview(bytes(5)) memoryview

檔案

open函數的引數

要讀寫檔案,首先要通過內建函數open 開啟檔案,獲得檔案物件。

函數open的引數如下

open(
    file, 
    mode='r', 
    buffering=-1, 
    encoding=None, 
    errors=None, 
    newline=None, 
    closefd=True, 
    opener=None
    ) 

其中下面這3個引數是我們常用的。

  • 引數 file

    file引數指定了要開啟檔案的路徑。

    可以是相對路徑,比如 ‘log.txt’, 就是指當前工作目錄下面的log.txt 檔案, 
    也可以是絕對路徑,比如 ’d:\project\log\log.txt',
    
  • 引數 mode

    mode引數指定了檔案開啟的 模式 ,開啟檔案的模式,決定了可以怎樣操作檔案。

    常用的開啟模式有

    • r 唯讀文字模式開啟,這是最常用的一種模式
    • w 只寫文字模式開啟
    • a 追加文字模式開啟

    如果我們要 讀取文字檔案內容到字串物件中 , 就應該使用 r 模式。

    我們可以發現mode引數的預設值 就是 ‘r’ 。

    就是說,呼叫open函數時,如果沒有指定引數mode的值,那麼該引數就使用預設值 ‘r’,表示唯讀開啟。

    如果我們要 建立一個新檔案寫入內容,或者清空某個文字檔案重新寫入內容, 就應該使用 ‘w’ 模式。

    如果我們要 從某個檔案末尾新增內容, 就應該使用 ‘a’ 模式。

  • 引數 encoding

    encoding 引數指定了讀寫文字檔案時,使用的 字元編解碼 方式。

    呼叫open函數時,如果傳入了encoding引數值:

      後面呼叫write寫入字串到檔案中,open函數會使用指定encoding編碼為位元組串;
        
      後面呼叫read從檔案中讀取內容,open函數會使用指定encoding解碼為字串物件
    

    如果呼叫的時候沒有傳入encoding引數值,open函數會使用系統預設字元編碼方式。 比如在中文的Windows系統上,就是使用cp936(就是gbk編碼)。

    建議大家編寫程式碼 讀寫文字檔案時,都指定該引數的值。

if __name__ == '__main__':
    # 指定編碼方式為 utf8
    f = open('D:/Code2022/Python/pythonProjectStudy/table_contents/tmp.txt', 'w', encoding='utf8')

    # write方法會將字串編碼為utf8位元組串寫入檔案
    f.write('李嘉圖:祝大家學有所成!')

    # 檔案操作完畢後, 使用close 方法關閉該檔案物件
    f.close()
    # 指定編碼方式為utf8
    f = open('D:/Code2022/Python/pythonProjectStudy/table_contents/tmp.txt', 'r', encoding='utf8')

    # read 方法會在讀取檔案中的原始位元組串後, 根據上面指定的gbk解碼為字串物件返回
    content = f.read()

    # 檔案操作完畢後, 使用close 方法關閉該檔案物件
    f.close()

    # 通過字串的split方法獲取其中使用者名稱部分
    name = content.split(':')[0]

    print(name)

with

if __name__ == '__main__':
    f = open('D:/Code2022/Python/pythonProjectStudy/table_contents/tmp.txt', 'w', encoding='utf8')
    f.write('李嘉圖:祝大家學有所成!')
    f.close()

    with open('D:/Code2022/Python/pythonProjectStudy/table_contents/tmp.txt', 'r', encoding='utf8') as f:
        linelist = f.readlines()
        for line in linelist:
            print(line)

附加:檔案和目錄

https://www.byhy.net/tut/py/extra/file_dir/

自呼叫其他程式

Python中呼叫外部程式主要是通過兩個方法實現的, 一個是os庫的 system 函數,另外一個是 subprocess 庫。

os.system函數

使用os庫的 system 函數 呼叫其它程式 是非常方便的。就把命令列內容 作為 system 函數的引數 即可

import os

if __name__ == '__main__':
    os.system('cd D:/test && mkdir test.txt')

os.system 函數呼叫外部程式的時候, 必須要等被呼叫程式執行結束, 才會接著往下執行程式碼。 否則就會一直等待

os.system 函數沒法獲取 被呼叫程式輸出到終端視窗的內容。 如果需要對被呼叫程式的輸出資訊進行處理的話, 可以使用 subprocess 模組。

os.startfile 函數

如果我們想達到類似檔案瀏覽器雙擊開啟一個檔案的效果可以使用 os.startfile 函數。

這個函數的引數可以是任何 非可執行程式 檔案

os.startfile('d:\\統計資料.xlsx')

可以呼叫該xlsx對應的關聯程式(Excel)來開啟該檔案。

subprocess 模組

Popen 是 subprocess的核心,子程序的建立和管理都靠它處理。

執行緒和程序

建立新執行緒

# 從 threading 庫中匯入Thread類
from threading import Thread
from time import sleep


# 定義一個函數,作為新執行緒執行的入口函數
def threadFunc(arg1, arg2):
    print('子執行緒 開始')
    print(f'執行緒函數引數是:{arg1}, {arg2}')
    sleep(5)
    print('子執行緒 結束')


if __name__ == '__main__':
    print('主執行緒執行程式碼')

    # 建立 Thread 類的範例物件
    thread = Thread(
        # target 引數 指定 新執行緒要執行的函數
        # 注意,這裡指定的函數物件只能寫一個名字,不能後面加括號,
        # 如果加括號就是直接在當前執行緒呼叫執行,而不是在新執行緒中執行了
        target=threadFunc,

        # 如果 新執行緒函數需要引數,在 args裡面填入引數
        # 注意引數是元組, 如果只有一個引數,後面要有逗號,像這樣 args=('引數1',)
        args=('引數1', '引數2')
    )

    # 執行start 方法,就會建立新執行緒,
    # 並且新執行緒會去執行入口函數裡面的程式碼。
    # 這時候 這個程序 有兩個執行緒了。
    thread.start()

    # 主執行緒的程式碼執行 子執行緒物件的join方法,
    # 就會等待子執行緒結束,才繼續執行下面的程式碼
    thread.join()
    print('主執行緒結束')

共用資料的存取控制

from threading import Thread, Lock
from time import sleep

bank = {
    'count': 0
}

bankLock = Lock()


# 定義一個函數,作為新執行緒執行的入口函數
def deposit(theadidx, amount):
    # 操作共用資料前,申請獲取鎖
    bankLock.acquire()

    balance = bank['count']
    # 執行一些任務,耗費了0.1秒
    sleep(0.1)
    bank['count'] = balance + amount
    print(f'子執行緒 {theadidx} 結束')

    # 操作完共用資料後,申請釋放鎖
    bankLock.release()


if __name__ == '__main__':
    theadlist = []
    for idx in range(10):
        thread = Thread(target=deposit,
                        args=(idx, 1)
                        )
        thread.start()
        # 把執行緒物件都儲存到 threadlist中
        theadlist.append(thread)
    for thread in theadlist:
        thread.join()

    print('主執行緒結束')
    print(f'最後我們的賬號餘額為 {bank["count"]}')

deamon執行緒

from threading import Thread
from time import sleep


def threadFunc():
    sleep(1)
    print('子執行緒 結束')


if __name__ == '__main__':
    # thread = Thread(target=threadFunc)
    # thread.start()
    # print('主執行緒結束')
    thread = Thread(target=threadFunc,
                    daemon=True  # 設定新執行緒為daemon執行緒
                    )
    thread.start()
    print('daemon主執行緒結束')

多執行緒

Python 官方直譯器 的每個執行緒要獲得執行許可權,必須獲取一個叫 GIL (全域性直譯器鎖) 的東西。

這就導致了 Python 的多個執行緒 其實 並不能同時使用 多個CPU核心。

所以如果是計算密集型的任務,不能採用多執行緒的方式。

from threading import Thread

def f():
    while True:
        b = 53*53

if __name__ == '__main__':
    plist = []
    # 啟動10個執行緒
    for i in range(10):
        p = Thread(target=f)
        p.start()
        plist.append(p)

    for p in plist:
        p.join()

多個CPU核心的運算能力,可以使用Python的多程序庫。

from multiprocessing import Process

def f():
    while True:
        b = 53*53

if __name__ == '__main__':
    plist = []
    for i in range(2):
        p = Process(target=f)
        p.start()
        plist.append(p)

    for p in plist:
        p.join()
from multiprocessing import Process, Manager
from time import sleep


def f(taskno, return_dict):
    sleep(1)
    # 存放計算結果到共用物件中
    return_dict[taskno] = taskno


if __name__ == '__main__':
    manager = Manager()
    # 建立 類似字典的 跨程序 共用物件
    return_dict = manager.dict()
    plist = []
    for i in range(10):
        p = Process(target=f, args=(i, return_dict))
        p.start()
        plist.append(p)
    for p in plist:
        p.join()
    print('get result...')
    # 從共用物件中取出其他程序的計算結果
    for k, v in return_dict.items():
        print(k, v)

JSON

序列化和反序列化

Python中內建了json這個庫,可以 方便的把內建的資料物件 序列化為json格式文字的字串。

import json

historyTransactions = [

    {
        'time': '20300101070311',  # 交易時間
        'amount': '3088',  # 交易金額
        'productid': '45454455555',  # 貨號
        'productname': 'iphone30'  # 貨名
    },
    {
        'time': '20300101050311',  # 交易時間
        'amount': '18',  # 交易金額
        'productid': '453455772955',  # 貨號
        'productname': '餅乾'  # 貨名
    }

]

if __name__ == '__main__':
    # dumps 方法將資料物件序列化為 json格式的字串
    jsonstr = json.dumps(historyTransactions)
    print(jsonstr)

import json

historyTransactions = [

    {
        'time': '20300101070311',  # 交易時間
        'amount': '3088',  # 交易金額
        'productid': '45454455555',  # 貨號
        'productname': 'iphone30'  # 貨名
    },
    {
        'time': '20300101050311',  # 交易時間
        'amount': '18',  # 交易金額
        'productid': '453455772955',  # 貨號
        'productname': '餅乾'  # 貨名
    }

]

if __name__ == '__main__':
    # dumps 方法將資料物件序列化為 json格式的字串
    jsonstr = json.dumps(historyTransactions)
    print(jsonstr)
    print('================')
    print(json.dumps(historyTransactions, ensure_ascii=False, indent=4))
    print('================')
    jsonstr = '[{"time": "20300101070311", "amount": "3088", "productid": "45454455555", "productname": "iphone7"}, {"time": "20300101070311", "amount": "18", "productid": "453455772955", "productname": "\u5999\u5999\u5999"}]'
    translist = json.loads(jsonstr)
    print(translist)
    print(type(translist))

裝飾器

Python中裝飾器通常用來裝飾函數、或者類的方法。

通常被裝飾後的函數, 會在原有的函數基礎上,增加一點功能。

裝飾器經常被用在庫和框架中, 給別的開發者使用。

這些庫的開發者預料到 使用者 開發的函數可能需要 一些增強的功能。

但是 這些庫的開發者 沒法去改使用者的程式碼, 就可以把這些增強的部分做在 裝飾器函數中。

這樣使用者,只需要在他們的函數前面上@xxx 就使用了這些增強的功能了。

基礎範例:

import time


# 定義一個裝飾器函數
def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'當地時間: {curTime}'

    return wrapper


@sayLocal
def getXXXTime():
    print()
    return time.strftime('%Y_%m_%d %H:%M:%S', time.localtime())


if __name__ == '__main__':
    # 裝飾 getXXXTime
    # getXXXTime = sayLocal(getXXXTime)
    print(getXXXTime())

進階範例-被裝飾的函數有引數:

import time


def sayLocal(func):
    def wrapper(*args, **kargs):
        curTime = func(*args, **kargs)
        return f'當地時間: {curTime}'

    return wrapper


@sayLocal
def getXXXTimeFormat1(name):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    return f'{curTime} ,資料採集者:{name} '


@sayLocal
def getXXXTimeFormat2(name, place):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    return f'{curTime} ,資料採集者:{name} , 採集地:{place}'


if __name__ == '__main__':
    print(getXXXTimeFormat1('張三'))
    print(getXXXTimeFormat2('張三', place='北京'))

進階範例-裝飾器函數自身有引數:

# 新增輸出紀錄檔的功能
def logging(flag):
    def decorator(fn):
        def inner(num1, num2):
            if flag == "+":
                print("--正在努力加法計算--")
            elif flag == "-":
                print("--正在努力減法計算--")
            result = fn(num1, num2)
            return result

        return inner

    # 返回裝飾器
    return decorator


# 使用裝飾器裝飾函數
@logging("+")
def add(a, b):
    result = a + b
    return result


@logging("-")
def sub(a, b):
    result = a - b
    return result


if __name__ == '__main__':
    result = add(1, 2)
    print(result)

    result = sub(1, 2)
    print(result)

加密

演演算法 計算結果長度
MD5 16位元組
SHA1 20位元組
SHA224 28位元組
SHA256 32位元組
SHA384 48位元組
SHA512 64位元組

典型應用場景:

校驗拷貝下載檔案

校驗資訊有效性

使用 Python 內建庫 hashlib 建立hash值。

範例:

import hashlib

if __name__ == '__main__':
    # 使用 md5 演演算法
    # m = hashlib.md5()
    # 如果你想使用別的雜湊演演算法,比如, sha256 演演算法,只需要修改為對應的函數 sha256()即可
    m = hashlib.sha256()

    # 要計算的源資料必須是位元組串格式
    # 字串物件需要encode轉化為位元組串物件
    m.update("ML李嘉圖|mllijaitu".encode())

    # 產生雜湊值對應的bytes物件
    resultBytes = m.digest()
    # 產生雜湊值的十六進位製表示
    resultHex = m.hexdigest()
    print(resultHex)

SSH遠端操作

Python第三方庫 Paramiko 就是作為ssh使用者端遠端控制Linux主機 的。

exec_command 是每次執行都是 新開啟一個channel的東西執行,

每個channel都是命令執行的環境,每執行命令都是一個新的執行環境,不在上次執行的環境裡面,

相當於 每次都在各自的執行環境裡面,和前面的執行環境沒有關係。

多個命令一起執行,用分號隔開,像這樣:

stdin, stdout, stderr = ssh.exec_command("cd testdir;pwd")

操作單臺主機:

# 單臺主機操作
import paramiko

#############################設定資訊#####################################
# 登陸引數設定
hostname = ""
host_port = 22
username = "root"
password = ""


########################################################################

def ssh_client_con():
    """建立ssh連線,並執行shell指令"""
    # 1 建立ssh_client範例
    ssh_client = paramiko.SSHClient()
    # 自動處理第一次連線的yes或者no的問題
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
    # 2 連線伺服器
    ssh_client.connect(
        port=host_port,
        hostname=hostname,
        username=username,
        password=password
    )
    # 3 執行shell命令
    # 構造shell指令
    shell_command = "ls"
    stdin, stdout, stderr = ssh_client.exec_command(shell_command)
    # 輸出返回資訊
    stdout_info = stdout.read().decode('utf8')
    print(stdout_info)
    # 輸出返回的錯誤資訊
    stderr_info = stderr.read().decode('utf8')
    print(stderr_info)


def sftp_client_con():
    # 1 建立transport通道
    tran = paramiko.Transport((hostname, host_port))
    tran.connect(username=username, password=password)
    # 2 建立sftp範例
    sftp = paramiko.SFTPClient.from_transport(tran)
    # 3 執行上傳功能
    local_path = ""  # 本地路徑
    remote_path = ""  # 遠端路徑
    put_info = sftp.put(local_path, remote_path, confirm=True)
    print(put_info)
    print(f"上傳{local_path}完成")
    # 4 執行下載功能
    save_path = ""  # 本地儲存檔案路徑
    sftp.get(remotepath=remote_path, localpath=save_path)
    print(f'下載{save_path}完成')
    # 5 關閉通道
    tran.close()


if __name__ == '__main__':
    # 呼叫函數執行功能
    ssh_client_con()
    # sftp_client_con()

Python物件導向程式設計

類變數與範例變數

# 建立一個學生類
class Student:
    # number屬於類變數,不屬於某個具體的學生範例
    number = 0
    
    # 定義學生屬性,初始化方法
    # name和score屬於範例變數
    def __init__(self, name, score):
        self.name = name
        self.score = score
        Student.number = Student.number + 1
    
    # 定義列印學生資訊的方法
    def show(self):
        print("Name: {}. Score: {}".format(self.name, self.score))

# 範例化,建立物件
student1 = Student("John", 100)
student2 = Student("Lucy", 99)

print(Student.number)  # 列印2
print(student1.__class__.number) # 列印2

類方法

有些變數只屬於類,有些方法也只屬於類,不屬於具體的物件。

你有沒有注意到屬於物件的方法裡面都有一個self引數, 比如__init__(self), show(self)?

self是指物件本身。

屬於類的方法不使用self引數, 而使用引數cls,代表類本身。

另外習慣上對類方法我們會加上@classmethod的修飾符做說明。

class Student:
    # number屬於類變數,不屬於某個具體的學生範例
    number = 0

    # 定義學生屬性,初始化方法
    # name和score屬於範例變數
    def __init__(self, name, score):
        self.name = name
        self.score = score
        Student.number = Student.number + 1

    # 定義列印學生資訊的方法
    def show(self):
        print("Name: {}. Score: {}".format(self.name, self.score))

    # 定義類方法,列印學生的數量
    @classmethod
    def total(cls):
        print("Total: {0}".format(cls.number))


if __name__ == '__main__':
    # 範例化,建立物件
    student1 = Student("John", 100)
    student2 = Student("Lucy", 99)

    Student.total()  # 列印 Total: 2

類的私有屬性和私有方法

類裡面的私有屬性和私有方法以雙下劃線__開頭。私有屬性或方法不能在類的外部被使用或直接存取。

# 建立一個學生類
class Student:

    # 定義學生屬性,初始化方法
    # name和score屬於範例變數, 其中__score屬於私有變數
    def __init__(self, name, score):
        self.name = name
        self.__score = score
    
    # 定義列印學生資訊的方法
    def show(self):
        print("Name: {}. Score: {}".format(self.name, self.__score))

# 範例化,建立物件
student1 = Student("John", 100)

student1.show()  # 列印 Name: John, Score: 100
student1.__score  # 列印出錯,該屬性不能從外部存取。

@property

# 建立一個學生類
class Student:

    # 定義學生屬性,初始化方法
    # name和score屬於範例變數, 其中score屬於私有變數
    def __init__(self, name, score):
        self.name = name
        self.__score = score

    # 利用property裝飾器把函數偽裝成屬性
    @property
    def score(self):
        print("Name: {}. Score: {}".format(self.name, self.__score))


if __name__ == '__main__':
    # 範例化,建立物件

    student1 = Student("John", 100)

    student1.score  # 列印 Name: John. Score: 100

類的繼承

# 建立父類別學校成員SchoolMember
class SchoolMember:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def tell(self):
        # 列印個人資訊
        print('Name:"{}" Age:"{}"'.format(self.name, self.age), end=" ")


# 建立子類老師 Teacher
class Teacher(SchoolMember):

    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)  # 利用父類別進行初始化
        self.salary = salary

    # 方法重寫
    def tell(self):
        SchoolMember.tell(self)
        print('Salary: {}'.format(self.salary))


# 建立子類學生Student
class Student(SchoolMember):

    def __init__(self, name, age, score):
        SchoolMember.__init__(self, name, age)
        self.score = score

    def tell(self):
        SchoolMember.tell(self)
        print('score: {}'.format(self.score))


if __name__ == '__main__':
    teacher1 = Teacher("John", 44, "$60000")
    student1 = Student("Mary", 12, 99)
    teacher1.tell()
    student1.tell()

靜態變數和靜態方法

# 建立一個學生類
class Student:
    # number屬於類變數,定義在方法外,不屬於具體範例
    number = 0

    # 定義學生屬性,初始化方法
    # name和score屬於範例變數,定義在方法裡
    def __init__(self, name, score):
        self.name = name
        self.score = score
        Student.number = self.number + 1

    # 定義列印學生資訊的方法
    def show(self):
        print("Name: {}. Score: {}".format(self.name, self.score))

    # 靜態方法無法使用cls和self引數存取類或範例的變數
    @staticmethod
    def func1():
        print("this is static function!")