Python 程式設計

Undefined443發表於2024-06-10

開發

PyCharm Docs

騰訊雲:Python 虛擬環境(pipenv、vnev、conda)一網打盡

pipx

pipx 官網

pipx — 在孤立環境中安裝和執行 Python 應用程式

安裝

brew install pipx
pipx ensurepath

命令補全

pipx completions # 遵循該命令的指引

Poetry

Poetry

Poetry 是當下熱門的 Python 包管理器。Poetry 注重為專案提供完整的生命週期管理,包括構建、打包、釋出和依賴管理。它的目標是成為 Python 專案的唯一工具。其使用 pyproject.toml 檔案來管理專案的依賴和構建配置。

安裝

pipx install poetry

命令補全

mkdir $ZSH_CUSTOM/plugins/poetry
poetry completions zsh > $ZSH_CUSTOM/plugins/poetry/_poetry

然後,你必須將 poetry 新增到你的 ~/.zshrc 中的 plugins 陣列:

plugins(
    poetry
    ...
)

建立新專案

poetry new poetry-demo
cd poetry-demo
poetry shell
exit

初始化已經存在的專案

cd pre-existing-project
poetry init

安裝依賴

poetry install

啟用虛擬環境

poetry shell
$ exit  # 退出虛擬環境

移除虛擬環境

poetry env remove

Basic Usage | Poetry Docs

Pipenv

Pipenv 是 Python 官方推薦的依賴管理工具,旨在簡化 pip 和 virtualenv 的使用。其使用 PipfilePipfile.lock 來管理專案的依賴和虛擬環境。

# 安裝
pip3 install --user pipenv  # 如果當前使用者不是 root,就使用 --user 選項

不要使用 brew 安裝 pipenv:

Homebrew installation is discouraged because it works better to install pipenv using pip on macOS.

pipenv install  # 為目錄建立新的虛擬環境,並使用目錄中的 Pipfile 或 requirements.txt 安裝依賴

PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy  # deploy 會驗證 Pipfile.lock 是不是由對應的 Pipfile 生成的

# 使用映象源安裝
pipenv install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy

pipenv --venv  # 檢視當前虛擬環境的資訊

pipenv shell  # 啟用虛擬環境

pipenv run python main.py  # 直接在外部執行虛擬環境命令

exit  # 退出虛擬環境

pipenv --rm  # 刪除虛擬環境

# 更改映象源
pipenv --pypi-mirror https://pypi.tuna.tsinghua.edu.cn/simple

pipenv lock    # 生成 Pipfile.lock
pipenv sync    # 安裝 Pipfile.lock 中的依賴
pipenv update  # pipenv lock && pipenv sync

pipenv requirements > requirements.txt  # 生成 requirements.txt

常用命令一覽:

pipenv --where                 # 列出本地工程路徑
pipenv --venv                  # 列出虛擬環境路徑
pipenv --py                    # 列出虛擬環境的 Python 可執行檔案
pipenv install                 # 建立虛擬環境
pipenv install [moduel]        # 安裝包
pipenv install [moduel] --dev  # 安裝包到開發環境
pipenv uninstall[module]       # 解除安裝包
pipenv uninstall --all         # 解除安裝所有包
pipenv graph                   # 檢視包依賴
pipenv lock                    # 生成 lockfile
pipenv run python [pyfile]     # 執行 py 檔案
pipenv --rm                    # 刪除虛擬環境

Official Website

Python Guide CN

Python——pipenv 精心整理教程 | 掘金

擁抱 pipenv - ThomasYoungK | 簡書

Pipenv environment variable LANG is not set! | neldeles's personal blog/portfolio

pip

Remove the pip search command

由於自 2020 年 11 月 14 日以來,PyPI XMLRPC API 持續收到過量的搜尋呼叫,因此 pip search 命令將在不久的將來(撰稿日期 2022.9.26)棄用。目前要想使用 pip search 功能,可以在 PyPI 官網 進行搜尋。

為 Python 專案獨立地配置虛擬環境:Virtual Environments and Packages

