3. Python基础知识:注释、变量以及数据类型、标识符和关键字、输入函数、输出函数、运算符、程序类型转换

好,咱们正式开始Python基础部分。说实话,很多搞CUDA的同学一开始觉得Python太“软”,不如C++硬核。但我在做GPU加速项目时,Python往往是那个“粘合剂”——数据预处理、模型搭建、结果可视化,全得靠它。所以,别小看这些基础,它们是后续并行计算的基石。

3.1 注释:给未来的自己留个便签

注释这东西,说白了就是代码里的“人话”。机器不看,但人要看。我见过最惨的案例是,自己写的代码三个月后看不懂了……嗯,从那以后我养成了写注释的习惯。

Python里注释有两种:

  • 单行注释:用 # 开头,整行都失效
  • 多行注释:用三个单引号 ''' 或三个双引号 """ 包裹
# 这是单行注释,我习惯在关键逻辑前加一行
print("Hello, CUDA!")  # 行尾也可以加注释

'''
这是多行注释
可以写好几行说明
比如解释这个函数为什么这么写
"""
当然双引号也行
'''
我的习惯:在写GPU核函数前的Python预处理部分,我会用注释标注“这段数据要转成float32”或者“这里要小心内存对齐”。别嫌麻烦,调试时能救命。

3.2 变量与数据类型:Python的“盒子”哲学

Python的变量,你可以想象成一个贴了标签的盒子。你不需要提前告诉盒子能装什么——它很智能,你放进去什么,它就是什么类型。

我刚开始从C++转Python时特别不习惯,总觉得变量得先声明类型才安心。后来发现,动态类型在快速原型开发时太爽了。

# 变量赋值,不需要声明类型
a = 10          # 整数
b = 3.14        # 浮点数
c = "CUDA"      # 字符串
d = True        # 布尔值
e = [1, 2, 3]   # 列表

Python常见的数据类型:

类型 名称 示例 说明
int 整数 42 任意大小,没有溢出问题
float 浮点数 3.14159 双精度,GPU里常用float32
str 字符串 "Hello" 不可变,支持索引和切片
bool 布尔值 True / False 注意首字母大写
list 列表 [1, 2, 3] 可变,可嵌套,类似数组
tuple 元组 (1, 2, 3) 不可变,常用于函数返回多个值
dict 字典 {"key": "value"} 键值对,查找极快
我曾经踩过的坑:在GPU内存分配时,Python的float默认是双精度(64位),但很多GPU操作需要单精度(32位)。如果不做类型转换,性能直接腰斩。所以,记得用 numpy.float32 显式指定。

3.3 标识符和关键字:起名字的艺术

标识符就是你给变量、函数、类起的名字。规则很简单:

  • 只能包含字母、数字、下划线
  • 不能以数字开头
  • 不能是Python关键字
  • 区分大小写(myVarmyvar 是两个东西)

Python的关键字大概有30多个,比如 ifforwhiledefclass 等等。你想想看,如果你给变量起名叫 class,Python就懵了——你到底是想定义类还是引用变量?

# 合法的标识符
my_variable = 10
var2 = 20
_longName = 30

# 不合法的标识符
# 2var = 10    # 数字开头,报错
# my-var = 10  # 有连字符,报错
# if = 10      # 关键字,报错
我的命名习惯:变量用下划线命名法(snake_case),比如 gpu_memory;类用驼峰命名法(CamelCase),比如 GpuKernel。团队协作时,统一风格比风格本身更重要。

3.4 输入函数:让程序学会“听”

input() 函数是Python从控制台获取用户输入的方式。注意,它返回的永远是字符串

name = input("请输入你的名字:")
print("你好," + name)

# 如果需要数字,必须手动转换
age = input("请输入年龄:")
age = int(age)  # 字符串转整数
print("明年你", age + 1, "岁")
我曾经踩过的坑:在写一个GPU数据加载脚本时,我直接用 input() 接收了文件路径,结果用户输入了带空格的路径,程序直接崩溃。后来我加了 strip() 去空格,再用 os.path.exists() 检查。永远不要相信用户的输入!

3.5 输出函数:让程序学会“说”

print() 是Python最常用的输出函数。它有很多小技巧,我挑几个常用的:

# 基本用法
print("Hello, World!")

# 多个参数,自动用空格分隔
print("GPU", "CUDA", "Python")  # 输出:GPU CUDA Python

# 指定分隔符
print("GPU", "CUDA", "Python", sep=" -> ")  # 输出:GPU -> CUDA -> Python

# 指定结束符(默认是换行)
print("加载中", end="...")
print("完成")  # 输出:加载中...完成

# 格式化输出(f-string,Python 3.6+)
name = "CUDA"
version = 12.0
print(f"当前版本:{name} {version}")  # 输出:当前版本:CUDA 12.0

我个人最常用f-string,简洁又直观。在调试GPU程序时,我经常用 print(f"blockIdx: {blockIdx.x}, threadIdx: {threadIdx.x}") 来定位问题。

3.6 运算符:计算的“工具包”

运算符就是用来做各种操作的符号。Python的运算符很丰富,咱们挑重点说:

类别 运算符 示例 说明
算术 + - * / // % ** 10 // 3 = 3 // 是整除,** 是幂运算
比较 == != < > <= >= 5 == 5True 返回布尔值
赋值 = += -= *= /= x += 1 等价于 x = x + 1 简化代码
逻辑 and or not True and FalseFalse 注意不是 &&||
成员 in not in "CUDA" in ["CUDA", "OpenCL"] 检查是否在序列中
身份 is is not a is None 检查是否是同一个对象
# 算术运算符
a = 10
b = 3
print(a + b)   # 13
print(a / b)   # 3.333...
print(a // b)  # 3(整除)
print(a ** b)  # 1000(10的3次方)

# 逻辑运算符
x = True
y = False
print(x and y)  # False
print(x or y)   # True
print(not x)    # False
重点提醒:在GPU编程中,// 整除和 % 取余非常常用。比如计算线程索引时,threadIdx.x // blockDim.x 可以算出当前线程在网格中的位置。这个技巧我几乎每个项目都用。

3.7 程序类型转换:让数据“变形”

类型转换就是把一种数据类型变成另一种。Python提供了内置函数来做这件事:

# 常见类型转换
a = "123"
b = int(a)      # 字符串 -> 整数,b = 123
c = float(a)    # 字符串 -> 浮点数,c = 123.0
d = str(456)    # 整数 -> 字符串,d = "456"
e = bool(1)     # 整数 -> 布尔值,e = True
f = list("abc") # 字符串 -> 列表,f = ['a', 'b', 'c']

类型转换有两种:

  • 隐式转换:Python自动帮你转。比如 3 + 0.5,整数3自动变成浮点数3.0
  • 显式转换:你手动调用函数转。比如 int("42")
我曾经踩过的坑:在读取GPU性能数据时,我从CSV文件读到的数字全是字符串。直接做数学运算?报错!必须先用 float() 转一下。还有一次,我把 None 转成了布尔值,结果 bool(None)False,导致一个条件判断出了bug。记住:0None、空字符串、空列表都会转成 False

好了,Python基础部分就到这里。这些内容看起来简单,但它们是后续所有并行计算代码的“砖瓦”。下一章咱们会深入控制流——if判断和循环,这些在GPU线程调度中会频繁用到。到时候见!