最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python項目重構(gòu)SQLite數(shù)據(jù)庫的實戰(zhàn)記錄

 更新時間:2026年03月19日 08:46:42   作者:竹林818  
這篇文章主要為大家詳細(xì)介紹了Python項目重構(gòu)SQLite數(shù)據(jù)庫的相關(guān)知識,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以了解下

背景:一團(tuán)亂麻的數(shù)據(jù)層

上個月,我接手維護(hù)一個公司內(nèi)部用的數(shù)據(jù)采集與分析工具。這個工具用Python寫成,核心功能是把從不同API抓取到的設(shè)備狀態(tài)數(shù)據(jù)存起來,供一個簡單的Web面板查詢。數(shù)據(jù)量不大,每天也就幾千條記錄,所以當(dāng)初的開發(fā)者選用了SQLite,這本身沒問題。問題出在實現(xiàn)上。

我剛拿到代碼時,差點沒背過氣去。整個項目就一個database.py文件,里面密密麻麻全是這樣的代碼:

def insert_device_data(device_id, status, timestamp):
    conn = sqlite3.connect('data.db')
    cursor = conn.cursor()
    # 注意看這里,字符串直接拼接!
    sql = f"INSERT INTO device_log (device_id, status, log_time) VALUES ('{device_id}', '{status}', '{timestamp}')"
    cursor.execute(sql)
    conn.commit()
    conn.close()

這簡直是“教科書式”的錯誤示范:手動管理連接、字符串拼接SQL(SQL注入的邀請函)、沒有錯誤處理、表結(jié)構(gòu)變更靠人肉在DB Browser里點點點。更頭疼的是,由于沒有數(shù)據(jù)模型定義,我根本不知道device_log表到底有哪些字段,類型是什么。項目運行半年,已經(jīng)出現(xiàn)了幾次因為數(shù)據(jù)格式問題導(dǎo)致的插入失敗,以及一次輕微的數(shù)據(jù)混亂。

我的任務(wù)很明確:在不影響現(xiàn)有數(shù)據(jù)的前提下,重構(gòu)這個數(shù)據(jù)層,讓它變得可維護(hù)、可擴(kuò)展、更安全。核心訴求就三點:1. 用ORM替代裸SQL;2. 引入數(shù)據(jù)庫遷移工具;3. 保持向后兼容,平滑過渡。

問題分析:ORM選型與遷移策略

我的第一反應(yīng)是上SQLAlchemy,這是Python生態(tài)里最強大的ORM,沒有之一。但緊接著就面臨兩個具體問題:

  • 如何兼容現(xiàn)有混亂的數(shù)據(jù)? 直接讓SQLAlchemy的declarative_base根據(jù)現(xiàn)有表結(jié)構(gòu)自動生成模型(automap)是一個選擇,但自動生成的模型可能無法完美反映一些業(yè)務(wù)約束,而且表名和字段名如果很隨意,生成的類名也會很丑。
  • 如何管理表結(jié)構(gòu)變更? 我需要一個類似Django Migrations的工具。Alembic是SQLAlchemy官方的遷移工具,但它通常需要從模型生成遷移腳本。而我現(xiàn)在的情況是:數(shù)據(jù)庫已經(jīng)存在,模型還沒定義。這是一個“先有雞還是先有蛋”的問題。

我最初的思路是分兩步走:先用sqlite3命令行或工具把現(xiàn)有表結(jié)構(gòu)導(dǎo)出來,手動寫成SQLAlchemy模型。然后,把這個初始模型當(dāng)作基準(zhǔn),讓Alembic生成一個“初始遷移”腳本,但這個腳本應(yīng)該是空的(因為表已經(jīng)存在)。我查了Alembic文檔,發(fā)現(xiàn)它確實支持這種場景,通過--autogenerate檢測模型與數(shù)據(jù)庫的差異,如果模型定義與當(dāng)前數(shù)據(jù)庫狀態(tài)一致,生成的遷移腳本就是空的。然后,我未來的所有變更都可以從這個一致的狀態(tài)開始,通過Alembic來管理。

排查現(xiàn)有數(shù)據(jù)庫時,我又發(fā)現(xiàn)一個坑:原表里有些字段是TEXT類型,但存儲的其實是JSON字符串。在舊代碼里,每次查詢出來都要用json.loads()解析。這應(yīng)該在模型層就處理好。SQLAlchemy提供了JSON類型(配合sqlite3json1擴(kuò)展)或者用自定義類型,這給了我優(yōu)化數(shù)據(jù)結(jié)構(gòu)的機會。