python3 -m venv .venv  # 在當前專案下新建一個 .venv 資料夾,用於存放虛擬環境
source .venv/bin/activate  # 進入虛擬環境
deactivate  # 退出虛擬環境

即使沒有啟用虛擬環境,只要執行的是虛擬環境的 Scripts 目錄下的 python 或 pip 可執行程式,依然能達到進入虛擬環境的效果。

在 PyCharm 中指定 Python 直譯器為虛擬環境中的直譯器即可進入虛擬環境。

換源

臨時更換映象源

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ some-package

永久更換映象源

在檔案 ~/.pip/pip.conf 中填入以下內容:

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host = https://pypi.tuna.tsinghua.edu.cn

Windows 上的相應檔案路徑為 %HOMEPATH%\pip\pip.ini

PIP 配置檔案參考文件

requirements.txt

pipreqs 工具可以透過掃描專案目錄幫助我們生成專案的依賴清單:

pip install pipreqs
pipreqs .  # 為當前專案生成依賴清單

也可以使用 pip3 freeze,該命令將當前虛擬環境中安裝的所有包及其版本號寫入 requirements.txt 檔案。(需要在 venv 環境下執行)

pip freeze > requirements.txt  # 生成當前環境的依賴清單
pip install -r requirements.txt  # 安裝依賴

可以在 requirements.txt 頂部新增 -i https://pypi.tuna.tsinghua.edu.cn/simple 來指定映象源。

pipinstaller

Installs pip packages on all your installed Python versions (Windows only)

官方文件

# 打包專案
pipinstaller -p . -o ./dist 

Practice

進度條

from tqdm import tqdm

progress_bar = tqdm(total=total_num, desc='Progress', unit='kb', unit_scale=True)
for i in range(total_num):
    progress_bar.update(1)
progress_bar.close()

雜項

Python 識別符號命名規範

淺複製和深複製

淺複製和深複製

  1. 淺複製,指的是重新分配一塊記憶體,建立一個新的物件,但裡面的元素是原物件中各個子物件的引用。
  2. 深複製,是指重新分配一塊記憶體,建立一個新的物件,並且將原物件中的元素,以遞迴的方式,透過建立新的子物件複製到新物件中。

bytes

相當於位元組陣列

b1 = b"ASCII"                          # ASCII 字元可以直接轉換為 bytes
b2 = bytes("string", encoding="utf-8") # 使用構造方法將指定的字符集轉換為 bytes
b3 = "string".encode("utf-8")          # 使用字串自帶的 encode() 方法獲得 bytes

s = b3.decode("utf-8")                 # bytes 類也有 decode() 方法返回 string

複數

complex = 1 + 2j  # 虛部以 j 或 J 作為字尾

高精度計算

import decimal # 十進位制運算
a = decimal.Decimal("10.0")
b = decimal.Decimal("3")
print(a/b)
from fractions import Fraction
print(Fraction(10, 3))

原生字串

r'raw string'
r"raw string"

長字串

'''long string'''
"""long string"""
r'''raw long string'''

字串換行

# 推薦
str = ("str1"
"str2")

# 不推薦
str = "str1\
str2"

型別轉換

Python 型別轉換

int(str)
float(str)
bool(str)
complex(real, [imag])
str(x)

chr(x)  # 將 ASCII 碼 x 轉換成對應的字元
ord(c)  # 將字元 c 轉換成 ASCII 碼

oct(x)  # 將 x 轉換為 8 進位制字串
hex(x)  # 將 x 轉換為 16 進位制字串

常用函式

type(obj) # 檢測 obj 的型別
id(obj)   # 返回 obj 的地址
dir(obj)  # 檢視 obj 中的內容,obj 可省略。
help(obj) # 檢視 obj 的幫助文件

__doc__ 屬性:提供某個函式的說明文件

assert 斷言

assert expression # 若表示式值為假,丟擲 AssertionError 錯誤。

邏輯控制

if

if expression:
    # TO DO
elif expression:
  pass
else:
    # TO DO

while

while expression:
    # TO DO
else:
    # TO DO

