Python 语言快速入门
运算符
数值运算:
- 四则运算、
//
整除、%
、**
幂。
逻辑运算:
- 比较:<、==、>、<=、> =、!=
- 逻辑运算:and、or、not
- 类型转换:int(a)、float(b)、str(a)
海象运算符:在表达式同时进行赋值和返回赋值的值。
1 | if (n := len(a)) > 10: |
位运算:
- &(与)、|(或)、^(异或)、~(取反)、<<、> >
成员运算符:
in
、not in
:如果在 指定序列 中(没)找到值。
身份运算符:
is
、is not
:判断两个标识符是否引用自一个对象。
is
与==
区别:is
用于判断两个变量引用对象是否为同一个,==
用于判断引用变量的值是否相等。
1
2
3
4
5
6
7 a = [1, 2, 3]
b = a
print(a == b) # True
print(a is b) # True
b = a[:]
print(a == b) # True
print(a is b) # False
数据类型
Number
1 | a, b, c, d = 20, 5.5, True, 4+3j |
1.int
:表示长整数类型。
2.bool
:True 为 1,False 为 0。
在 Python3 中,bool 是 int 的子类,可以和数字相互运算。
1 | a = True |
3.float
:表示浮点数类型。
1 | # 除法总是返回浮点数 |
4.complex
:表示复数类型。
String
字符串是不可变的,因此不能索引赋值和切片赋值。
1.索引:new_str[1]
。但 索引越界会报错。
2.切片:
1 | new_str = "Hello,world!" |
3.重复:new_str * 2
4.连接:使用 +
运算符连接字符串
5.长度:len(new_str)
6.f 字符串:
1 | name = "zhengyu" |
切片操作的第三个参数如果不传入,默认是 1。如果为正,则左闭右开取;如果为负,则左开右闭取。
一个很坑的点,python 中有
str()
函数,所以变量命名就不能用str
了。
List
列表是可变的。
1.索引:
1 | list = [1, "Hello", "World", "筝语", "Yotta"] |
2.切片:
1 | list = [1, "Hello", "World", "筝语", "Yotta"] |
3.重复:list* 2
4.连接:使用 +
运算符连接字符串
5.长度:len(list)
6.推导式
推导式包含一个表达式,后面为一个 for 子句,然后是零个或多个 for 或 if 子句。
1 | nums = [x for x in range(10)] |
Tuple
元组和列表类似,不同之处在于 元组的元素不能修改,写在小括号内 ()
。也可以索引、截取、+
操作符拼接。
1 | # 定义元组时,园括号可有可无 |
Set
创建集合用花括号 {}
或 set()
函数创建。
注意:创建 空集合 只能用
set()
,不能用{}
,因为{}
创建的是空字典。
1 | set1 = {1, "Baidu", 2, "Zhihu", 3, "Bilibili"} |
集合也支持推导式:
1 | a = set(x for x in 'abracadabra' if x in 'abc') |
Dictionary
创建字典用花括号 {}
或 dict()
函数创建,字典是无序的 key:value
键值对集合。
键(key)必须使用不可变类型。
1 | dicts = { |
字典也支持推导式:
1 | dicts = {x: x**2 for x in (2, 4, 6)} |
bytes
表示 不可变的二进制序列,可以使用 b
前缀创建,也可以使用哦个 bytes()
函数将其他类型的对象转换为 bytes
类型。bytes()
函数的第一个参数是要转换的对象,第二个参数是编码方式,如果省略第二个参数,则默认使用 UTF-8
编码。
1 | x = bytes("hello", encoding="utf-8") |
与字符串类型类似,bytes
类型也支持许多操作和方法,如切片、拼接、查找、替换等等。同时,由于 bytes
类型是不可变的,因此在进行修改操作时需要创建一个新的 bytes
对象。
1 | bts = b"Hello" |
流程控制
if…elif…else
1 | import math |
for 循环
和其他语言不同,python 中的 for 循环必须以 for...in...
使用,列表、元组、字符串都是序列,是在任意序列的元素上迭代,按它们在序列中出现的顺序。python 的 for 循环最后可以接 else,循环结束时候会执行。
break 可以跳出 for 和 while,如果循环终止,对应的 else 不会执行。continue 可以跳过当此循环块的剩余语句,然后进行下次循环。
range(start, stop, step)
函数:返回等差数列的可迭代对象。
1 | print(list(range(5, 10))) # [5, 6, 7, 8, 9] |
1 | for i in range(5): |
while 循环
1 | count = 0 |
match…case
基础用法,类似于其它语言的 switch...case
。
1 | def http_error(status): |
高级用法,用到解包和模式匹配。
1 | def process_list(lst): |
循环辅助函数
enumerate()
:返回一个迭代器,迭代器返回元组,元组第一个元素是索引,第二个元素是元素本身。
而字典类型,可以使用字典的方法:
dict.keys()
:返回一个迭代器,迭代器返回键构成的列表。dict.items()
:返回一个迭代器,迭代器返回元组,元组第一个元素是键,第二个元素是值。
zip()
:在多个迭代器上并行迭代,从每个迭代器返回一个数据项组成元组,遍历次数取两者更短的。
1 | new_str = "你好世界" |
函数参数
1.默认参数:def process_list(lst: list = [1, 1])
注意:默认参数虽然能赋值,但最好只拿来用。
比如下面例子,L 默认参数是空的,但函数会累加后续调用时传递的参数。
1
2
3
4
5
6
7 def f(a, L=[]):
L.append(a)
return L
print(f(1)) # [1]
print(f(2)) # [1, 2]
print(f(3)) # [1, 2, 3]如果不想后续调用共享默认值,需要这样写:
1
2
3
4
5 def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
2.关键字参数
函数调用时,可以传入关键字参数,参数名必须与函数定义时参数名一致。
3.可变参数列表和可变关键字参数列表
def cheeseshop(kind, *arguments, **keywords)
中,arguments
是可变参数列表,元组类型,keywords
是可变关键字参数列表,字典类型。
1 | def getPos(x: int, y: int, z: int = 0, *arguments, **keywords) -> None: |
4.解包可变参数列表和解包可变关键字参数列表
用 *
把实参从列表或元组解包出来,用 **
把实参从字典解包出来。
1 | def getStyle(bold: int, color: str, bgColor: str = "white"): |
5.位置参数和关键字参数的分隔
1 | def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): |
在 /
前的必须是位置参数,在 *
后的必须为关键字参数,两者之间的是两种参数类型都行。
1 | def getPos2(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): |
5.函数作为实参
函数可以作为实参传递给另一个函数,下面是排序操作的 key 函数例子。
1 | pairs:list[tuple[int, str]] = [(1, 'one'), (3, "three"), (2, 'two')] |
当然也可以使用 Lambda 表达式。
1 | pairs.sort(key=lambda pair: -pair[0]) |
模块和包
导入模块
1.导入整个模块:
1 | import module_name |
2.导入模块函数
1 | # 导入模块特定函数 |
3.给模块起别名
1 | import numpy as np |
__all__
变量
__all__
是模块中的一个特殊变量,它定义了模块中应该被导入的内容。当使用 from module_name import *
时,只有 __all__
指定的内容会被导入。即 __all__
限制 *
导入的行为。
1 | __all__ = ["echo", "surround", "reverse"] |
模块搜索路径
当导入一个名为 spam
的模块时,解释器首先会搜索具有该名称的内置模块。 这些模块的名称在 sys.builtin_module_names
中列出。 如果未找到,它将在变量 sys.path
所给出的目录列表中搜索名为 spam.py
的文件。 sys.path
是从这些位置初始化的:
- 被命令行直接运行的脚本所在的目录(或未指定文件时的当前目录)。
PYTHONPATH
(目录列表,与 shell 变量PATH
的语法一样)。- 依赖于安装的默认值(按照惯例包括一个
site-packages
目录,由site
模块处理)。
为了快速加载模块,Python 把模块的编译版本缓存在
__pycache__
目录中,文件名为module.version.pyc
。
包
包是通过使用“带点号模块名”来构造 Python 模块命名空间的一种方式。模块名 A.B
表示名为 A
的包中名为 B
的子模块。
比如有个包目录结构如下:
1 | sound/ Top-level package |
导入包时,Python 搜索 sys.path
里面的目录,查找包的子目录。
需要有 __init__.py
文件才能让 Python 将包含该文件的目录当作包处理。__init__.py
可以是空文件,也可以执行包的初始化代码或设置 __all__
变量。
/formats
、effects/
、filters/
是次一级的子包,每个子包中也有 __init__.py
文件。
echo.py
等文件是子包中的模块,模块中可能包含函数、类或变量。
引用包中的模块:
1 | from sound.effects import echo |
引用包中子模块的函数或变量:
1 | from sound.effects.echo from echofilter |
如果是 sound.filters.vocoder
模块需要使用 sound.effects
包中的 echo 模块,可以这样写:
1 | from sound.effects import echo |
除了以上方式,还可以使用相对导入:
1 | from . import echo |
输入与输出
f-字符串
整数:
:d
:默认整数格式化:<width>d
:右对齐,宽度为width
:<0<width>d
:左对齐,宽度为width
,用 0 填充空缺:>0<width>d
:右对齐,宽度为width
,用 0 填充空缺
浮点数:
:.<precision>f
:固定小数点后precision
位。:.<precision>e
:科学计数法表示。
字符串:
:<width>.<precision>s
:宽度为width
,最多显示precision
个字符。
1 | text = "Hello, world!" |
日期:
格式化指令 | 描述 |
---|---|
%Y |
完整的四位数年份,例如 2023 |
%y |
不带世纪的两位数年份,例如 23 |
%m |
月份(01-12) |
%b |
月份的缩写,例如 Jan |
%B |
月份的全称,例如 January |
%d |
一个月中的第几天(01-31) |
%H |
小时(24 小时制,00-23) |
%I |
小时(12 小时制,01-12) |
%M |
分钟(00-59) |
%S |
秒(00-59) |
%p |
AM/PM 标记 |
%a |
星期几的缩写,例如 Mon |
%A |
星期几的全称,例如 Monday |
%w |
星期几(0-6,0 表示周日) |
%j |
一年中的第几天(001-366) |
format()方法
1 | # 花括号中的数字表示传递给str.format()方法的对象所在位置 |
读写文件
调用 open()
函数:
第一个传参是文件名字符串;
第二个传参是包含描述文件使用方式字符的字符串。
r
,表示文件只能读取;w
表示只能写入(现有同名文件会被覆盖);a
表示打开文件并追加内容,任何写入的数据会自动添加到文件末尾。r+
表示打开文件进行读写。- 可选参数,省略时的默认值为
r
。
第三个参数是编码形式;
1 | # 读取文件并写入文本 |
如果没有使用
with
关键字,则应调用f.close()
关闭文件,即可释放文件占用的系统资源。
错误和异常
类
作用域和命名空间
命名空间是从名称到对象的映射,使用 Python 字典实现。
比如下面都是命名空间:
- 内置名称集合
- 模块全局名称
- 函数调用的局部名称
- 对象的属性集合
不同命名空间的名称之间绝对没有关系。
表达式 z.real
中,real
是对象 z
的属性,模块属性和模块中定义的全局名称之间存在直接映射:它们共享相同的命名空间。属性可以只读或可写,模块属性是可写的,即 modname.the_answer = 42
写入,也可以使用 del
语句删除可写属性,即 del modname.the_answer
。
内置名称的命名空间是在 Python 解释器启动时创建的,永远不会删除。模块的全局命名空间在读取模块定义时创建。函数的局部命名空间在函数被调用时创建,并在函数返回或抛出未处理的异常时被删除。
一个命名空间的作用域是 Python 代码中的一段文本区域,从这个区域可直接访问该命名空间。
作用域虽然是静态确定的,但会被动态使用。执行期间任何时刻,都会有 3 或 4 个“命名空间可直接访问”的嵌套作用域:
- 最内层的作用域,包含局部名称
- 外层闭包函数的作用域,包含“非局部、非全局”的名称
- 倒数第二层作用域,包含当前模块的全局名称
- 最外层作用域,是内置名称的命名空间
如果一个名称被声明为全局,则所有引用和赋值都将直接指向“倒数第二层作用域”,即包含模块的全局名称的作用域。要重新绑定在最内层作用域以外找到的变量,可以使用 nonlocal
语句;如果未使用 nonlocal
声明,这些变量将为只读。
作用域是按字面文本确定的:模块内定义的函数的全局作用域就是该模块的命名空间,无论该函数从什么地方或以什么别名被调用。 另一方面,实际的名称搜索是在运行时动态完成的。但是,Python 正在朝着“编译时静态名称解析”的方向发展,因此不要过于依赖动态名称解析!
global
语句用于表明特定变量在全局作用域里,并应在全局作用域中重新绑定;nonlocal
语句表明特定变量在外层作用域中,并应在外层作用域中重新绑定。
1 | def scope_test(): |
类的基本语法
为了保证所有实例定义的变量各自独立,初始化要在 __init__
函数中定义,在外面定义的变量会在不同实例之间共享。基本类型是可以在外面定义的,但如 list、tuple、set、dict 等类型是不能的。
1 | class Dog: |
self
代表的是类的实例,而非类
继承
Python 支持多继承,当冲突时候,取第一个继承类。
1 | class People: |
比如上面代码,如果子类 Student 没有重写 speak 方法,那么调用的是 People 类的 speak 方法,如果继承时写成 class Student(Speaker, People)
,那么调用的是 Speaker 类的 speak 方法。
可以通过 super()
函数调用父类的一个方法:
1 | super(Student, s).speak() |
类专有方法
__init__
:构造函数,生成对象时调用__del__
:析构函数,释放对象时调用__repr__
:打印,转化__setitem__
:按照索引赋值__getitem__
:按照索引取值__len__
:获取长度__cmp__
:比较运算__add__
:加法运算__sub__
:减运算__mul__
:乘运算__truediv__
:除运算__mod__
:求余运算__pow__
乘方__iter__
:迭代器__next__
:迭代器下一个值
1 | class Vector: |
使用类迭代器的例子:
1 | class Reverse: |