「学习笔记」Python基础入门

文章目录

此篇学习笔记基于Python 3.14.0版本,Python安装包里有Python解释器、Python运行所需要的基础库,以及交互式运行工具——PythonShell。 编写和运行Python程序主要的两种方式:

  1. 交互方式:Python + 回车键
  2. 文件方式:Python 文件名.py + 回车键

1. 语法基础

1.1 标识符的命名规则:

  1. 区分大小写;
  2. 只能是由下画线(_)字母数字或Unicode字符(包括汉字,但不建议)组成;
  3. 首字符不能是数字;
  4. 关键字不能作为标识符;
  5. 不要使用内置函数作为标识符;

1.2 关键字

只有 True、False、None的首字母大写,其他关键字全部小写:

  • 布尔与空:True(布尔真值)、False(布尔假值)、None(空值或缺失值)。
  • 逻辑运算:and(逻辑与)、or(逻辑或)、not(逻辑非)、in(成员判断)、is(对象身份判断)。
  • 流程控制:if/elif/else(条件分支)、for/while(循环)、break(跳出循环)、continue(跳过本次循环)、pass(空操作占位符)。
  • 函数与类定义:def(定义函数)、return(函数返回值)、lambda(创建匿名函数)、yield(生成器返回值)、class(定义类)、del(删除对象)。
  • 异常处理:try/except/finally(捕获和处理异常)、raise(主动抛出异常)、assert(断言检查)。
  • 模块与作用域:import/from(导入模块)、as(创建别名)、global(声明全局变量)、nonlocal(声明外层变量)。
  • 上下文与异步:with(上下文管理器,自动资源清理)、async/await(定义和调用异步协程)。

1.3 变量(弱类型)

在Python中为一个变量赋值的同时就声明了该变量,变量的数据类型根据赋值数据所属的类型推导出来,该变量还可以接收其他类型的数据。

1.4 语句

在Python中,一行代码表示一条语句,通常结尾不加分号。