for

for var in c:
    # TO DO
else:
    # TO DO

break
continue

whilefor 後面都可以跟 else

match

match http_status:
    case 404:
        return "Not found"
    case 200:
        return "OK"
    case 418:
        return "I'm a teapot"
    case _:  # 相當於 default
        return "Something's wrong with the internet"

運算子

算數運算子與賦值運算子

// 整除

** 冪運算

* 可以用於重複字串:str * 4

別忘了冪運算可以實現開方運算:16**(1/4)

算數運算子
賦值運算子

is, is not== 用來比較兩個變數的值是否相等,而 is 用來對比兩個變數引用的是否是同一個物件。

邏輯運算子

and, or, not

andor 運算子會將其中一個表示式的值作為結果。

三目運算子

max = a if a>b else b

I/O

輸入

str = input("tipmsg")  # tipmsg 是提示資訊

輸出

print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

print() 函式接受多個輸出內容 value,可以透過 sep 設定分隔符,end 設定結束符,file 設定輸出檔案,flush 控制輸出快取(一般為 False)。

f = open("input.txt", "w")  # 以寫入模式開啟 input.txt
print('Hello World', file=f)
f.close()

print 格式化輸出

print() 使用的轉換說明符與 C 語言 printf() 類似。

name = "Xiao"
age = 20
print("My name is %s and I'm %d years old." % (name, age))

格式化字串和變數之間用 % 分隔。

跳脫字元

序列

str[1]  # 可以使用 0 ~ n-1 的索引訪問序列元素,也可以使用 -n ~ -1 的索引訪問序列元素。
str[start:end:step]  # 序列切片,[start, end)
list = [None] * 5  # 序列相乘

in 關鍵字

可以使用 in 關鍵字檢查某元素是否為序列的成員:

str = "c.biancheng.net"
print('c' in str)

還有和 in 功能相反的 not in 關鍵字。

內建函式

len(s)
max(s)
min(s)
list(s)      # 將序列轉換為列表
str(s)       # 將序列轉換為字串
sum(s)       # 計算元素和,元素必須為數字。
sorted(s)    # 返回一個排好序的 list
reversed(s)  # 返回給定序列的反向迭代器
enumerate(s) # 將序列組合為一個索引序列

sorted()

list

Python 中沒有陣列,但是有功能更加強大的列表。

# 建立列表
l = [1, "2", [3.0]] # 列表可以儲存任意型別
# list() 可以將其他資料型別轉換為列表型別
l = list("Hello")   # ['H', 'e', 'e', 'l', 'l', 'o']

# 新增元素
l.append(obj)    # 將引數作為整體加入列表
l.extend(obj)    # 若引數是列表或元組,將它們包含的元素逐個加入列表。
l.insert(0, obj) # 在指定位置插入元素,將引數視為整體
l3 = l1 + l2

# 刪除元素
del l[0]         # 也可以使用負數索引
del l[start:end] # 刪除 [start, end)
l.pop(index)     # 若省略索引則刪除最後一個元素
l.remove(val)    # 根據元素本身的值進行刪除,只會刪除第一個匹配項,並且必須保證匹配項存在。
l.clear()        # 清空列表

# 修改元素
l[0] = 2
l[1:4] = [3.4, 5, 2]     # 新賦值元素個數可以不與區間元素個數相同
l[1:6:2] = [0.1, -99, 2] # 但是如果指定步長,則必須相同。

# 查詢元素
# 如果元素不存在,丟擲 ValueError 錯誤。返回 val 的索引值。
l.index(val[,start[,end]])

count(val) # 返回元素在列表中出現的次數

del 刪除的應該是引用變數,實際的記憶體空間並不會馬上被回收。要想讓系統回收這些記憶體,需要使用 gc 庫的 collect() 函式。

import gc
l = [1, 2, 3]
del l
gc.collect()

range 快速初始化數字列表

r1 = range(10)
r2 = range(0, 10)    # 返回 [0, 10) 的 range
r3 = range(0, 10, 2) # 設定步長

