『Python 干货』#1 基础语法(简明)

访问博客查看 本文 最新内容,排版更美观ヾ(•ω•`)o 如有错误欢迎指出~

Python 系列学习笔记:

  • Python笔记 #1 基础语法
  • Python笔记 #2 NumPy
  • Python笔记 #3 Matplotlib

跟着贵系的暑培过了一遍语法,然而写代码时还是感到乏力,总觉得要打一遍才能记住,于是有了这篇博客。

本文部分内容参考了清华 AYF 同学的教程,部分参考了 Python 官方的一份 Tutorial,函数用法参考了 xyfJASON 的博客。本文将持续更新。

Python 特性

相比于 C 的编译型、弱类型、静态类型特点,Python 则是一种解释型、强类型、动态类型的语言。

交互式 vs 脚本

作为一种解释型语言,Python 不需要像 C 一样编译运行,它可以逐行运行代码,因此又分为交互式窗口运行与脚本运行两种模式:

  • 交互式窗口:在 CLI 输入 python,即可进入交互式窗口,输入一行代码即可运行。本文使用的交互式环境是 IPython,输入 ipython 即可呼出。
  • 脚本:在 CLI 输入 python xxx.py,就会依次执行整个脚本文件。本文使用的 IDE 是 PyCharm

Hello World

>>> print('Hello, world!') # 打印输出
Hello, world!

在交互式窗口,>>> 作为提示符,在 IPython 中则是 In [1]:。执行 exit() 或者按下 Ctrl+D 就能退出窗口。

注意到,这行代码中没有 ; 分号,可以直接运行而无需编译,字符串用了单引号,注释用 # 开始【与 C 不同】。

简单数据类型

变量类型

【与 C 不同】,Python 不用声明变量类型,解释器自动解释。

  • int:变长整数,默认是 4 字节,有需要时自动增长,用于高精度运算,还支持十六进制、八进制和二进制表示。
  • complex:自带的复数类型,表示为 real + imag*1j 的形式,虚部为 1 的时候不可省略 1,可以用 j 也可以用 J。实虚部分别为一个 float
  • float:8 字节浮点数,【相当于 C 的 double】。
  • bool:True 和 False,注意首字母大写,用作数值计算时与 C 一样视作 0 和 1。
  • NoneType: None,空值,常用于返回值、特判。

需要单独说明的是,Python 会存储所有的 -5 到 256 的整数,其他任何变量是这些值时,会被指向这个预先开好的内存,因此任何两个值为 5 的 int 变量都指向同⼀内存地址。

尽量用小写变量名(下划线法),这是 Python3 的主流命名方式。

运算符

这里列出常见的运算符,运算符可以重载,重载需要修改其对应的定义函数。

算术运算符

  • + - *【与 C 相同】
  • %35 % 4 == 3-35 % 4 == 135 % -4 == -1-35 % -4 == -3,【与 C 不同:负数对正数取模时返回正余数,而非像 C 那样返回负余数】
  • /__trudiv__,真除,得到 float 结果
  • //__floordiv__,除后向下取整(不是舍弃小数),得到 int 结果
  • **__pow__,幂运算

比较运算符

  • < <= > >= == != 【与 C 相同】

位运算符:

  • & | ^ ~ << >> 【与 C 相同】

赋值运算符

  • =:赋值号,不能被重载
  • += -= *= /= %= 【与 C 相同】
  • 注意 Python 中没有 ++ -- 的运算符,只能通过 += 1 实现

逻辑运算符

  • and or not 【类似 C 中的 && ||
  • 对于 andor,通常用于条件分支 bool 的判断,如果非要连接 int 变量,得到的结果不会直接转换为 bool,而是返回能够得出结果最后一个变量【与 C 中的短路类似】

三目运算符

  • a if cond else b:相当于 C 中的 cond ? a : b,注意其参数顺序【与 C 不同】,但更贴近自然语言

特殊条件运算符

  • in:被包含于,详见下文「容器」,返回 bool
  • not inin 的否定,返回 bool
  • is:判断两个变量的地址是否相同,不可重载,返回 bool
  • is not:判断两个变量地址是否不同,不可重载,返回 bool

字符串

Python 将字符串封装成了基本类型并处理了多种运算,带来许多便利,注意基本类型本身是不可修改的,所谓修改其实是将重新生成另一个字符串,再将其赋值给目标。

此外,Python 中没有单独的字符类型,单个字符将被视为长度为 1 的字符串。【与 C 不同】,可以用 "" 或者 '' 括起字符串。

下面是一些常用函数,设 str 是一个字符串:

  • str.title():返回单词首字母大写,其余字母小写(不管以前是不是大写)的字符串
  • str.upper()str.lower():返回全大/小写的字符串
  • str1 + str2:返回用加号拼接的字符串【与 C++ 的 string 类似】
  • str * 3:返回重复三遍拼接的字符串
  • 制表符 \t,换行符 \n【与 C 相同】
  • str.lstrip()str.rstrip()str.strip(): 返回删除开头/末尾/两端空白的字符串
  • str.replace(str1, str2):将字符串中的单词 str1 全部替换成 str2
  • str.split():以空格为分隔符把字符串拆成一个个单词,并返回包含这些单词的列表

此外还有一种跨行字符串,用 ’‘’”“” 括起来,由于脚本中顺序执行时不会输出变量值,这类字符串常用于跨行注释,特别是函数头注释。

输入输出与编码

此处说明几个重要函数:

  • len(obj):获取 obj 的长度,常用于获取字符串(注意是 Unicode,因此中文和英文字符都占 1 位)、字节串、容器的长度
  • str(a): 把数字(整型或浮点型) a 转换成字符串
  • chr(0x4f60):将整型变量 i 转化成单个字符
  • ord('你'):获取单个字符的编码(Unicode),注意在 Unicode 中,英文字母的编码有意设置与 ASCII 码一致

最常用的输出语句 print,本质上是将变量转化为字符串输出,在末尾自动换行。该函数可以有多个变量,变量间用 , 分隔,输出时会用空格隔开。

如果要在一句话中插入许多变量,这条 print 语句可能会很丑陋,因此 Python 中有三种格式化字符串的方法。

  • %:如 print('I am %d' % (age)),【与 C 类似】
  • str.format():如 print('hello, {}'.format(name))
  • f-string:如 print(f'hello,{name}.I am {age}')

其中 f-string 是 Python3.6 的新特性,最为直观便利。

此外,input('Press ENTER to continue') 是常见的一种输入语句,会显示对应的提示内容,读入内容以回车键结尾。

字节串

bytes 即是 Python 中的字节串,它表示最纯粹的二进制数据,【类似 C 的 unsigned char *】,但是在显示上它仅支持 ASCII 显示,因此用肉眼看显得有些不伦不类,通常它只存在于数据的处理过程中。

bytes 的构造与字符串类似,但是要加一个 b 做前导,如 print(b'\x41')

容器

Python 提供了一系列内置容器,它们如同 C++ 的 STL ⼀样,不过比 STL 的用法灵活得多。

同样先介绍几个重要函数:

  • type(obj):可以获取参数 obj 的类型
  • isinstance(obj, class_or_tuple):可以判断 obj 是不是类的实例
  • id(obj):获取 obj 的地址, a is b 等价于 id(a) == id(b)

列表 | List

列表(list)是很常用的容器,常被看作 Python 中的数组,但实际上【与 C++ 的 vector 类似】。设 lst 是一个列表:

基础操作

  • 定义列表:lst = [a, b, c, d] ,其中 a,b,c,d 等是列表的元素,类型可以不同
  • 构造空列表:直接写 []list()
  • 打印列表:print(lst)(会将列表中的元素列出,括在方括号里)
  • 访问元素:lst[3] , 下标从 0 开始。此外,还支持负数索引,-1 表示倒数第一个,-2 倒数第二个【与 C 不同】

修改、添加、删除元素

  • 修改:直接访问元素并赋值
  • lst.append(x):在列表末尾添加元素 x
  • lst.insert(idx, x):在列表索引 idx 处插入一个元素 x (插入后,x 的索引是 idx,其后的元素后移一格)
  • del lst[3]:删除指定元素(删除后,其后元素前移一格)
  • lst.pop():弹出并返回最后一个元素
  • lst.pop(idx):弹出并返回指定元素
  • lst.remove(x):删除第一个值为 x 的元素

组织列表

  • lst.sort()lst.sort(reverse = True):对列表排序,永久性修改顺序
  • sorted(lst)sorted(lst, reverse = True):返回排序后的列表,但不改变列表原有顺序
  • lst.reverse():翻转列表,永久性修改顺序
  • len(lst):返回列表长度,即元素个数(不论类型)

遍历列表

从头到尾遍历列表:for i in lst: 循环表达式

【与 C 不同】i 是列表元素,不是索引;循环结束后 i 停留为最后一个元素。

若要检查列表是否为空,可以用 if lst: 条件表达式,返回 bool

列表切片

  • lst[l:r]:返回一个列表,元素依次是 lst 列表的索引在左闭右开区间内的元素,省略 lr 则默认从头开始或到尾结束

  • lst[l:r:step]:指定步长为 step 切片,step 为 -1 时返回倒序

  • 可以用循环遍历列表切片:for i in lst[l:r]:

  • 复制列表:在切片中同时省略 lr,即返回从头到尾的列表,如 lst2 = lst1[:],而非 lst2=lst1,后者lst1lst2 实质是同一个列表【类似 C 的引用】

元组 | Tuple

元组就是元素值不可修改(弱意义上的,其中元素的元素可以被修改)的列表。设 tpl 是一个元组:

基础操作

  • 定义元组:tpl = (a, b, c, d),把列表定义中的方括号改成圆括号 () 即可
  • 定义时的小括号有时候可以省略,可以直接用 , 逗号构造元组【与 C 不同,没有逗号运算符】
  • 构造单元组:(1) 会被理解成表达式,要用 (1,)
  • 构造空元组,直接写 ()tuple(),但 (,) 会报错
  • 访问元素:tpl[3] , 下标从 0 开始。

遍历元组

for i in tpl: 和列表一样

修改元组

元组中元素的值不能修改,但是元组变量本身可以被赋值,这点与字符串类似。此外,如果元组的中的元素是可修改的,如 List,

元组解包

经典的 Python 交换赋值代码:a, b = b, a,利用了解包和逗号构造

集合 | Set

集合由一组无序、互不重复的元素构成(在数学上也是如此),在内部用哈希实现。设 st 是一个集合:

基础操作

  • 定义集合:st = {1, 2, 3},把列表定义的方括号改成花括号 {} 即可
  • 构造空集合:只能用 set(),因为 {} 的表达被空字典占用了
  • 由列表转集合:st = set([1, 1, 1, 2, 2, 3]),会自动去重,常用于去重列表的遍历 for i in set(lst):
  • 注意集合的元素必须是可 hash 的,不能为 list 这种可变容器变量

添加、删除元素

  • st.add(4):添加一个元素
  • st.remove(2):删除一个元素

字典 | Dictionary

字典是一系列「键值对」,用于存储一组有穷映射,可将任何 Python 对象作为值,【类似于更高端版本的 C++ 的 map】。

基础操作

  • 定义字典:dic = {'name': '张三', 'age': 18}花括号括起一系列键值对,键与值之间冒号 : 分隔,键值对之间逗号 , 分隔。
  • 访问元素:d['name'],用键访问
  • 注意字典的键必须是可 hash 的,不能为 list 或 set 这种可变容器变量,但可以是 tuple

添加、修改、删除

  • 添加:直接赋值即可(即使键本来不存在),如:dic['x'] = 0
  • 修改:dic['age'] = 18,直接赋值即可
  • 删除:del dic['age']

遍历字典

  • 遍历所有键值对:for k, v in dic.items():,其中 items() 返回键值对的列表
  • 遍历所有键:for k in dic.keys():,其中 keys() 返回键的列表,可省略
  • 由于 keys() 的本质是列表,各种对列表的操作也适用,如:for k in sorted(dic.keys()):if 'age' in dic.keys():
  • 遍历所有值:for v in dic.values():,其中 values() 返回值的列表

迭代器

前文提到的所有容器,包括字符串、字节串都是可迭代的,这意味着它们可以用 for 循环来遍历,也可以用生成式构造(下面介绍)。

但最为特殊的迭代器是 range 类型,作为数值列表,与列表有相似之处,但它实际上不占用内存(用于大循环时很节省空间)。下面是一些常用方法:

  • range(l, r):依次生成左闭右开区间中的整数【类似于 C++ 的 for(int i = l; i < r; i++)】,如果省略左端,会默认以 0 开始
  • range(l, r, step):指定步长为 step【类似于 C++ 的 for(int i = l; i < r; i += step)
  • min(range(l, r))max(range(l, r))sum(range(l, r)):返回数值列表的最小值、最大值、总和
  • for i in range(10): 的循环中改变了 i 的值,如 i += 1, 不会影响循环的次数,因为迭代器中的数值列表是不可修改的【与 C 不同】

生成式

使用生成式构造容器是非常常见、高效的操作,下面举几个例子:

  • a = list(range(10)):直接转化数值列表为基本列表
  • lst = [i ** 2 for i in a]:列表生成式
  • st = {x % 7 for x in a}:集合生成式
  • dic = {x ** 2: x for x in a}:字典生成式,保留 : 来分隔键与值
  • tpl = tuple(x + 1 for x in a):元组生成式,直接用 () 会被当成表达式

综上所述,生成式表达可以用三段式:表达式 for 循环变量 in 迭代对象 if 筛选条件,其中最后的筛选条件不一定要。

流程控制

Python 中不用大括号来显式地分块,而是用冒号配合缩进。代码块与代码块之间至少隔着一个空行表示结束。当一个代码块必须存在,又不想写任何语句的时候,可以写一个 pass 作为占位符。

条件分支

a = 1
if a == 1:
    print('a is 1')
elif a == 0:
    print('a is 0')
else:
    print('wtf is a?')

注意,elifelse 后面也要有冒号配合缩进,如果有多个条件,用 andor 逻辑运算符连接。

for 循环

前面所列的容器、数值列表都可以用于 for 循环迭代,比较特别的是字符串也可以迭代:

a = 'hello world'
for c in a:
    print(c) # 竖着打印出 hello world,因为 print 自带换行

此外,如果在数值列表的迭代中用不到迭代变量 i,仅作为迭代次数使用,可以用 _ 变量表达。

while 循环

a = 10
while 2 <= a < 20:    # 语法糖,这种写法是被建议使⽤的
    print(a)
    a -= 1            # 注意 Python 中没有⾃增 1 和⾃减 1 运算符

跳出循环可以用 breakcontinue【与 C 相同】

函数

基础函数

def fib(n):
    """函数文档注释"""
    current, then = 0, 1
    for _ in range(n):
        current, then = then, current + then
    return current

fib(10) # 调用函数

函数传参

函数传参本质上和赋值号是一样的,都是浅复制(指针指向)【类似 C 的形参】。

传入参数的数量可以不固定,但是必须指定默认值;也可以调换顺序,但必须指明对象。

def add(a, b = 0):
    print(a + b)

add(1)              # 参数 b 采用默认 0
add(b = 2, a = 1)   # 调换顺序,指明对象

当然,也可以传递列表等容器,但传递的也是列表的地址,在函数中修改同样会改变原列表,如果不想修改原列表可以用 [:] 传递切片。

传递任意数量的参数

  • 在形参前加星号 *,Python3 会创建一个该形参名称的元组
  • 在形参前加双星号 **,Python3 会创建一个该形参名称的字典

返回值

作为动态类型语言,函数返回值可以不固定,可以多个 return 在不同情况下返回不同值,或者没有 return(等价于 return None)。

函数模块调用

函数可以被存储在模块中被调用,模块是扩展名为 .py 的文件,包含函数的代码【类似于 C 的头文件】

  • 导入整个模块:使用 import pizza 导入,调用时使用 . 句点,如:pizza.make(16, 'green peppers')
  • 导入模块中特定函数:使用 from pizza import make, eat,调用时无需句点,直接用函数名
  • 导入特定函数别名:使用 from pizza import make as mk,调用时无需句点,直接用别名
  • 导入模块中所有函数:使用 from pizza import *,调用时无需句点,但是会污染命名空间,不建议使用

所有 import 都放在程序开头【类似于 C++ 的 #include<>】。

Lambda 匿名函数

和很多语言⼀样,Python 中可以使用 Lambda 匿名函数完成一些简单的逻辑,但比较特殊的地方在于,Python 中匿名函数必须只由单个表达式构成,这个表达式的值也就是匿名函数的返回值。

lambda 关键字可以用来创建一个匿名函数,紧跟其后的是参数列表和用冒号 : 分割开的单个表达式。如,lambda x: 2 * x 是将任何输入的数乘 2,而 lambda x, y: x+y 是计算两个数字的和。

使用匿名函数的经验准则是保持简单以及只在本地使用一次。一种常见却不推崇的做法是将其作为简单函数的另一种声明方式,赋值给变量,如:

>>> doubler = lambda x: 2 * x
>>> doubler(5)
10
>>> type(doubler)

对 lambda 函数命名的唯一作用可能是出于教学目的,其问题在于这使得调试不那么直观——错误信息只会提示某个 lambda 函数存在问题,但不会提示哪个函数。

正确的做法应该是将其作为参数传递,如 .sort 函数、sorted 函数等,此时单个表达式会被计算为一个值并且参与后续的计算。

>>> integers = [(3, -3), (2, 3), (5, 1), (-4, 4)]
>>> sorted(integers, key=lambda x: x[-1])
[(3, -3), (5, 1), (2, 3), (-4, 4)]

>>> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> nums.sort(key=lambda a: abs(5-a))
>>> nums
[5, 4, 6, 3, 7, 2, 8, 1, 9]

文件操作

读取文件

一种【与 C 相似】的操作是:

file = open('temp.txt', 'r')    # 打开一个文件,返回表示文件的对象
contents = file.read()          # 读取整个文件
print(contents)
file.close()                    # 关闭文件

此外,还有一种更推崇的操作方式,使用 with 上下文管理器,将打开的文件的对象存储在 as 后的变量,这样可以避免由于忘记关闭文件导致丢失数据:

with open('temp.txt', 'r') as file:
    contents = file.read()
    print(contents)

回到读取本身,方法 read() 读取整个文件的内容并返回单个字符串,并包含每个 \n 换行符。

但实际操作中读入一个长字符串是丑陋而且难以处理的,我们更倾向于逐行读入,用 line.rstrip() 消除行末的 \n

with open('temp.txt', 'r') as file:
    for line in file:   # file 也可作为一个迭代器
        print(line)     # 注意每一行末都会有一个 \n 被打印,而 print 本身又自带换行

另一种逐行读入是创建一个包含文件各行内容的列表,每个元素是一行内容的字符串,包含行尾的 \n

with open('temp.txt', 'r') as file:
    line1 = file.readline()     # 读入第一行作为字符串
    lines = file.readlines()    # 读入第二行以后的行(文件指针被移动了)

写入文件

with open('temp.txt', 'w') as file:
    file.write("I love programming.\n") # wirte 不会换行,要手动加

open() 函数的第二个实参 w 表示写入(自动创建或覆盖原内容),r 表示只读,a 表示附加(自动创建或添加到文件末尾),r+ 表示读写。如果不加第二个实参,则默认为 r 只读。

方法 write() 表示将字符串写入文件。如果要写入数值,应先用 str() 将其转化为字符串。

Python 中的类基础用法比 C/C++ 更简洁一些,待补充。

你可能感兴趣的:(『Python 干货』#1 基础语法(简明))