核心實現(xiàn)第一步:定義數(shù)據(jù)模型

我決定不完全依賴automap,而是結(jié)合數(shù)據(jù)庫現(xiàn)狀和業(yè)務(wù)邏輯,手動定義模型。這樣雖然初期工作量稍大,但模型更清晰、更可控。

首先,我使用sqlite3命令行查看了表結(jié)構(gòu):

.schema device_log

輸出大概是:

CREATE TABLE device_log (
    id INTEGER PRIMARY KEY,
    device_id TEXT,
    status TEXT,
    log_time TEXT,
    raw_data TEXT
);

我發(fā)現(xiàn)log_time存的是文本,raw_data里是JSON字符串。我新建了一個models.py文件來定義模型。

這里有個坑:SQLite默認(rèn)不強制數(shù)據(jù)類型(Type Affinity),你甚至可以把字符串存到聲明為INTEGER的列里。但SQLAlchemy的模型定義是強類型的,為了兼容,我決定在定義模型時,嚴(yán)格按照當(dāng)前實際存儲的數(shù)據(jù)類型來定義,避免初始化時就出錯。對于log_time,我暫時還是用String,后續(xù)再考慮轉(zhuǎn)為DateTime。對于raw_data,我決定使用SQLAlchemy的JSON類型,這需要SQLite支持json1擴(kuò)展(現(xiàn)代Python版本基本都支持)。

# models.py
from sqlalchemy import Column, Integer, String, JSON, DateTime
from sqlalchemy.ext.declarative import declarative_base
import json

Base = declarative_base()

class DeviceLog(Base):
    __tablename__ = 'device_log'

    id = Column(Integer, primary_key=True)
    device_id = Column(String(64), nullable=False, index=True)  # 加了索引,因為常按設(shè)備查詢
    status = Column(String(32), nullable=False)
    log_time = Column(String(32))  # 暫時保持String,兼容舊數(shù)據(jù)
    raw_data = Column(JSON)  # 使用JSON類型,SQLAlchemy會自動處理序列化/反序列化

    # 添加一個屬性,方便訪問解析后的raw_data
    @property
    def data(self):
        # 如果raw_data已經(jīng)是dict(用了JSON類型后),直接返回,否則嘗試解析
        if isinstance(self.raw_data, dict):
            return self.raw_data
        try:
            return json.loads(self.raw_data) if self.raw_data else {}
        except json.JSONDecodeError:
            return {}

注意,我特意為device_id添加了index=True。因為在業(yè)務(wù)查詢中,WHERE device_id = ?是非常頻繁的操作,原表沒有索引,這在數(shù)據(jù)增長后會是性能瓶頸。這個索引的添加,我會通過Alembic遷移來完成,而不是手動去數(shù)據(jù)庫創(chuàng)建。

核心實現(xiàn)第二步:初始化Alembic與空遷移

接下來是解決“先有數(shù)據(jù)庫,后有模型”的遷移初始化問題。我按照Alembic官方文檔操作:

  • 安裝必要包:pip install sqlalchemy alembic
  • 在項目根目錄初始化Alembic環(huán)境:alembic init alembic
  • 修改alembic.ini中的數(shù)據(jù)庫連接字符串,指向我的data.db
  • 修改alembic/env.py,導(dǎo)入我的Base元數(shù)據(jù),并設(shè)置target_metadata = Base.metadata

關(guān)鍵步驟來了:生成初始遷移腳本。我運行:

alembic revision --autogenerate -m "Initial migration based on existing schema"

Alembic會比較我的模型定義(Base.metadata)和當(dāng)前數(shù)據(jù)庫(data.db)的差異。因為我剛才定義模型時,刻意保持了與現(xiàn)有表結(jié)構(gòu)的一致(除了raw_data用了JSON類型,但SQLite底層存儲還是TEXT,Alembic的檢測可能忽略這種同質(zhì)變化),所以生成的遷移腳本upgrade函數(shù)很可能是空的。這正是我想要的——一個代表當(dāng)前狀態(tài)的基準(zhǔn)點。