range() 返回的並不是 list 型別,返回的就是 range 型別。如果要得到 list 型別,需要使用 list() 函式。

序列解包

Python 可以以這種形式直接將列表/元組中的元素賦給對應的變數:

lst = [1, 2, 3]
a1, a2, a3 = lst

tuple 元組

  1. 元組是不可變序列;
  2. 元組可以在對映和集合中當作“鍵”來使用;
t1 = (1, 2, 3)  # 元素可以是任意型別;小括號可以省略。
t1 = 1, 2, 3
t2 = ("str",)   # 如果元組中只有一個字串,必須在字串後面加一個逗號。

t3 = tuple(obj) # 將其他資料型別轉化為元組型別

dict 字典

相當於 C++ 中的 map

d1 = {key1: val1, key2: val2}        # val 可以是任意資料型別,key 只要滿足唯一和不可變即可。
d2 = dict.fromkeys(list, value=None) # 使用 list 作為鍵的列表,value 為每個鍵對應的值。
d3 = dict(str1=val1, str2=val2)      # str 表示字串型別的鍵(不帶引號)

s = [['two', 2], ['one', 1], ['three', 3]]
d4 = dict(s)                         # 向 dict() 函式傳入列表或元組,而它們中的元素又各自是包含 2 個元素的列表或元組,其中第一個元素作為鍵,第二個元素作為值。

keys = ['one', 'two', 'three']
vals = [1, 2, 3]
a = dict(zip(keys, vals))

基本操作

# 訪問元素
d[key]               # 若 key 不存在,則丟擲異常。
d.get(key[,default]) # 不會丟擲異常,可以指定查詢失敗時的預設返回值。

# 增加/修改元素
d[key] = val         # 直接給不存在的鍵賦值;當鍵已存在時,覆蓋對應的值。

# 刪除元素
del d[key]

# 判斷字典中是否存在指定的鍵
print(key in d)

常用操作

獲取字典的鍵、值、鍵值對
d.keys()   # 返回 dict_keys
d.values() # 返回 dict_values
d.items()  # 返回 dict_items

這些方法的返回值不是常見的列表或元組型別,如果想使用返回的資料,一般有下面兩種方法:

  1. 使用 list() 函式
  2. 使用 for in 迴圈
for k, v in d.items():
    print(k + ": " + v)

copy

copy() 方法只會對最表層的鍵值對進行深複製。

d1 = {'one': 1, 'two': 2, 'three': [1, 2, 3]}
d2 = d1
d1['four'] = 4        # 不影響 d2
d1['three'].remove(1) # 影響 b2

其他

d2.update(d1) # 使用 d1 更新 d2 ,不存在的鍵值對加入,重複的鍵值對覆蓋。

d.pop(key)    # 刪除指定的鍵值對
d.popitem()   # 刪除最後的鍵值對

d.setdefault(key, defval) # key 不存在時,設定 key-defval 鍵值對並返回 defval;key 存在時,返回其對應的 val 。

使用字典格式化字串

set 集合

set 相當於只有鍵,沒有值的 map 。

s1 = {1, 2, 3}
s2 = set(iteration) # 將字串、列表、元組等可迭代物件轉換成集合
  1. {} 會被直譯器當作空字典,而不是空集合。
  2. 由於 Python 中的 set 集合是無序的,所以每次輸出時元素的排序順序可能都不相同。

基本操作

s.add(obj)    # 只能新增不可變資料
s.remove(obj) # 若元素不存在,則丟擲 KeyError 錯誤。

s1 & s2       # 取交集
s1 | s2       # 取並集
s1 - s2       # 取差集
s1 ^ s2       # 取對稱差集

取對稱差集:取集合 A 和 B 中不屬於 A & B 的元素

set 方法
frozenset

字串

str1 = "string 1" "string 2" # 拼接字串常量
str2 = s1 + s2               # 拼接字串變數

str(obj)                     # 將 obj 轉換為字串
repr(obj)                    # 將 obj 轉換為 Python 字串的表示式形式

s.encode("gbk")              # 獲取編碼後得到的 bytes