1.5 注释

  • 井号(#)+ 空格 + 注释内容。
  • 通常代码第1行# coding=utf-8的注释很特殊,它告诉Python解释器该文件的编码集是UTF-8,该注释语句必须被放在文件的第1行或第2行才能有效。它还有替代写法:# _*- coding: utf-8 _*-

1.6 模块

一个模块在本质上就是一个文件,在模块中封装了很多代码元素。

  • import<模块名> # 导入模块的所有代码元素
  • from<模块名>import<代码元素> # 导入模块中的代码元素
  • from<模块名>import<代码元素>as<代码元素别名> # 如果名称有冲突,as使用别名

1.7 数据类型

在Python中所有的数据类型都是类,每个数据值都是类的“实例”;内置数据类型:数字字符串列表元组集合字典

  • 数字类型有4种:整数类型(int类)、浮点类型(float类)、复数类型(a+bi,a:实部,b:虚部,i:虚数单位)和布尔类型(bool类)。
  • 在Python的数字类型中,除复数外,其他三种数字类型如整数、浮点和布尔都可以相互转换,分为隐式类型的转换(数学计算)和显式类型的转换(转换函数:int(),float(),bool())。

1.8 运算符

  • 算术运算符:加+、减-、乘*、除/、取余%、幂**、地板除法//
  • 比较运算符:==!=><>=<=
  • 逻辑运算符:逻辑与and、逻辑或or、逻辑非not
  • 位运算符:位与&、位或|、位反~、位异或^、左移<<、右移>>。eg:按位取反运算涉及原码、补码、反码运算,取巧的公式:~a=-(a+1),即十进制数127按位取反为-128
  • 赋值运算符:=

Python运算符优先级从高到低依次为: 括号()、幂**、一元~x, +x, -x、算术(乘/除/取余/地板除)/, *, %, //、算术(加/减)+, -、位移<<, >>、按位与&、按位异或^、按位或|、比较<, <=, >, >=, ==, !=、身份is, is not、成员in, not in、逻辑非not、逻辑与and、逻辑或or、赋值=, +=, -=, 等

1.9 程序流程控制

  • if语句:ifif-elseif-elif-else
# coding=utf-8
score = int(input("请输入一个0~100整数:"))
if score >= 90:
    grade = 'A' # 缩进推荐4个半角空格
elif score >= 60:
    grade ='B
else:
    grade ='F
print("Grade ="+ grade)
  • while语句:while
# coding=utf-8
i = 0
while i*i < 10:
    i += 1
    if i == 3:
        break # 在i==3时终止循环
    print(str(i) + '*' str(i) + '=', i*i)
else: # else子语句只有在循环体正常结束时才执行,这里while被break中断,else不会执行
    print('While Over!')
  • for语句:for-in
# coding=utf-8
for item in range(10):
    if item == 3:
        continue # 在i==3时终止本次循环,接着进行下次循环i==4
    print(item)
else: # for循环体未中断,执行else语句
    print('For over!')

2. 容器类型

在Python中,序列(列表、元组等)、集合和字典等可以容纳多项数据,我们称它们为容器类型的数据。 序列包括列表(list)、字符串(str)、元组(tuple)和字节序列(bytes)等。

  • 序列中的元素都是有序的,每一个元素都带有序号,这个序号叫作索引
    • 正序(左→右)从0开始到len-1即[0~(len-1)],倒序(右→左)从-1开始到-len即[(-len)~(-1)]。
  • 加(+)和乘(*)运算符也可以用于序列中的元素操作。加(+)运算符可以将两个序列连接起来,乘(*)运算符可以将两个序列重复多次。如:'Hello'*2 == 'HelloHello''Hello'+'World' == 'HelloWorld'
  • 切片运算符[start:end:step]就是从序列中切分出小的子序列;其中,start是开始索引(默认0),end是结束索引(默认序列的长度),step是步长(默认1,可以为负整数(倒序获取));切下的小切片包括start,但不包括end

2.1 列表 list

列表(list)是一种可变序列类型,我们可以追加、插入、删除和替换列表中的元素。

  • 创建列表
    • list(iterable)函数,参数iterable是可迭代对象(字符串、列表、元组、集合和字典等);如list('Hello')创建的列表为:['H','e','l','l','o']
    • [元素1,元素2,元素3,⋯],指定具体的列表元素。
  • 追加元素:append(x)方法,加(+)运算符或extend(x)方法。
list = [20,10,50]
list.append(80) # [20, 10, 50, 80]
a = [60,30]
list += a       # [20, 10, 50, 80, 60, 30]
b = [70,90]
list.extend(b)  # [20, 10, 50, 80, 60, 30, 70, 90]
  • 插入元素:insert(i,x)方法,其中i指定索引位置,x是要插入的元素。
  • 替换元素:将列表下标索引元素进行赋值即可,如list[0]=65
  • 删除元素:remove(x)方法,如果找到多个匹配的元素,则只删除第一个匹配的元素。`。

2.2 元组 tuple

元组(tuple)是一种不可变序列类型。

  • 创建元组
    • tuple(iterable)函数,参数iterable是可迭代对象(字符串、列表、元组、集合和字典等
    • (元素1,元素2,元素3,⋯),指定具体的组元素,对于元组元素,可以使用小括号括起来,也可以省略小括号。
  • 元组打包:创建元组,并将多个数据放到元组中,这个过程被称为元组打包。
  • 元组拆包:是将元组中的元素取出,分别赋值给不同的变量。
21,32,43,45    # 指定具体的组元素
               # (21, 32, 43, 45)
tuple('Hello') # tuple函数创建
               # ('H', 'e', 'l', 'l', 'o')
tuple([21,32,43,45]) # (21, 32, 43, 45)
1,  # 创建只有一个元素的元组 (1,)
()  # 创建空元组 () 

2.3 集合 set

集合(set)是一种可迭代的、无序的、不能包含重复元素的容器类型的数据。

  • 创建集合
    • set(iterable)函数,参数iterable是可迭代对象(字符串、列表、元组、集合和字典等
    • {元素1,元素2,元素3,⋯},指定具体的集合元素。
  • 修改集合
    • add(x)添加元素,如果元素已经存在,则不能添加,不会抛出错误。
    • remove(x)删除元素,如果元素不存在,则抛出错误。
    • clear()清除集合。

2.5 字典 dict

字典(dict)是可迭代的、通过键(key)值对来访问元素的可变的容器类型的数据。键不能重复,值可以重复。

  • 创建字典
    • dict()函数。
    • {key1:value1,key2:value2,⋯},指定具体的字典键值对。
{100:'张三',101:'李四'} # 指定具体的字典键值对
                      # {100: '张三', 101: '李四'}
dict([(100,'张三'),(101,'李四')]) # dict()函数,传入列表
                      # {100: '张三', 101: '李四'}
dict(((100,'张三'),(101,'李四'))) # dict()函数,传入元组
                      # {100: '张三', 101: '李四'}
dict(zip([100,101],['张三','李四'])) # 通过zip()函数将两个可迭代对象打包成元组,第一个对象为键,第二个为值,它们元素个数相同,一一对应。
                      # {100: '张三', 101: '李四'}
  • 修改字典:字典可以被修改,但都是针对键和值同时操作的,对字典的修改包括添加/替换(dict[key]=value)和删除(pop(key))。
  • 访问字典视图:items()返回字典的所有键值对视图。keys()返回字典键视图。values()返回字典值视图。
d = {100:'张三',101:'李四',102:'王五'}
for s_id in d.keys():
    print('ID:' + str(s_id)) # ID:100  ID:101  ID:102
for s_name in d.values():
    print('姓名:' + s_name)  # 姓名:张三  姓名:李四  姓名:王五
for s_id,s_name in d.items():
    print('ID:{0},姓名:{1}'.format(s_id,s_name))
    # ID:100,姓名:张三  ID:101,姓名:李四  ID:102,姓名:王五

3. 字符串 str

在Python中,字符串(str)是一种不可变的字符序列,有三种表示方式:普通字符串、原始字符串、长字符串。

3.1 普通字符串

普通字符串:指用单引号'或双引号"括起来的字符串。 如果想在字符串中包含一些特殊的字符,例如换行符、制表符等,在普通字符串中就需要转义,前面要加上反斜杠\,这叫作字符转义,如\n(换行符)、\t(制表符)、\\(反斜杠自身)、\'(单引号)、\"(双引号)、\r(回车符)、\b(退格符)、\f(换页符)。

3.2 原始字符串

原始字符串(raw string):特殊字符不需要被转义,按照字符串的本来样子呈现。在普通字符串前加r就是原始字符串,如r'Hello'

s1 = 'Hello\n World'
print(s1)
# Hello
#  World
s2 = r'Hello\n World'
print(s2)
# Hello\n World

3.3 长字符串

长字符串:使用三个单引号(''')或三个双引号(""")括起来。

3.4 字符串转换

字符串与数字相互转换:str()函数,int()函数,float()函数,

str(123) # '123'
str(True) # 'True'
float('60.0') # 60.0
int('60') # 60
int('AB',16) # 171

3.5 字符串格式化

格式化字符串:format()方法与占位符({})表示

i = 32
'i*i={}'.format(i*i) # 'i*i=1024'
'{0}*{0}={1}'.format(i,i*i) # '32*32=1024'
'{p1}*{p1}={p2}'.format(p1=i,p2=i*i) # '32*32=1024'

格式化控制符:对字符串的格式进行更加精准的控制,语法:{参数序号:格式控制符}({1:d})或{参数名:格式控制符}({p1:d})。 Python常用的格式化控制符包括s(字符串)、d(整数)、f(浮点数)、x(十六进制)、o(八进制)、e(科学计数法)等。

'{0:s}年龄{1:d},工资{2:f}元'.format('Tony',20,6868.00)
# 'Tony年龄20,工资6868.000000元'
'{p1:s}年龄{p2:d},工资{p3:f}元'.format(p1='Tony',p2=20,p3=6868.00)
# 'Tony年龄20,工资6868.000000元'
'{%s}年龄{%d},工资{%f}元'%('Tony',20,6868.00)
# '{Tony}年龄{20},工资{6868.000000}元'

3.6 字符串操作

  • 字符串查找:str.find(sub[,start[,end]]),表示在索引startend之间查找子字符串sub,如果找到,则返回最左端位置的索引;如果没有找到,则返回-1。
  • 字符串替换:str.replace(old,new[,count]),表示:用new子字符串替换old子字符串。count参数指定了替换old子字符串的个数,如果count被省略,则替换所有old子字符串。
  • 字符串分割:str.split(sep=None,maxsplit=-1),表示:使用sep子字符串分割字符串str。maxsplit是最大分割次数,如果maxsplit被省略,则表示不限制分割次数。
  • 更多字符串操作:单击Windows“开始”菜单中的Python 3.x Manuals打开官方文档。

4. 函数

函数定义结构:

def 函数名(参数列表):
    函数体
    return 返回值可选

函数在类中定义,称之为类的方法。

4.1 函数调用

调用函数(参数传递)

  • 位置参数:实参与形参按顺序匹配;
  • 关键字参数:通过参数名=值传递,无需考虑顺序;
  • 默认参数:参数可指定默认值(需位于参数列表末尾),调用时可省略;
  • 可变参数:
    • *args接收任意数量的位置参数(封装为元组);
    • **kwargs接收关键字参数(封装为字典);

返回值处理:return语句可返回单个值多个值(封装为元组)或无值(默认返回None)。

def rect_area(width, height):
     area = width * height
     print("width:{0},height:{1},面积:{2}".format(width,height,area))
     return area

rect_area(50,60)  # 实参与形参按顺序匹配
width:50,height:60,面积:3000
3000
rect_area(height=60,width=50) # 参数名=值,无需考虑顺序
width:50,height:60,面积:3000
3000
def sum(*nums):   # 元组可变参数
     total = 0.0
     for num in nums:
          total += num
     return total

print(sum(100.0,20.0,60.0))
180.0
def show_info(**infos):   # 字典可变参数
     for key,value in infos.items():
          print("{0}{1}".format(key,value))

show_info(name="Tom", age=18, sex=True)
nameTom
age18
sexTrue

4.2 变量的作用域与函数类型

函数中变量的作用域: 变量可以在模块中创建,作用域(变量的有效范围)是整个模块,被称为全局变量。变量也可以在函数中创建,在默认情况下作用域是整个函数,被称为局部变量。

函数类型: 函数的数据类型是function,被称为函数类型。任意类型的数据(包括函数)都可以作为函数返回值使用,还可以作为函数参数使用。

4.3 内置函数

Python内置函数:

  • 过滤函数filter语法:filter(function, iterable)
    • 参数function是过滤条件函数,返回布尔值;
    • 参数iterable是容器类型的数据,如list等;
  • 映射函数map语法:map(function, iterable)
    • 参数function是变换规则函数,返回变换之后的元素;
    • 参数iterable是容器类型的数据,如list等;
  • 匿名函数lambda语法:lambda 参数列表:函数体(lambda函数体只有一条语句计算结果并返回,不需要return返回)
data1 = [66,15,91,28,98,50,7,80,99]
filtered = filter(lambda x: (x > 50), data1)
list(filtered) # [66, 91, 98, 80, 99]
mapped = map(lambda x: (x + 10), data1)
list(mapped) # [76, 25, 101, 38, 108, 60, 17, 90, 109]

5. 类与对象

Python中的数据类型都是,可以通过class关键字自定义类,即创建一种新的数据类型,对象是类的一个实例。 类的成员包括:属性,构造方法,成员变量(类变量,实例变量),成员方法(类方法,成员方法)。 类变量和类方法属于类,通过类调用;实例变量和实例方法属于对象,通过对象调用。

class Car(object): # 定义类
    pass # 类体
car = Car() # 实例化一个对象
  • 属性:对类进行封装而提供的特殊方法。
  • 构造方法:__init__,第1个参数是self,之后的参数用来初始化成员变量。调用构造方法时不需要传入self参数。
  • 成员变量:数据成员(类或对象的数据)。
  • 成员方法:在类中定义的函数。
    • 类方法:第1个参数是类本身cls,属于类,不属于个体实例。
    • 实例方法:第1个参数是self(实例本身),是某个实例(或对象)个体特有的方法。
class Account:
     interest_rate = 0.0668  # 类变量-利率
     def __init__(self, owner, amount):
          self.owner = owner   # 实例变量-账户名
          self.amount = amount # 实例变量-账户金额
     # 类方法
     @classmethod
     def interest_by(cls, amt):
          return cls.interest_rate * amt
interest = Account.interest_by(12000.0) # 调用类方法
print('计算利息:{0:.4f}'.format(interest)) # 计算利息:801.6000

5.1 封装性

封装性:封装隐藏了对象的内部细节,只保留有限的对外接口节,使得操作对象变得简单。

  • 私有变量:Python中的变量默认是公有的,在变量前加上双下画线(__)变成私有变量。外部访问通过公有的set(赋值)和get(取值)方法访问。
  • 私有方法:私有方法与私有变量的封装是类似的,在方法前加上双下画线(__)就是私有方法了。
  • 属性:在两个方法前加上装饰器(@property和@属性名.setter)使得方法成为属性;替代了get()set()这两个公有方法;属性使用起来类似于公有变量,可以在赋值符(=)左边或右边,左边被赋值,右边取值。
class Dog:
    # 构造方法
    def __init__(self, name, age):
      self.__name = name # 私有变量name
      self.__age = age   # 私有变量age
    # get方法
    def get_age(self):
      return self.__age
    # set方法
    def set_age(self, age):
      self.__age = age
    @property
    def name(self): # 属性,替代get_name方法,方法名就是属性名
      return self.__name
    @name.setter
    def name(self, name): # 属性,替代set_name
      self.__name = name

# 实例化一个对象
dog = Dog('球球', 2)
print('{0}的年龄是{1}'.format(dog.name, dog.get_age())) # 球球的年龄是2
dog.set_age(3)
dog.name = '胖胖'
print('修改后,{0}的年龄是{1}'.format(dog.name, dog.get_age())) # 修改后,胖胖的年龄是3

5.2 继承性

继承性:子类继承父类,定义类时在类的后面使用一对小括号指定它的父类。

class Animal:
    def __init__(self, name):
      self.name = name
    def infos(self):
      return '动物的名字:{0}'.format(self.name)

class Dog(Animal): # 指定父类Animal
    def __init__(self, name, age):
        super().__init__(name) # 调用父类构造方法
        self.age = age

# 实例化一个对象
dog = Dog('球球', 2)
print(dog.infos()) # 动物的名字:球球
  • 子类继承父类时,只有公有的成员变量和方法才可以北继承。
  • 当子类继承多个父类时,如果在多个父类中有相同的成员方法或成员变量,则子类优先继承左边父类中的成员方法或成员变量,从左到右继承级别从高到低。
  • 方法重写:子类会重写(Override)父类的同名方法

5.3 多态性

多态性:指对象可以表现出多种形态。在多个子类继承父类,并重写父类方法后,这些子类所创建的对象之间就是多态的。这些对象采用不同的方式实现父类方法。

  • 鸭子类型:若看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟可以被称为鸭子。
  • 由于支持鸭子类型测试,所以Python解释器不检查发生多态的对象是否继承了同一个父类,只要它们有相同的行为(方法),它们之间就是多态的。
def start(obj): # 接收的obj对象具有speak()方法
    obj.speak()

class Animal:
    def speak(self):
        print('动物叫...')
class Dog(Animal):
    def speak(self):
        print('狗:旺旺叫...')
class Cat(Animal):
    def speak(self):
        print('猫:喵喵叫...')
class Car:
    def speak(self):
        print('汽车:滴滴叫...')
# start()函数可以接收所有speak()方法对象
start(Dog()) # 狗:旺旺叫...
start(Cat()) # 猫:喵喵叫...
start(Car()) # 汽车:滴滴叫...

6. 异常处理

Traceback异常堆栈信息,描述了程序运行过程引发异常的信息。

Traceback (most recent call last):
  File "test.py", line 1, in <module> # test.py文件第一行发生异常
    result = n / int(i)               # 发生异常的表达式
ZeroDivisionError: division by zero   # 异常信息描述,这里是除零异常 ZeroDivisionError

在Python中,异常类命名的主要后缀有ExceptionErrorWarning

6.1 异常捕获

捕获异常:通过try-except语句实现,语法:

try
    <可能会引发异常的语句>
except [异常类型]:
    <处理异常>
finally:
    <释放资源>

一个try代码块后可以跟多个except代码块,但只能是最后一个except代码块省略异常类型。

i = input('请输入数字:')
n = 8888
try:
    result = n / int(i)
    print('{0}除以{1}等于{2}'.format(n,i,result))
except (ZeroDivisionError, ValueError) as e: # 多个except处理类似可以合并
    print('除0或无效数字异常:{}'.format(e))
except:
    print('其他异常...')
finally:
    print('释放资源...')

6.2 自定义异常类

自定义异常类:实现自定义异常类,需要继承Exception类或其子类。

  • 前面们遇到的ZeroDivisionError和ValueError异常都属于Exception的子类。
  • 可以通过raise语句手动引发异常。
class myException(Exception): # 自定义异常类
    def __init__(self, message): # message是异常描述信息
        message = '自定义异常:'+ message
        super().__init__(message) # 把message传给父类构造方法

i = input('请输入数字:')
n = 8888
try:
    result = n / int(i)
    print('{0}除以{1}等于{2}'.format(n,i,result))
except ZeroDivisionError as e:
    raise myException('不能除以0')
except ValueError as e:
    raise myException('输入数字无效')

请输入数字0
Traceback (most recent call last):
  File "<python-input-0>", line 9, in <module>
    result = n / int(i)
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "<python-input-0>", line 12, in <module>
    raise myException('不能除以0')
myException: 自定义异常不能除以0

7. 常用内置模块

7.1 math(数学计算)

math:数学计算模块(ceil,floor,sqrt,sin等)

import math
math.ceil(6.6)    # 7,向上取整
math.floor(6.6)   # 6,向下取整
math.pow(5,2)     # 25.0,5的2次幂
math.sqrt(9)      # 3.0,9的平方根
math.log(8, 2)    # 3.0,以2为底8的对数,若省略底默E为底计算自然对数
math.pi           # 3.141592653589793
math.degrees(0.5 * math.pi) # 90.0,弧度转换成角度
math.radians(90 / math.pi)  # 0.5,角度转换成弧度
math.sin(0.3)     # 0.29552020666133955

7.2 datetime(日期时间)

datetime:日期时间模块(datetime,date,time,timedelta, tzinfo)

  • 日期时间:datetime.datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):datetime(年,月,日,时,分,秒,微秒,时区)
import datetime
datetime.datetime(2025,11,11)  # datetime.datetime(2025, 11, 11, 0, 0)
datetime.datetime.today()      # datetime.datetime(2025, 11, 11, 14, 29, 2, 955960)
datetime.datetime.now(tz=None) # datetime.datetime(2025, 11, 11, 14, 29, 2, 955960)
  • 日期:datetime.date(year, month, day):date(年,月,日)
import datetime
datetime.date(2025,11,11) # datetime.date(2025, 11, 11)
datetime.date.today()     # datetime.date(2025, 11, 11)
datetime.date.fromtimestamp(1762842883) # datetime.date(2025, 11, 11)
  • 时间:datetime.time(hour=0, minute=0, second=0, microsecond=0, tzinfo=None):time(时,分,秒,微秒,时区)
  • 时间跨度:datetime.timedelta(days=0, seconds=0, microsecond=0, millisecond=0, minutes=0, hours=0, weeeks=0):timedelta(天,秒,微秒,毫秒,分钟,小时,周)
import datetime
datetime.time(23,59,59,955960) # datetime.time(23, 59, 59, 955960)
datetime.timedelta(weeks=5)    # datetime.timedelta(days=35)

d = datetime.date.today()      # datetime.date(2025, 11, 11)
delta = datetime.timedelta(weeks=1) # datetime.timedelta(days=7)
d -= delta                     # datetime.date(2025, 11, 4)
  • 日期时间与字符串相互转换
    • 将日期时间对象转换为字符串时,称之为日期时间格式化,使用strftime(format)方法。
    • 将字符串转换为日期时间对象的过程,叫作日期时间解析,使用strptime(str,format)方法。
d = datetime.datetime.today()       # datetime.datetime(2025, 11, 11, 15, 9, 45, 770088)
s = d.strftime('%Y-%m-%d %H:%M:%S') # '2025-11-11 15:09:45'
d2 = datetime.datetime.strptime(s, '%Y-%m-%d %H:%M:%S') # datetime.datetime(2025, 11, 11, 15, 9, 45)

使用strptime方法时,提供的字符串应该可以表示一个有效的日期时间字符串,否则会发生ValueError异常。

7.2 re(正则表达式)

re:正则表达式模块

  • match(pattern, text)函数:字符串匹配,pattern是正则表达式(字符串模板),text是要验证的字符串。匹配成功返回一个Match对象(匹配对象),否则返回None。
  • search(pattern, text)函数:在text字符串中查找匹配的内容,如果找到,则返回第1个匹配的Match对象,否则返回None。
  • findall(pattern, text)函数:在text字符串中查找匹配的内容,如果找到,则返回所有匹配的Match对象,否则返回None。
m = re.match(r'\w+@test\.com', 'tony@test.com')
type(m)  # <class 're.Match'>
>>> m    # <re.Match object; span=(0, 13), match='tony@test.com'>
p = r'Java|java|JAVA'
text = 'I like Java and java and JAVA.'
m1 = re.search(p, text)
m2 = re.findall(p, text)
>>> m1    # <re.Match object; span=(7, 11), match='Java'>
>>> m2    # ['Java', 'java', 'JAVA']
  • re.sub(pattern, repl, string, count=0)函数:字符串替换,参数pattern是正则表达式;参数repl是用于替换的新字符串;参数string是即将被替换的旧字符串;参数count是要替换的最大数量,默认值为零,表示不限制替换数量。
  • re.split(pattern, string, maxsplit=0)函数:字符串分割,返回字符串列表对象。参数pattern是正则表达式;参数string是要分割的字符串;参数maxsplit是最大分割次数;maxsplit的默认值为零,表示分割次数没有限制。

8. 文件读写

文本文件的内部以字符形式存储数据,字符是有编码的,例如GBK(简体中文)、UTF-8等; 二进制文件的内部以字节形式存储数据,没有编码的概念。二进制文件较为常用,例如图片,音频,视频,可执行文件.exe等。

8.1 打开文件

打开文件:open(file, mode='r', encoding=None, errors=None)

  • file参数:文件,可以是字符串(文件名)或整数(已经打开的文件)。
  • mode参数:打开模式,t文本,b二进制,r只读,w只写,x独占创建,a追加,+更新(读写,必须与r、w或a组合使用)。
  • encoding参数:文件编码,默认UTF-8,主要用于打开文本文件。
  • errors参数:用来指定在文本文件发生编码错误时如何处理。推荐取值为’ignore’,表示在遇到编码错误时忽略该错误,程序会继续执行,不会退出。

mode参数字符串组合:

  • rtr:文本文件只读模式
  • wtw:文本文件只写模式
  • xtx:文本文件独占创建模式
  • ata:文本文件追加模式(只能追加写入,不可读文件)
  • r+:文本文件读写模式,如果文件不存在,则抛出异常
  • w+:文本文件读写模式,如果文件不存在,则创建文件
  • a+:文本文件读追加模式,如果文件不存在,则创建文件
  • rb:二进制文件只读模式
  • wb:二进制文件只写模式
  • xb:二进制文件独占创建模式
  • ab:二进制文件追加模式(只能追加写入,不可读文件)
  • rb+:文本文件读写模式,如果文件不存在,则抛出异常
  • wb+:文本文件读写模式,如果文件不存在,则创建文件
  • ab+:文本文件读追加模式,如果文件不存在,则创建文件

8.2 关闭文件

  • ①在finally代码块中使用close()关闭文件;
  • ②在with as代码块中自动关闭文件(在as后面声明一个资源变量,在with as代码块结束之后自动释放资源)。
with open('test.txt') as f:
    content = f.read()
    print(content)

8.3 读写文本文件

  • read(size=-1):从文件中读取字符串,size限制读取的字符数,size=-1指对读取的字符数没有限制。
  • readline(size=-1):在读取到换行符或文件尾时返回单行字符串。如果已经到文件尾,则返回一个空字符串。size是限制读取的字符数,size=-1表示没有限制。
  • readlines():读取文件数据到一个字符串列表中,每一行数据都是列表的一个元素。
  • write(s):将字符串s写入文件中,并返回写入的字符数。
  • writelines(lines):向文件中写入一个字符串列表。不添加行分隔符,因此通常为每一行末尾都提供行分隔符。
  • flush():刷新写缓冲区,在文件没有关闭的情况下将数据写入文件中。
# coding=utf-8
with open('test.txt', 'r', encoding='gbk') as f:
    lines = f.readlines()
    with open('copy.txt', 'w', encoding='utf-8') as copy_f:
        copy_f.writelines(lines)
        print('文本文件复制成功!')

8.4 读写二进制文件

二进制文件的读写单位是字节,不需要考虑编码问题。

  • read(size=-1):从文件中读取字节,size限制读取的字节数,size=-1表示没有限制。
  • readline(size=-1):从文件中读取并返回一行。size是限制读取的行数,size=-1表示没有限制。
  • readlines():读取文件数据到一个字节列表中,每一行数据都是列表的一个元素。
  • write(b):写入b字节,并返回写入的字节数。
  • writelines(lines):向文件中写入一个字节列表。不添加行分隔符,因此通常为每一行末尾都提供行分隔符。
  • flush():刷新写缓冲区,在文件没有关闭的情况下将数据写入文件中。
# coding=utf-8
with open('test.png', 'rb') as f:
    b = f.read()
    with open('copy.png', 'wb') as copy_f:
        copy_f.write(b)
        print('二进制文件复制成功!')

9. 网络通信

  • TCP/IP:由IP(Internet Protocol,网际协议)和TCP(Transmission Control Protocol,传输控制协议)两个协议构成。
    • IP是一种低级路由协议,它将数据拆分在许多小的数据包中,并通过网络将它们发送到某一特定地址,但无法保证所有包都抵达目的地,也不能保证包按顺序抵达。
    • 由于通过IP传输数据存在不安全性,所以还需要通过TCP进行网络通信。TCP是一种高层次的协议,是面向连接的可靠数据传输协议,如果有些数据包没被收到,则会重发,对数据包的内容准确性进行检查并保证数据包按顺序抵达。所以,TCP能够保证数据包安全地按照发送时的顺序送达目的地。
  • IP地址:网络通信中,TCP/IP使用IP地址来标识源地址目的地址
    • IPv4:32位地址,4个8位的二进制数组成,约有43亿个可用地址,通常以点分十进制表示(如192.168.1.1)。地址资源枯竭,依赖NAT(网络地址转换)缓解短缺。
    • IPv6:128位地址,大约有3.4×10^38个地址,冒号分隔的十六进制(2001:0db8:85a3:0000:0000:8a2e:0370:7334,允许省略连续的零,即简写为2001:db8:85a3::8a2e:370:7334)。彻底解决地址不足,支持物联网(IoT)等海量设备接入。
  • 端口:如果把IP地址比作电话号码,那么端口就是分机号码。网络通信时需要同时指定IP地址和端口号。端口号用来识别同一台计算机中进行通信的不同应用程序。因此,它也被称为程序地址
    • 端口号的范围是0~65535。将小于1024的端口号保留给预定义的服务,例如HTTP是80,FTP是21,Telnet是23,Email是25 等等。
  • HTTP/HTTPS
    • HTTP(Hypertext Transfer Protocol,超文本传输协议)属于应用层协议,使用端口80与TCP/IP通信。HTTP/1.1共定义了8种请求方法:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE和CONNECT。GET和POST方法最常用。
    • HTTPS(Hypertext Transfer Protocol Secure,超文本传输安全协议)是超文本传输协议和SSL的组合,用于提供加密通信及对网络服务器身份的鉴定。简单地说,HTTPS是加密的HTTP。使用端口443与TCP/IP通信。

9.1 urllib.request 模块

urllib.request模块:通过Request对象发送网络请求(GET/POST)

import urllib.request
url='http://localhost:8080/test.do'
# 创建Request对象,默认GET请求
req1 = urllib.request.Request(url)
# 准备POST请求参数(字节序列)
params = urllib.parse.urlencode({'id':'10'}).encode()
# 创建Request对象,提供了data参数就是POST请求
req2 = urllib.request.Request(url, data=params)

9.2 JSON数据

服务器返回的是JSON (JavaScript Object Notation)数据格式(JSON对象(object)和JSON数组(array))。 JSON数据的解码(decode)指将JSON数据转换为Python数据,当从网络中接收或从磁盘中读取JSON数据时,需要将其解码为Python数据。 使用json模块提供的loads(str)函数进行JSON数据的解码,参数str是JSON字符串,返回Python数据。

import urllib.request
import json
url='http://localhost:8080/test.do'
# 创建Request对象,默认GET请求
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as response:
    data = response.read()
    json_data = data.decode()
    print('JSON字符串:', json_data)
    py_dict = json.loads(json_data)
    for d_key,d_value in d.items():
        print('Python字典数据,key:{0},value:{1}'.format(d_key,d_value))

10. 访问数据库

数据库编程主要分为两类:查询(Read)和修改(C插入、U更新、D删除)。 在修改过程中如果执行SQL操作成功,则提交数据库事务;如果失败,则回滚事务。 数据库事务(transaction)是修改数据库的一系列操作,这些操作要么全部执行,要么全部不执行。若全部操作执行成功,则确定修改,称之为“提交事务”;如果有操作执行失败,则放弃修改,称之为“回滚事务”。

10.1 mysql-connector驱动器

使用mysql-connector(MySQL官方提供的驱动器)来连接使用MySQL

import mysql.connector
# 连接MySQL数据库
mydb = mysql.connector.connect(
  host="localhost", # 数据库主机地址
  user="test",      # 数据库用户名
  passwd="123456"   # 数据库密码
  database="test_db"
)
mycursor = mydb.cursor()
# 创建一个名为 test_db 的库
mycursor.execute("CREATE DATABASE test_db")
# 查看数据库
mycursor.execute("SHOW DATABASES")
# 创建一个名为 sites 的数据表
mycursor.execute("CREATE TABLE sites (name VARCHAR(255), url VARCHAR(255))")
# 查看数据表
mycursor.execute("SHOW TABLES")
# 创建一个主键,主键起始值为 1,逐步递增
mycursor.execute("ALTER TABLE sites ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY")
# 创建数据表时指定主键
mycursor.execute("CREATE TABLE sites (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), url VARCHAR(255))")
# 插入数据
sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"
val = ("RUNOOB", "https://www.runoob.com")
mycursor.execute(sql, val)
mydb.commit() # 数据表内容有更新,必须使用到该语句
print("1 条记录已插入, ID:", mycursor.lastrowid)
# 批量插入
sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"
val = [
  ('Google', 'https://www.google.com'),
  ('Github', 'https://www.github.com'),
  ('Taobao', 'https://www.taobao.com'),
  ('stackoverflow', 'https://www.stackoverflow.com/')
]
mycursor.executemany(sql, val)
mydb.commit()
print(mycursor.rowcount, "批量插入成功。")
# 查询数据
mycursor.execute("SELECT * FROM sites")
myresult = mycursor.fetchall()     # fetchall() 获取所有记录
# 查询数据-指定字段
mycursor.execute("SELECT name, url FROM sites")
myresult = mycursor.fetchall()     # 若 fetchone() 则只读取一条
# 删除记录
sql = "DELETE FROM sites WHERE name = 'stackoverflow'"
mycursor.execute(sql)
mydb.commit()
print(mycursor.rowcount, " 条记录删除")
# 更新数据
sql = "UPDATE sites SET name = %s WHERE name = %s"
val = ("Zhihu", "ZH")
mycursor.execute(sql, val)
mydb.commit()
print(mycursor.rowcount, " 条记录被修改")
# 删除表
sql = "DROP TABLE IF EXISTS sites"  # 删除数据表 sites
mycursor.execute(sql)

11. 多线程

一个进程就是一个正在执行的程序,每一个进程都有自己独立的一块内存空间、一组系统资源。 一个进程中可以包含多个线程,多个线程共享一块内存空间和一组系统资源。所以,系统在各个线程之间切换时,开销要比进程小得多。 主线程:Python程序至少有一个线程,即主线程,程序在启动后由Python解释器负责创建主线程,在程序结束后由Python解释器负责停止主线程。 在多线程中,主线程负责其他线程的启动、挂起、停止等操作。其他线程被称为子线程。

  • threading线程模块:提供了多线程编程的高级API,线程类Thread,线程相关的函数等。
  • 常用的线程相关的函数:
    • active_count():返回当前处于活动状态的线程个数。
    • current_thread():返回当前的Thread对象。
    • main_thread():返回主线程对象。主线程是Python解释器启动的线程。
  • 创建子线程:创建一个可执行的子线程,需要线程对象线程体,子线程在启动后会执行线程体。
    • 线程对象:线程类Thread或Thread子类所创建的对象;
    • 线程体:①自定义函数实现线程体。②自定义线程类实现线程体。

11.1 自定义函数实现线程体

  • 创建Thread对象的构造方法:Thread(target=None, name=None, args=())
    • target参数指向线程体函数;
    • name参数设置线程名,如果省略,则系统会为其分配一个名称;
    • args是为线程体函数提供的参数,是一个元组类型。
import threading
import time
# 线程体函数
def thread_run():
    t = threading.current_thread() # 当前线程对象
    for n in range(5):
        print('第{0}次执行线程{1}'.format(n, t.name)) # 当前线程名
        # 线程休眠(只有当前线程休眠,其他线程才有机会执行)
        time.sleep(2)
    print('线程{0}执行完成!'.format(t.name))
# 主线程
# 创建线程对象t1
t1 = threading.Thread(target=thread_run)
# 创建线程对象t2
t2 = threading.Thread(target=thread_run, name='MyThread')
# 启动线程t1
t1.start()
# 启动线程t2
t2.start()

打印结果:

第0次执行线程Thread-1 (thread_run)
第0次执行线程MyThread
第1次执行线程Thread-1 (thread_run)
第1次执行线程MyThread
第2次执行线程Thread-1 (thread_run)
第2次执行线程MyThread
第3次执行线程Thread-1 (thread_run)
第3次执行线程MyThread
第4次执行线程Thread-1 (thread_run)
第4次执行线程MyThread
线程Thread-1 (thread_run)执行完成!
线程MyThread执行完成!

11.2 自定义线程类实现线程体

创建一个Thread子类(继承Thread类)并重写run()方法

import threading
import time
# 自定义线程类,继承Thread类
class SubThread(threading.Thread):
    def __init__(self, name=None):
        super().__init__(name=name)
    def run(self):
        t = threading.current_thread() # 当前线程对象
        for n in range(5):
            print('第{0}次执行线程{1}'.format(n, t.name)) # 当前线程名
            # 线程休眠(只有当前线程休眠,其他线程才有机会执行)
            time.sleep(2)
        print('线程{0}执行完成!'.format(t.name))
# 主线程
# 创建线程对象t1
t1 = SubThread()
# 创建线程对象t2
t2 = SubThread(name='MyThread')
# 启动线程t1
t1.start()
# 启动线程t2
t2.start()

打印结果:

第0次执行线程Thread-1
第0次执行线程MyThread
第1次执行线程MyThread
第1次执行线程Thread-1
第2次执行线程MyThread
第2次执行线程Thread-1
第3次执行线程MyThread
第3次执行线程Thread-1
第4次执行线程MyThread
第4次执行线程Thread-1
线程MyThread执行完成!
线程Thread-1执行完成!

11.3 线程管理

线程管理包括线程创建、线程启动、线程休眠等待线程结束和线程停止线程等待 假设主线程需要等待t1子线程结束才能继续执行,主线程调用t1.join()后主线程阻塞,等待t1结束后主线程继续执行。 join()方法的语法:join(timeout=None),参数timeout用于设置超时时间,单位是秒。 线程停止 在线程体结束时,线程就停止了。但某些复杂业务会在线程体中执行一个“死循环”,通过判断停止变量来结束循环。在一般情况下,死循环会执行线程任务,然后休眠,再执行,再休眠,直到结束循环。

示例:网络爬虫程序每隔一段时间都会执行一次下载图片任务,在下载任务完成后,休眠一段时间再执行。这样反复执行,直到爬虫程序停止。

import threading
import time

# 线程停止变量
isRunning = True

# 工作线程体函数
def workthread_run():
    while isRunning: # 死循环
        # 线程开始工作
        print('工作线程执行中...')
        # 线程休眠
        time.sleep(5) # 每5秒调用一次
    print('工作线程结束。')

# 控制线程体函数
def controlthread_run():
    global isRunning
    while isRunning: # 死循环
        # 从键盘输入停止指令exit
        command =input('请输入停止指令:')
        if command == 'exit':
            isRunning = False  # 修改停止变量
            print('控制线程结束。')

# 主线程
# 创建工作线程对象workthread
workthread =threading.Thread(target=workthread_run)
# 启动线程workthread
workthread.start()
# 创建控制线程对象controlthread
controlthread=threading.Thread(target=controlthread_run)
# 启动线程controlthread
controlthread.start()

打印结果:

工作线程执行中...
工作线程执行中...
工作线程执行中...
工作线程执行中...
exit
控制线程结束。
工作线程结束。

12. 类型注解(Type Hints)

Python3.6 开始,可以直接为变量添加类型注解。

# 没有类型注解的代码
name = "Alice"
age = 30
is_student = False
scores = [95, 88, 91]
def greet(name):
    return f"Hello, {name}"

# 有类型注解的代码
name: str = "Alice"         # 注解为字符串 (str)
age: int = 30               # 注解为整数 (int)
is_student: bool = False    # 注解为布尔值 (bool)
scores: list = [95, 88, 91] # 注解为列表 (list)
def greet(name: str) -> str: # 有类型注解的函数,参数和返回值都是字符串
    return f"Hello, {name}"

12.1 typing 模块

基本的 str, int, bool, list 很好用,复杂类型注解需要typing模块提供。

from typing import List, Dict, Tuple, Set, Optional, Union

# List[int] 表示这是一个只包含整数的列表
numbers: List[int] = [1, 2, 3, 4, 5]
# Dict[str, int] 表示这是一个键为字符串、值为整数的字典
student_scores: Dict[str, int] = {"Alice": 95, "Bob": 88}
# Tuple[int, str, bool] 表示这是一个包含整数、字符串、布尔值的元组
person_info: Tuple[int, str, bool] = (25, "Alice", True)
# Set[str] 表示这是一个只包含字符串的集合
unique_names: Set[str] = {"Alice", "Bob", "Charlie"}
# 可选类型 Optional[str] 等价于 Union[str, None] 字符串或None
name: Optional[str] = "Alice"
# 联合类型 Union 当值可能是多种类型之一时使用 
def process_input(data: Union[str, int, List[int]]) -> None:
    """处理可能是字符串、整数或整数列表的输入"""
    if isinstance(data, str):
        print(f"字符串: {data}")
    elif isinstance(data, int):
        print(f"整数: {data}")
    elif isinstance(data, list):
        print(f"列表: {data}")
process_input("hello")    # 输出:字符串: hello
process_input(42)         # 输出:整数: 42  
process_input([1, 2, 3])  # 输出:列表: [1, 2, 3]
END .

相关系列文章

×