我打開生成的遷移文件(在alembic/versions/下),確認(rèn)upgrade()downgrade()函數(shù)確實為空,或者只包含一些無關(guān)緊要的操作。然后,我必須執(zhí)行這個遷移,以在Alembic的特殊表alembic_version中記錄這個版本,這樣Alembic才知道當(dāng)前數(shù)據(jù)庫處于這個“初始”狀態(tài)。

alembic upgrade head

執(zhí)行后,用數(shù)據(jù)庫工具查看,會發(fā)現(xiàn)多了一個alembic_version表,里面有一條記錄,版本號對應(yīng)剛才生成的遷移腳本。

核心實現(xiàn)第三步:通過遷移添加索引與優(yōu)化

現(xiàn)在,Alembic已經(jīng)接管了數(shù)據(jù)庫版本。我可以開始改進(jìn)模型,并通過遷移來應(yīng)用這些改進(jìn)了。

首先,我實現(xiàn)之前計劃的優(yōu)化:為device_id添加索引,以及將log_timeString改為DateTime(這需要數(shù)據(jù)轉(zhuǎn)換)。

修改模型 (models.py):

class DeviceLog(Base):
    __tablename__ = 'device_log'
    # ... 其他字段不變
    log_time = Column(DateTime)  # 改為DateTime類型
    # device_id的索引已經(jīng)在定義中,無需修改

注意,這里直接改了log_time的類型。Alembic會發(fā)現(xiàn)這個變化。

生成并檢查遷移腳本

alembic revision --autogenerate -m "Add index on device_id and change log_time to DateTime"

打開新生成的遷移文件,我看到了類似以下的內(nèi)容:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_index(op.f('ix_device_log_device_id'), 'device_log', ['device_id'], unique=False)
    # 注意這里!Alembic檢測到類型變化,生成了alter_column操作
    op.alter_column('device_log', 'log_time',
                   existing_type=sa.TEXT(),
                   type_=sa.DateTime(),
                   existing_nullable=True)
    # ### end Alembic commands ###

這里有個大坑! op.alter_column對于SQLite是有限支持的。SQLite本身不支持直接修改列類型(ALTER TABLE ... ALTER COLUMN)。Alembic會通過一個復(fù)雜的過程(創(chuàng)建新表、復(fù)制數(shù)據(jù)、刪除舊表、重命名)來模擬這個操作。這對于有數(shù)據(jù)的表是危險的,尤其是類型轉(zhuǎn)換(TEXT -> DateTime)可能失敗。

手動編輯遷移腳本,安全處理數(shù)據(jù)轉(zhuǎn)換。我不能完全依賴自動生成的腳本。我需要修改這個遷移,確保數(shù)據(jù)轉(zhuǎn)換是安全可控的。

import sqlalchemy as sa
from alembic import op
import sqlite3
from datetime import datetime

# revision identifiers, used by Alembic.
revision = 'xxxxxx'
down_revision = 'yyyyyy'