encode() 和 decode() 方法

常用方法

# 分割字串
s.split(sep[,maxsplit])   # sep:分隔符,預設為 None ,表示所有空白符;maxsplit 表示分割次數(返回的列表中最多有 maxsplit + 1 個元素),分割後剩餘的部分不會截斷,而是一起放到返回的 list 中。
s.split(maxsplit=-1)      # 如果不指定 sep 引數,則必須指定 maxsplit;maxsplit=-1 表示分割次數沒有限制。

# 連線字串
str.join(iterable) # str 是合併時的分隔符,iterable 是合併字串的來源,可以是列表、元組等。
'-'.join(["152", "0950", "3397"])

# 統計子串出現次數
s1.count(s2[,start[,end]])

# 查詢子串
s1.find(s2[,start[,end]])  # 返回 s2 在 s1 中的索引,或者 -1 。
s1.rfind(s2[,start[,end]])
s1.index(s2[,start[,end]]) # 與 find 的區別是,如果查詢失敗,index() 會丟擲異常。

# 文字對齊
# width 是包括 s 本身在內的字串總長度;fillchar 預設為空格。
s.ljust(width[,fillchar])  # 左對齊
s.rjust(width[,fillchar])  # 右對齊
s.center(width[,fillchar]) # 居中

s1.startswith(s2[,start[,end]]) # 檢查 s1 是否以 s2 為開頭
s1.endswith(s2[,start[,end]])

s.title() # 將每個單詞的首字母大寫,其他小寫。
s.lower() # 全部轉換為小寫
s.upper()

# 刪除指定字元(串)
s.strip([chars])  # 刪除 s 左右兩側的指定字元(串)
s.lstrip([chars])
s.rstrip([chars])

如果 sep 引數為 None ,則連續的空白符不會對分割結果產生影響。

格式化輸出

format 格式化輸出

使用 {}: 指定佔位符

print("貨幣形式:{:,d}".format(1000000))
print("科學計數法:{:E}".format(1200.12))
print("100 的十六進位制:{:#x}".format(100))
print("0.01 的百分比表示:{:.0%}".format(0.01))

推導式

使用推導式可以快速生成列表、元組、字典以及集合型別的資料,因此推導式又可細分為列表推導式、元組推導式、字典推導式以及集合推導式。

列表推導式的語法格式:

[表示式 for 迭代變數 in 可迭代物件 [if 條件表示式]]

其中,[if 條件表示式] 可以省略。

# 列表推導式
[x**2 for x in range(1, 10)]                       # 1 到 9 的平方列表
[x**2 for x in range(1, 10) if x % 2 == 0]         # 1 到 9 的偶數平方列表
[(x, y) for x in range(1, 6) for y in range(1, 5)] # 可以使用多個迴圈

# 元組推導式
(x**2 for x in range(1, 10)) # 用法和列表推導式相同

# 字典推導式
{key:len(key) for key in l}  # 使用列表 l 中的元素作為 key ,key 的長度作為 value 。

# 集合推導式
{x**2 for x in range(1, 10)}

元組推導式生成的結果並不是 tuple ,而是 generator 。在遍歷過 generator 物件後其中的元素將不復存在。

a = (x for x in range(1, 10)) # 透過元組推導式獲得一個 generator

如果想要使用元組推導式獲得新元組或新元組中的元素,有以下三種方式:

  1. 使用 tuple() 建構函式
print(tuple(a))
  1. 使用 for 迴圈遍歷 generator 物件
for i in a:
    print(i, end=' ')
  1. 使用 __next__() 方法遍歷 generator 物件
print(a.__next__())
print(a.__next__())

zip 函式

zip() 可以將多個序列按照對應位置順序重組為一個個新的元組。當序列元素個數不一致時,取最短的那個。

zip(iterable, ...)

zip() 函式返回的是一個 zip() 物件,可以遍歷 generator 的方法來訪問。

collections 模組

import collections

dq = deque()

函式

def func():
    '''
    這裡可以編寫說明文件
    '''
    # TO DO
    return

