HBase数据高效导入Python:从原理到实战到优化全解析159
在大数据时代,HBase作为一款高可靠性、高性能、面向列的分布式数据库,在海量数据的存储和实时查询场景中扮演着关键角色。而Python,凭借其强大的数据处理能力、丰富的科学计算库以及简洁易用的语法,已成为数据科学家、工程师和分析师的首选语言。当我们需要对HBase中存储的TB甚至PB级别的数据进行深度分析、机器学习模型训练、实时报表生成或与其他Python生态系统集成时,将HBase数据高效地导入Python环境就成为了一个核心任务。
本文将作为一份详尽的指南,深入探讨HBase数据导入Python的各种策略、工具、实战代码以及性能优化技巧。我们将从HBase与Python的连接原理出发,逐步介绍多种行之有效的数据导入方案,旨在帮助读者根据具体需求选择最合适的路径,并解决实际操作中可能遇到的问题。
一、HBase与Python:连接的桥梁与挑战
HBase是基于Java构建的,运行在Hadoop分布式文件系统(HDFS)之上,其原生API主要面向Java。而Python作为一门解释型语言,虽然通过Jython等工具可以与Java生态进行有限的互操作,但在大数据场景下直接调用原生Java API通常不够高效和便捷。因此,我们需要通过“桥梁”来连接HBase和Python。
主要的挑战在于:
语言不兼容:Java与Python的API差异。
数据序列化/反序列化:HBase存储的数据是字节数组,导入Python后需要正确解码。
网络传输效率:如何高效地从分布式HBase集群传输大量数据到Python环境。
大数据量处理:Python单机内存限制,需要考虑分布式处理或分批导入。
为克服这些挑战,业界发展出了多种连接和导入策略。
二、HBase数据导入Python的几种核心策略
我们将主要介绍以下四种策略:
通过Thrift接口与HappyBase库:最直接的Python客户端连接方式。
通过Apache Phoenix与SQL连接:利用SQL层简化数据查询。
通过Apache Spark(PySpark)进行分布式处理:大数据量导入与处理的首选。
HBase数据批量导出到HDFS,再由Python读取:适用于离线分析的批量数据导入。
2.1 策略一:使用Thrift接口与HappyBase库(直连方案)
HBase通过Apache Thrift提供了一个语言无关的RPC(Remote Procedure Call)接口,允许非Java客户端与HBase服务进行通信。HappyBase是Python社区中一个非常流行且易于使用的HBase客户端库,它基于Thrift实现。
2.1.1 环境准备
安装HBase Thrift Server:确保HBase集群上已启动Thrift Server。通常通过命令 `hbase thrift start` 启动,或配置``开启。
安装HappyBase:
pip install happybase
2.1.2 代码实战
以下示例展示了如何连接HBase,并扫描数据:import happybase
import pandas as pd
# HBase连接参数
HBASE_HOST = 'your_hbase_thrift_server_host' # 例如:'localhost' 或 '192.168.1.100'
HBASE_PORT = 9090 # Thrift Server默认端口
TABLE_NAME = 'your_table_name'
def connect_hbase():
"""建立HBase连接"""
try:
connection = (host=HBASE_HOST, port=HBASE_PORT, timeout=60000)
()
print(f"Successfully connected to HBase at {HBASE_HOST}:{HBASE_PORT}")
return connection
except Exception as e:
print(f"Error connecting to HBase: {e}")
return None
def scan_hbase_data(connection, table_name, column_families=None, row_start=None, row_stop=None, batch_size=1000):
"""
扫描HBase表数据并返回一个生成器,适用于迭代处理大数据。
:param connection: HappyBase连接对象
:param table_name: 要扫描的表名
:param column_families: 要查询的列族列表 (e.g., ['cf1', 'cf2'])
:param row_start: 起始行键 (inclusive)
:param row_stop: 结束行键 (exclusive)
:param batch_size: 每次从HBase获取的行数,用于优化网络传输
:return: 包含字典形式数据的生成器
"""
table = (table_name)
scanner_kwargs = {}
if column_families:
scanner_kwargs['columns'] = [('utf-8') for cf in column_families] # HappyBase需要bytes
if row_start:
scanner_kwargs['row_start'] = ('utf-8')
if row_stop:
scanner_kwargs['row_stop'] = ('utf-8')
print(f"Scanning table '{table_name}' with parameters: {scanner_kwargs}")
for key, data in (batch_size=batch_size, scanner_kwargs):
decoded_data = {
'row_key': ('utf-8') # 行键通常需要解码
}
for col_name, col_value in ():
try:
# 列名和列值都需要解码,根据实际数据类型判断
decoded_col_name = ('utf-8')
decoded_col_value = ('utf-8') # 假设为UTF-8字符串
decoded_data[decoded_col_name] = decoded_col_value
except UnicodeDecodeError:
# 如果不是UTF-8字符串,可能需要其他解码方式或直接保留bytes
decoded_data[('utf-8')] = col_value # 此时保留bytes或尝试其他解码
# print(f"Warning: Could not decode {col_name} to UTF-8. Retaining as bytes.")
except Exception as e:
print(f"Error processing column {col_name}: {e}")
decoded_data[('utf-8')] = col_value # Fallback
yield decoded_data
def process_and_display_data(data_generator, limit=10):
"""处理并显示数据,或转换为Pandas DataFrame"""
all_rows = []
count = 0
for row in data_generator:
(row)
count += 1
if count >= limit:
break
if all_rows:
df = (all_rows)
print("--- Sample Data (DataFrame) ---")
print(())
print(f"Total rows processed: {count}")
return df
else:
print("No data retrieved.")
return ()
if __name__ == "__main__":
connection = None
try:
connection = connect_hbase()
if connection:
# 示例:扫描'my_cf:name'和'my_cf:age'列族
# 请根据你的HBase表实际结构修改
data_generator = scan_hbase_data(connection,
table_name=TABLE_NAME,
column_families=['my_cf'],
batch_size=2000)
df = process_and_display_data(data_generator, limit=5000)
# 在这里可以对df进行进一步的分析、清洗或模型训练
# print(())
finally:
if connection:
()
print("HBase connection closed.")
2.1.3 优缺点分析
优点:
简单易用:HappyBase API设计简洁,上手快。
直接连接:不需要额外的中间件(如Spark集群)。
灵活:支持各种HBase操作,如`get`、`put`、`delete`、`scan`。
适用于中小规模数据或特定行键查询:对于数据量不是特别巨大的全表扫描或基于行键范围的查询,性能良好。
缺点:
性能瓶颈:对于TB级别以上的大规模全表扫描,Thrift Server可能会成为瓶颈,单线程处理能力有限。所有数据需要通过单个Thrift Server传输,无法充分利用HBase集群的分布式优势。
内存限制:如果一次性将大量数据加载到Python内存中,容易导致内存溢出。需要分批处理。
缺少高层抽象:HappyBase只提供了底层的HBase操作,不直接支持SQL查询或高级数据分析功能。
2.2 策略二:通过Apache Phoenix与SQL连接
Apache Phoenix是HBase上的一个SQL层,它提供了标准的JDBC API,允许用户通过SQL语句来查询和管理HBase数据。对于习惯使用SQL进行数据操作的用户,Phoenix极大地简化了HBase的使用。
2.2.1 环境准备
安装并启动Apache Phoenix:在HBase集群上部署Phoenix。
创建Phoenix表:通过Phoenix命令行或JDBC连接创建SQL表,它会在HBase上生成对应的物理表。
安装Python JDBC桥接库:Python需要一个库来连接Java的JDBC驱动。常用的有`jaydebeapi`。
pip install jaydebeapi JPype1
2.2.2 代码实战 (示例,通常需要配置Java环境)
连接Phoenix并执行SQL查询:import jaydebeapi
import pandas as pd
import os
# 配置Java环境,需要JPype1以及Java Home路径
# export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
# export CLASSPATH=$CLASSPATH:/path/to/ # Phoenix Thin Client JAR包路径
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server # for
# Phoenix Thin Client JDBC URL
PHOENIX_THIN_JDBC_URL = "jdbc:phoenix:thin:url=your_phoenix_query_server_host:8765" # 替换为你的Query Server地址
PHOENIX_THIN_DRIVER = ""
PHOENIX_CLIENT_JAR = "/path/to/" # 替换为你的Phoenix Thin Client JAR包路径
def connect_phoenix():
"""建立Phoenix连接"""
try:
# 确保CLASSPATH中包含了Phoenix的JDBC驱动
# 也可以在jars参数中指定JAR包路径
conn = (
PHOENIX_THIN_DRIVER,
PHOENIX_THIN_JDBC_URL,
jars=PHOENIX_CLIENT_JAR,
# 其他JVM参数,如JVM内存设置等
# jpype_args=['-={}'.format(PHOENIX_CLIENT_JAR)]
)
print(f"Successfully connected to Phoenix via {PHOENIX_THIN_JDBC_URL}")
return conn
except Exception as e:
print(f"Error connecting to Phoenix: {e}")
print("Please ensure JAVA_HOME and CLASSPATH are correctly set and Phoenix Thin Client JAR is accessible.")
return None
def fetch_phoenix_data(connection, sql_query):
"""
执行SQL查询并获取数据。
:param connection: JayDeBeApi连接对象
:param sql_query: SQL查询语句
:return: Pandas DataFrame
"""
cursor = ()
try:
(sql_query)
columns = [desc[0] for desc in ]
rows = ()
df = (rows, columns=columns)
print(f"--- Fetched {len(df)} rows from Phoenix ---")
print(())
return df
except Exception as e:
print(f"Error executing Phoenix query: {e}")
return ()
finally:
()
if __name__ == "__main__":
# 确保在运行前设置了JAVA_HOME和CLASSPATH环境变量
# 例如:
# ['JAVA_HOME'] = '/usr/lib/jvm/java-8-openjdk-amd64'
# ['CLASSPATH'] = PHOENIX_CLIENT_JAR # 如果只有一个jar包
# 或者将jar包路径添加到现有CLASSPATH
# ['CLASSPATH'] = ('CLASSPATH', '') + ':' + PHOENIX_CLIENT_JAR
connection = None
try:
connection = connect_phoenix()
if connection:
# 替换为你的Phoenix表名和列名
sql = "SELECT id, name, age FROM YOUR_PHOENIX_TABLE WHERE age > 30 LIMIT 1000"
df = fetch_phoenix_data(connection, sql)
# 在这里可以对df进行进一步的分析
finally:
if connection:
()
print("Phoenix connection closed.")
2.2.3 优缺点分析
优点:
SQL友好:对于熟悉SQL的用户,学习成本低。
查询优化:Phoenix会将SQL查询转换为HBase的Scan和Filter,并进行查询优化,性能通常优于HappyBase的通用扫描。
索引支持:Phoenix支持二级索引,可以极大加速非行键查询。
与BI工具集成:由于提供JDBC/ODBC驱动,方便与Tableau、Superset等BI工具集成。
缺点:
复杂性:Phoenix本身的部署和管理相比直接使用HBase Thrift更复杂。
Python集成:`jaydebeapi`需要正确的Java环境和CLASSPATH配置,对Python开发者而言可能稍显繁琐。
全表扫描效率:尽管有优化,但对于无过滤条件的超大规模全表扫描,可能仍不如Spark等分布式计算框架高效。
2.3 策略三:通过Apache Spark(PySpark)进行分布式处理
对于PB级别的数据量,单个Python进程或Thrift Server都难以胜任。Apache Spark作为领先的分布式计算框架,通过其HBase连接器和PySpark API,可以高效地并行读取HBase数据并进行分布式处理。
2.3.1 环境准备
安装Spark集群:确保有一个可用的Spark集群。
安装HBase-Spark连接器:在Spark配置中或提交作业时,引入HBase-Spark连接器JAR包(例如 `shc-core` 或 `hbase-spark`)。
安装PySpark:
pip install pyspark
2.3.2 代码实战
以下示例展示了如何使用PySpark从HBase读取数据到DataFrame:from import SparkSession
from import col, lit
# HBase连接参数 (这些参数可能需要根据你的HBase集群配置进行调整)
# 注意:你需要确保HBase-Spark连接器JAR包在Spark的classpath中
# 通常在spark-submit命令中通过 --packages 或 --jars 指定
# 例如: --packages :hbase-spark:2.0.0-alpha --jars /path/to/
# 或在SparkSession中配置
HBASE_ZOOKEEPER_QUORUM = "your_zookeeper_quorum_host1,host2,host3" # HBase Zookeeper地址
HBASE_ZOOKEEPER_CLIENT_PORT = "2181" # Zookeeper端口
TABLE_NAME = "your_table_name"
COLUMN_FAMILIES = "cf1,cf2" # 以逗号分隔的列族列表
# 定义HBase catalog JSON,描述如何映射HBase表到Spark DataFrame
# 这里的"rowkey"是HBase的行键,"col1", "col2"是具体的列。
# 请根据你的HBase表结构修改
HBASE_CATALOG = f"""{{
"table": {{"namespace": "default", "name": "{TABLE_NAME}"}},
"rowkey": "key",
"columns": {{
"rowkey": {{"col": "rowkey", "type": "string"}},
"col1": {{"cf": "cf1", "col": "column1", "type": "string"}},
"col2": {{"cf": "cf1", "col": "column2", "type": "long"}},
"col3": {{"cf": "cf2", "col": "column3", "type": "double"}}
}}
}}"""
# 初始化SparkSession
# 确保HBase相关的JAR包已正确配置,例如通过spark-submit的 --packages 或 --jars
# 或者在SparkConf中添加额外的JARs,这需要更复杂的配置和管理
spark = \
.appName("HBaseToPySpark") \
.master("local[*]") \
.config("", ":hbase-spark:2.0.0-alpha") \
.config("", "2.0.0-alpha") \
.config("", HBASE_ZOOKEEPER_QUORUM) \
.config("", HBASE_ZOOKEEPER_CLIENT_PORT) \
.getOrCreate()
("WARN")
def read_hbase_to_df(spark_session, table_catalog):
"""
从HBase读取数据到Spark DataFrame。
:param spark_session: SparkSession对象
:param table_catalog: HBase表的Catalog JSON
:return: Spark DataFrame
"""
try:
df = \
.format("") \
.option("", False) \
.option("", table_catalog) \
.load()
print(f"--- Successfully loaded data from HBase table '{TABLE_NAME}' ---")
print("DataFrame Schema:")
()
print("Sample Data:")
(5)
print(f"Total rows: {()}")
return df
except Exception as e:
print(f"Error reading data from HBase: {e}")
print("Please check your HBase catalog, Zookeeper quorum, and Spark/HBase connector configuration.")
return None
if __name__ == "__main__":
try:
hbase_df = read_hbase_to_df(spark, HBASE_CATALOG)
if hbase_df:
# 可以在这里对DataFrame进行各种PySpark操作
# 例如:过滤、聚合、Join等
filtered_df = (col("col2") > 100).select("rowkey", "col1", "col2")
print("--- Filtered Data ---")
(5)
# 将Spark DataFrame转换为Pandas DataFrame (如果数据量不是太大)
# 注意:collect() 会将所有数据拉到Driver内存,可能导致内存溢出
# for very large datasets, avoid collect() and process data in Spark itself
if () < 100000: # 示例:限制转换为Pandas的数据量
pandas_df = ()
print("--- Converted to Pandas DataFrame (head) ---")
print(())
else:
print("DataFrame too large to convert to Pandas for display.")
finally:
()
print("SparkSession stopped.")
2.3.3 优缺点分析
优点:
高性能与可伸缩性:利用Spark的分布式计算能力,并行读取和处理HBase数据,适用于海量数据。
丰富的API:PySpark提供了SQL、DataFrame API以及MLlib等丰富的库,方便进行复杂的数据分析、转换和机器学习。
容错性:Spark的弹性分布式数据集(RDD)和DataFrame具有容错机制。
与其他大数据组件集成:无缝与HDFS、Hive、Kafka等集成。
缺点:
环境复杂:需要部署和维护Spark集群,以及HBase-Spark连接器。
资源消耗:Spark集群运行需要大量计算和内存资源。
学习曲线:对于不熟悉Spark的用户,学习成本较高。
启动开销:Spark应用的启动时间相对较长,不适合超低延迟的单点查询。
2.4 策略四:HBase数据批量导出到HDFS,再由Python读取(离线方案)
对于不需要实时性,但数据量非常大且需要进行离线分析的场景,可以利用HBase自带的工具将数据批量导出到HDFS,然后Python再从HDFS读取数据。
2.4.1 流程概述
HBase Export:使用HBase的 `Export` 工具将表数据以SequenceFile格式导出到HDFS。
HDFS to Python:Python通过`hdfscli`、`pyarrow`等库直接从HDFS读取数据,或通过Spark读取。
2.4.2 代码实战
步骤1:HBase导出数据到HDFS (在HBase集群执行)
# 导出整个表
hbase your_table_name /user/hbase/export/your_table_data
# 导出特定时间范围的数据
# hbase your_table_name /user/hbase/export/your_table_data_time_range 1672531200000 1672617600000
步骤2:Python从HDFS读取数据
A. 使用PyArrow (推荐,更现代,支持多种文件格式)
确保HDFS集群安装了`hdfs`模块,并安装`pyarrow`:`pip install pyarrow`import as hdfs
import as pq
import pandas as pd
# HDFS连接参数
HDFS_HOST = 'your_hdfs_namenode_host'
HDFS_PORT = 8020 # HDFS默认端口
HDFS_EXPORT_PATH = '/user/hbase/export/your_table_data' # HBase导出的数据路径
def read_hdfs_sequence_file(hdfs_path):
"""
通过pyarrow读取HDFS上的SequenceFile。
注意:pyarrow直接读取HBase Export生成的SequenceFile可能比较复杂,
通常需要先将SequenceFile转换为Parquet或CSV等更友好的格式。
此处提供读取Parquet的示例。
如果HBase Export导出的是SequenceFile,可能需要先用Spark或MapReduce转换为Parquet。
"""
# 假设你已经将SequenceFile转换为Parquet文件
# 转换示例(使用Spark):
# ("").load(HDFS_EXPORT_PATH).("/user/hbase/export/")
fs = (host=HDFS_HOST, port=HDFS_PORT)
parquet_path = f"{HDFS_EXPORT_PATH}.parquet" # 假设已转换为Parquet
if (parquet_path):
print(f"Reading Parquet file from HDFS: {parquet_path}")
table = pq.read_table(parquet_path, filesystem=fs)
df = table.to_pandas()
print(f"Successfully loaded {len(df)} rows into Pandas DataFrame.")
print(())
return df
else:
print(f"Error: Parquet file not found at {parquet_path}. Please convert SequenceFile first.")
return ()
if __name__ == "__main__":
# 示例:假设HBase数据已转换为Parquet并存储在HDFS上
# 在实际操作中,HBase Export默认生成SequenceFile。
# 你可能需要额外的MapReduce或Spark作业来将SequenceFile转换为Parquet或CSV,
# 以便PyArrow或其他Python库更容易处理。
# 假设HBase Export导出的SequenceFile已经通过Spark转换为Parquet
# 例如,在Spark Shell或PySpark中执行:
# ("").load("/user/hbase/export/your_table_data").("/user/hbase/export/")
# 或者直接从HDFS读取CSV文件 (如果HBase Export能直接导出CSV或你手动转换了)
# with (f"{HDFS_EXPORT_PATH}.csv", 'rb') as f:
# df_csv = pd.read_csv(f)
df_from_hdfs = read_hdfs_sequence_file(HDFS_EXPORT_PATH)
# 进行后续分析
B. 使用`hdfscli` (更专注于HDFS文件操作)
`pip install hdfs`from hdfs import InsecureClient
import pandas as pd
import io
HDFS_URL = 'your_hdfs_namenode_host:50070' # HDFS Web UI端口
HDFS_EXPORT_PATH = '/user/hbase/export/' # 假设已导出为CSV
def read_hdfs_data_with_cli(hdfs_url, hdfs_path):
"""从HDFS读取数据,这里假设是CSV文件。"""
client = InsecureClient(hdfs_url)
try:
with (hdfs_path, encoding='utf-8') as reader:
df = pd.read_csv(reader)
print(f"Successfully loaded {len(df)} rows into Pandas DataFrame from HDFS.")
print(())
return df
except Exception as e:
print(f"Error reading from HDFS: {e}")
return ()
if __name__ == "__main__":
# 这种方法更适用于HBase数据已经通过其他方式(如Spark作业)转换为CSV或Parquet等易于Python读取的格式
df_from_hdfs_cli = read_hdfs_data_with_cli(HDFS_URL, HDFS_EXPORT_PATH)
2.4.3 优缺点分析
优点:
简单粗暴:HBase `Export`工具本身就是MapReduce作业,可以充分利用Hadoop集群的分布式能力。
适用于离线分析:对于需要定期进行大规模批处理分析的场景非常合适。
数据持久化:数据导出到HDFS后可以长期保存,供多次分析使用。
缺点:
非实时:数据有延迟,不适合需要最新数据的场景。
中间步骤:通常需要一个额外的Spark或MapReduce作业将HBase `Export`生成的SequenceFile转换为更易于Python处理的格式(如Parquet、ORC、CSV),增加了复杂性。
运维成本:需要HBase、HDFS集群的运维支持。
三、数据导入后的处理与优化
无论采用哪种策略,数据导入Python后,我们还需要考虑以下几个方面:
3.1 数据类型转换与解码
HBase中所有数据都存储为字节数组(`byte[]`)。导入Python后,需要根据原始数据类型进行解码(`decode('utf-8')`)、类型转换(`int()`, `float()`, `bool()`)或反序列化(例如,如果存储的是Protobuf或Avro)。确保所有列都转换成Python中合适的类型(例如,Pandas DataFrame支持的数值、字符串、日期等类型)。
3.2 内存管理与分批处理
对于大规模数据,一次性加载到Python内存会迅速耗尽资源。应采用以下策略:
迭代器/生成器:HappyBase的`scan`方法返回一个生成器,逐行处理数据。
`batch_size`:在HappyBase `scan`中设置,控制每次RPC调用获取的行数。
`chunksize`:如果转换为Pandas DataFrame,可以使用`pd.read_sql`或`pd.read_csv`的`chunksize`参数分块读取。
PySpark DataFrame:数据始终保持分布式,只有需要时才`collect()`或`toPandas()`。
3.3 性能优化
HBase表设计:
合理行键设计:确保查询的局部性和均匀的数据分布,避免热点问题。
列族规划:将常用列放在一起,减少IO。
二级索引:对于非行键查询,Phoenix的二级索引至关重要。
扫描优化:
指定列族/列:只获取需要的列,减少网络传输和HBase侧IO。
行键范围查询:利用`row_start`和`row_stop`缩小扫描范围。
过滤器(Filters):使用HBase原生过滤器(在HappyBase或Spark中配置)在HBase侧进行数据过滤,减少传输到Python的数据量。
资源配置:
为Thrift Server分配足够的内存。
为Spark集群配置足够的Executor、内存和CPU。
四、选择合适的策略
选择最佳的HBase数据导入Python策略取决于你的具体需求:
小规模数据、交互式查询或特定行键查询: HappyBase (Thrift) 是最简单直接的选择。
需要SQL接口、有复杂过滤和聚合需求,或需要与BI工具集成: Apache Phoenix。
大规模(TB/PB级别)数据处理、需要分布式计算、机器学习或与Hadoop生态深度集成: Apache Spark (PySpark)。
离线分析、数据量巨大且对实时性要求不高: HBase批量导出到HDFS,再由Python(PyArrow/hdfscli或Spark)读取。
五、总结与展望
将HBase数据导入Python是大数据分析链路中的关键一环。本文详细介绍了通过HappyBase、Phoenix、PySpark以及HBase批量导出等多种策略,并提供了相应的代码示例和优缺点分析。理解这些策略的原理和适用场景,能够帮助你根据实际需求做出明智的技术选型。同时,合理的数据设计、编码解码、内存管理和性能优化是确保导入过程高效稳定不可或缺的考量。
随着大数据技术的不断演进,HBase与Python的集成方式也将持续优化。未来,我们可以期待更便捷、更高效的连接器和更智能的数据处理工具,进一步降低大数据开发的门槛,助力数据价值的深度挖掘。
2025-09-29
命令行PHP:探索在Windows环境运行PHP脚本的实践指南
https://www.shuihudhg.cn/134436.html
Java命令行运行指南:从基础到高级,玩转CMD中的Java程序与方法
https://www.shuihudhg.cn/134435.html
Java中高效统计字符出现频率与重复字数详解
https://www.shuihudhg.cn/134434.html
PHP生成随机浮点数:从基础到高级应用与最佳实践
https://www.shuihudhg.cn/134433.html
Java插件开发深度指南:构建灵活可扩展的应用架构
https://www.shuihudhg.cn/134432.html
热门文章
Python 格式化字符串
https://www.shuihudhg.cn/1272.html
Python 函数库:强大的工具箱,提升编程效率
https://www.shuihudhg.cn/3366.html
Python向CSV文件写入数据
https://www.shuihudhg.cn/372.html
Python 静态代码分析:提升代码质量的利器
https://www.shuihudhg.cn/4753.html
Python 文件名命名规范:最佳实践
https://www.shuihudhg.cn/5836.html