def upgrade():
    # 1. 先添加索引,這個操作SQLite原生支持,是安全的
    op.create_index(op.f('ix_device_log_device_id'), 'device_log', ['device_id'], unique=False)

    # 2. 處理log_time列類型轉(zhuǎn)換。我們需要自定義邏輯。
    conn = op.get_bind()

    # 首先,讀取所有現(xiàn)有的id和log_time文本
    rows = conn.execute(sa.text("SELECT id, log_time FROM device_log WHERE log_time IS NOT NULL")).fetchall()

    # 創(chuàng)建一個臨時字典,存儲解析后的時間。如果解析失敗,則置為None
    updates = []
    for row_id, time_str in rows:
        dt = None
        # 嘗試解析舊數(shù)據(jù)中可能的時間格式
        for fmt in ('%Y-%m-%d %H:%M:%S', '%Y/%m/%d %H:%M:%S', '%Y-%m-%dT%H:%M:%S'):
            try:
                dt = datetime.strptime(time_str, fmt)
                break
            except ValueError:
                continue
        updates.append((row_id, dt))

    # SQLite的ALTER COLUMN不支持類型變更,Alembic會模擬。我們讓它執(zhí)行。
    # 但為了數(shù)據(jù)正確,我們在Alembic操作后,手動更新數(shù)據(jù)。
    # 注意:Alembic的模擬操作會創(chuàng)建新表、復(fù)制數(shù)據(jù),但復(fù)制時log_time列的數(shù)據(jù)還是文本。
    # 所以我們需要在Alembic的alter_column操作之后,再執(zhí)行我們的更新。

    # 實際上,更安全的做法是分步遷移,這里為了示例,我們采用一個策略:
    # 先讓Alembic執(zhí)行它的模擬alter操作(結(jié)構(gòu)變更),然后我們手動更新數(shù)據(jù)。
    # 但Alembic的op.alter_column在SQLite環(huán)境下是一個大事務(wù),我們不能在中間插入。
    # 因此,我選擇另一種方法:禁用Alembic的自動alter,完全手動處理。

    # 注釋掉自動生成的alter_column行
    # op.alter_column('device_log', 'log_time',
    #                existing_type=sa.TEXT(),
    #                type_=sa.DateTime(),
    #                existing_nullable=True)

    # 手動執(zhí)行SQLite的列變更流程(簡化版,假設(shè)沒有復(fù)雜約束)
    # 步驟略復(fù)雜,在實際項目中,我可能會選擇保持TEXT類型,在應(yīng)用層進(jìn)行解析。
    # 鑒于篇幅和示例的清晰性,我決定在本文中采取一個折中方案:
    # 保留log_time為String,但添加一個DateTime的屬性方法,并確保新數(shù)據(jù)按ISO格式存儲。
    # 這是一個重要的實戰(zhàn)決策:當(dāng)數(shù)據(jù)遷移風(fēng)險過高時,在應(yīng)用層兼容是更穩(wěn)妥的選擇。

    # 所以,我回滾了模型中對log_time的修改,仍然使用String。
    # 并在DeviceLog類中添加一個方法:
    # @property
    # def log_datetime(self):
    #     # ... 解析self.log_time字符串為datetime對象,提供None安全
    pass

def downgrade():
    op.drop_index(op.f('ix_device_log_device_id'), table_name='device_log')
    # 類型改回操作也相應(yīng)移除

經(jīng)過這番思考,我意識到在現(xiàn)有數(shù)據(jù)質(zhì)量不明的情況下,強行改變底層列類型風(fēng)險太大。我調(diào)整了方案:保持?jǐn)?shù)據(jù)庫列類型不變,在ORM層提供類型化的訪問接口。這是很多遺留系統(tǒng)重構(gòu)的實用技巧。我修改了模型,放棄直接修改Column類型,而是通過hybrid_property或普通屬性方法來提供datetime對象。

最終,我重新生成了一個只包含創(chuàng)建索引的遷移,并執(zhí)行它。

alembic upgrade head

這樣,性能索引加上了,數(shù)據(jù)結(jié)構(gòu)的風(fēng)險也避免了。

完整代碼示例

以下是核心模塊的最終版本代碼,可以直接運行(需要先安裝sqlalchemyalembic,并有一個名為data.db的SQLite數(shù)據(jù)庫,其中包含device_log表)。

models.py:

from sqlalchemy import Column, Integer, String, JSON, DateTime, create_engine, Index
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import json
from datetime import datetime

Base = declarative_base()