# 獲得說明文件
help(func)
print(func.__doc__)

在傳遞引數時,如果實參型別為不可變型別 (字串、數字、元組),則使用值傳遞;若實參型別為可變型別 (列表、字典),則使用引用傳遞。其原因是在 Python 中可變型別是引用資料型別,在傳參時實際上傳的是引用,本質上還是值傳遞。

因此,如果需要讓函式修改某些資料,則可以透過把這些資料包裝成列表等可變物件,然後在函式中修改列表的方式來完成。

關鍵字引數

def func(n1, n2):
    print(n1)
    print(n2)

func(1, 2)       # 使用位置引數
func(n2=2, n1=1) # 使用關鍵字引數
func(1, n2=2)    # 混合傳參,關鍵字引數必須位於所有位置引數之後。

引數預設值

def func(arg1, arg2, arg3=3): # 有預設值的形參必須放在最後
    pass

print(func.__defaults__)      # 可以透過函式的 `__defaults__` 屬性檢視函式的引數預設值

可變引數

# 元組引數
def func(home, *args) # 在位置引數接收實參後,args 將剩下的所有非關鍵字引數以元組的形式接收。
def func(*args, home) # 元組形式的可變引數不一定要放在最後,但此時必須以關鍵字引數的形式指定位置引數 home 。

# 字典引數
def func(home, **args) # args 接收所有以關鍵字引數賦值的實參,此時 args 必須放在最後。

可變引數的預設值是空元組/空字典,因此可以不給可變引數傳值。

逆向引數收集

可變引數將多個引數儲存到列表/元組中,這個過程稱為引數收集。反過來,將列表、元組、字典作為函式引數,Python 可以將其拆分,把其中的元素按照次序分給函式中的各個形參,這個過程叫做逆向引數收集。

和可變引數的規則類似,傳入列表或元組時,要在其名稱前加一個 * ,傳入字典時,要在其名稱前加一個 **

def func(arg1, arg2, arg3):
    pass

args = [1, 2, 3]
func(*args)  # 列表,以位置引數的方式傳參。

args = {"arg1": 1, "arg2": 2, "arg3": 3}
func(**args) # 字典,以關鍵字引數的方式傳參。

偏函式

簡單的理解偏函式,它是對原始函式的二次封裝,是將現有函式的部分引數預先繫結為指定值,從而得到一個新的函式,該函式就稱為偏函式。相比原函式,偏函式具有較少的可變引數,從而降低了函式呼叫的難度。

from functools import partial

def func1(arg1, arg2):
    pass

func2 = partial(func1, arg1 = 1) # 定義偏函式,其封裝了 func1() ,併為 arg1 設定了預設引數。
func2(arg2 = 2) # 這裡必須用關鍵字引數的形式給 arg2 傳參,否則 Python 將嘗試將引數傳給 arg1 。

變數作用域

在函式內定義全域性變數

def func():
    global var # 在使用 global 關鍵字修飾變數名時,不能給變數賦初值。

獲取指定作用域中的變數

globals() # 返回一個包含所有全域性變數的字典
globals()['var1'] = 1 # 可以透過該字典訪問或修改指定變數

locals() # 返回一個包含當前作用域內所有變數的字典
print(locals()['var1']) # locals() 返回的字典只能訪問變數,無法用來修改變數的值。

vars(obj) # 返回 obj 物件範圍內所有變數組成的字典。

如果要在函式內操作全域性變數,有以下兩種方法:

  1. 透過 globals() 函式
var = 1
def func():
    print(globals()['var']) # 訪問全域性變數 var
    var = "new var" # 定義一個新的區域性變數 var
  1. 使用 global 語句宣告全域性變數
var = 1
def func():
    global var
    print(var)
    var = "same var" # 對全域性變數 var 賦值

class Student:
    name = "Xiao"
    age = 20

    # 建構函式
    def __init__(self, _name):
        name = _name

    def func(self, msg):
        print(msg)

stu = Student("Li Xiao")
stu.money = 999_999_999 # 可以給類物件動態新增/刪除變數
del stu.money

相關文章