class DeviceLog(Base):
    __tablename__ = 'device_log'

    id = Column(Integer, primary_key=True)
    device_id = Column(String(64), nullable=False)
    status = Column(String(32), nullable=False)
    log_time = Column(String(32))  # 保持文本存儲,兼容性第一
    raw_data = Column(JSON, default=dict)  # 使用JSON類型,默認(rèn)空字典

    # 定義索引,注意這里只是元數(shù)據(jù),實際創(chuàng)建需要Alembic遷移
    __table_args__ = (Index('ix_device_log_device_id', 'device_id'), )

    @property
    def data(self):
        """獲取解析后的raw_data"""
        if isinstance(self.raw_data, dict):
            return self.raw_data
        try:
            return json.loads(self.raw_data) if self.raw_data else {}
        except json.JSONDecodeError:
            return {}

    @property
    def log_datetime(self):
        """將log_time字符串轉(zhuǎn)換為datetime對象,安全版本"""
        if not self.log_time:
            return None
        # 嘗試常見格式
        for fmt in ('%Y-%m-%d %H:%M:%S', '%Y/%m/%d %H:%M:%S', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S.%f'):
            try:
                return datetime.strptime(self.log_time, fmt)
            except ValueError:
                continue
        # 如果都無法解析,記錄警告或返回None
        # import logging
        # logging.warning(f"無法解析時間字符串: {self.log_time}")
        return None

    @log_datetime.setter
    def log_datetime(self, dt: datetime):
        """設(shè)置datetime,統(tǒng)一存儲為ISO格式字符串"""
        if dt is None:
            self.log_time = None
        else:
            # 存儲為ISO格式,便于解析和排序
            self.log_time = dt.isoformat(sep=' ', timespec='seconds')  # 例如 '2023-10-27 14:30:00'

# 數(shù)據(jù)庫連接和會話工廠
engine = create_engine('sqlite:///data.db', echo=False)  # echo=True可查看SQL日志
SessionLocal = sessionmaker(bind=engine)

def get_db():
    """依賴注入使用的會話獲取函數(shù)"""
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

使用示例 (example_usage.py):

from models import DeviceLog, get_db
from datetime import datetime

# 插入新數(shù)據(jù)
with next(get_db()) as db:
    new_log = DeviceLog(
        device_id="device_001",
        status="online",
        log_datetime=datetime.now(),  # 使用setter
        raw_data={"temperature": 23.5, "humidity": 60}  # 直接傳字典
    )
    db.add(new_log)
    db.commit()
    print(f"插入記錄ID: {new_log.id}")

# 查詢數(shù)據(jù)
with next(get_db()) as db:
    logs = db.query(DeviceLog).filter(DeviceLog.device_id == "device_001").order_by(DeviceLog.log_time).all()
    for log in logs:
        print(f"ID: {log.id}, Status: {log.status}")
        print(f"  時間對象: {log.log_datetime}")  # 使用property獲取datetime
        print(f"  原始數(shù)據(jù): {log.data}")  # 使用property獲取解析后的dict
        print(f"  存儲的時間文本: {log.log_time}")

Alembic遷移目錄 (alembic/) 是運行alembic init alembic命令生成的,需要根據(jù)項目配置env.pyalembic.ini。

踩坑記錄

1.Alembic空遷移執(zhí)行失敗:第一次執(zhí)行alembic upgrade head時,報錯Target database is not up to date。這是因為我沒有理解alembic_version表的作用。在已有數(shù)據(jù)庫上初始化時,必須先“標(biāo)記”當(dāng)前狀態(tài)。解決方案就是生成一個與當(dāng)前狀態(tài)一致的模型,創(chuàng)建一個空遷移,并執(zhí)行它,讓Alembic記錄這個基準(zhǔn)版本。

2.SQLite的ALTER COLUMN陷阱:如正文所述,Alembic為SQLite生成的alter_column操作是模擬的,涉及表重建。對于有重要數(shù)據(jù)且列類型轉(zhuǎn)換復(fù)雜的場景,這是高風(fēng)險操作。我踩的坑是盲目信任自動生成的腳本,差點導(dǎo)致數(shù)據(jù)丟失。

解決方法:仔細(xì)審查針對SQLite生成的遷移腳本,對于復(fù)雜的數(shù)據(jù)轉(zhuǎn)換,要么手動編寫安全的數(shù)據(jù)遷移邏輯,要么像我一樣,選擇保持?jǐn)?shù)據(jù)庫列類型不變,在應(yīng)用層進(jìn)行適配。

3.JSON字段的默認(rèn)值:在模型中將raw_data = Column(JSON)后,插入新記錄時如果沒提供raw_data,默認(rèn)會是None。但在業(yè)務(wù)邏輯中,我希望它默認(rèn)是空字典{}。直接設(shè)置default={}會導(dǎo)致所有實例共享同一個字典引用(可變默認(rèn)值陷阱)。

解決方法:使用default=dict,或者使用default=lambda: {},這樣每次創(chuàng)建新實例時都會生成一個新的空字典。

4.連接未關(guān)閉導(dǎo)致數(shù)據(jù)庫鎖:在舊代碼和我的新代碼早期版本中,如果異常發(fā)生,連接可能沒有正確關(guān)閉。在Web應(yīng)用或多線程環(huán)境下,這容易導(dǎo)致sqlite3.OperationalError: database is locked

解決方法:使用上下文管理器(with語句)或類似FastAPI的Depends(get_db)模式來確保會話在任何情況下都能正確關(guān)閉。我在get_db函數(shù)中使用了try...finally來保證。

小結(jié)

這次重構(gòu)讓我深刻體會到,即使是簡單的SQLite,在項目規(guī)模增長后,也需要用ORM和遷移工具這樣的“重型裝備”來管理。核心收獲是:用ORM定義清晰的數(shù)據(jù)模型,用遷移工具記錄每一次結(jié)構(gòu)變更,兩者結(jié)合是維護(hù)數(shù)據(jù)庫健康度的最佳實踐

下一步,我可以探索SQLAlchemy更高級的特性,如異步IO(asyncpg/aiosqlite)、復(fù)雜查詢優(yōu)化,并將這套模式應(yīng)用到更復(fù)雜的PostgreSQL項目中。

以上就是Python項目重構(gòu)SQLite數(shù)據(jù)庫的實戰(zhàn)記錄的詳細(xì)內(nèi)容,更多關(guān)于Python重構(gòu)SQLite數(shù)據(jù)庫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • matplotlib畫混淆矩陣與正確率曲線的實例代碼

    matplotlib畫混淆矩陣與正確率曲線的實例代碼

    混淆矩陣也稱誤差矩陣,是表示精度評價的一種標(biāo)準(zhǔn)格式,下面這篇文章主要給大家介紹了關(guān)于matplotlib畫混淆矩陣與正確率曲線的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • PyTorch模型訓(xùn)練優(yōu)化、FastAPI跨域配置與Vue響應(yīng)式交互的手寫數(shù)字識別實踐

    PyTorch模型訓(xùn)練優(yōu)化、FastAPI跨域配置與Vue響應(yīng)式交互的手寫數(shù)字識別實踐

    本文詳細(xì)介紹了手寫數(shù)字識別項目的前后端開發(fā)流程,包括環(huán)境搭建、代碼實現(xiàn)、操作步驟及問題解決,前端使用Vue,后端使用FastAPI,模型使用PyTorch的LeNet5,項目涵蓋了從數(shù)據(jù)預(yù)處理、模型訓(xùn)練到部署的全過程,旨在掌握全流程開發(fā)邏輯并獲取可復(fù)用的圖像分類項目流程
    2026-02-02
  • Python中range()與np.arange()的具體使用

    Python中range()與np.arange()的具體使用

    本文主要介紹了Python中range()與np.arange()的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • wxPython的安裝圖文教程(Windows)

    wxPython的安裝圖文教程(Windows)

    下面小編就為大家分享一篇wxPython的安裝圖文教程(Windows),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • python自動化測試中APScheduler?Flask的應(yīng)用示例

    python自動化測試中APScheduler?Flask的應(yīng)用示例

    這篇文章主要為大家介紹了python自動化測試中APScheduler?Flask的應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Python手搓郵件發(fā)送客戶端

    Python手搓郵件發(fā)送客戶端

    這篇文章主要為大家詳細(xì)介紹了如何使用Python手搓郵件發(fā)送客戶端,支持發(fā)送郵件,附件,定時發(fā)送以及個性化郵件正文,感興趣的可以了解下
    2025-01-01
  • Python 獲取指定文件夾下的目錄和文件的實現(xiàn)

    Python 獲取指定文件夾下的目錄和文件的實現(xiàn)

    這篇文章主要介紹了Python 獲取指定文件夾下的目錄和文件的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 在Python中用split()方法分割字符串的使用介紹

    在Python中用split()方法分割字符串的使用介紹

    這篇文章主要介紹了在Python中用split()方法分割字符串的使用介紹,是Python入門中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-05-05
  • python實現(xiàn)在pandas.DataFrame添加一行

    python實現(xiàn)在pandas.DataFrame添加一行

    下面小編就為大家分享一篇python實現(xiàn)在pandas.DataFrame添加一行,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-04-04
  • Python光學(xué)仿真之對光的干涉理解學(xué)習(xí)

    Python光學(xué)仿真之對光的干涉理解學(xué)習(xí)

    這篇文章主要為大家介紹了Python光學(xué)仿真之對光的干涉理解學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2021-10-10

最新評論

临湘市| 普洱| 蒙阴县| 广州市| 江源县| 凤山市| 威远县| 衢州市| 青川县| 南京市| 屯留县| 惠来县| 儋州市| 柯坪县| 五指山市| 虹口区| 河池市| 抚州市| 古蔺县| 黎川县| 当阳市| 那曲县| 绥棱县| 临泉县| 万山特区| 黄浦区| 凭祥市| 景宁| 云林县| 陇西县| 龙井市| 竹溪县| 滦平县| 广南县| 芜湖县| 法库县| 佛学| 高碑店市| 宽城| 威远县| 沭阳县|