其他复习资料
目录
4.基于进程的并行计算(使用multiprocessing标准库)
5.基于asyncio的异步IO编程(asyncio标准库)
一、面向对象程序设计
1.思维导图
2.基本概念
面向对象的三大特性:封装、继承。多态
对象的定义 某种事物的抽象(功能)
抽象原则包括数据抽象和过程抽象两个方面 数据抽象-定义对象属性;过程抽象-定义对象操作(其实就是属性和方法)
封装 把客观事物抽象并封装成对象
继承 允许使用现有类的功能,并在无需重新改写原来的类的情况下,对这些功能进行扩展
多态性:对象可以表示多个类型的能力
3.类对象和实例对象
3.1创建对象
创建一个类和对象的示例代码如下:
class Person1: #定义类Person1
pass #类体为空语句
#测试代码
p1 = Person1() #创建和使用类对象
print(Person1, type(Person1), id(Person1))
print(p1, type(p1), id(p1))
3.2定义类中的成员变量
定义类中成员变量的代码如下:(通过self.变量名定义的属性)
【例9.3】定义类Person2。定义成员变量(域)
class Person2: #定义类Person2
def __init__(self, name,age): #__init__方法
self.name = name #初始化self.name,即成员变量name(域)
self.age = age #初始化self.age,即成员变量age(域)
def say_hi(self): #定义类Person2的函数say_hi()
print('您好, 我叫', self.name) #在实例方法中通过self.name读取成员变量name(域)
#测试代码
p1 = Person2('张三',25) #创建对象
p1. say_hi () #调用对象的方法
print(p1.age) #通过p1.age(obj1.变量名)读取成员变量age(域)
3.3类中属性的公有和私有
属性分有私有属性和公有属性
两个下划线开头,但是不以两个下划线结束的属性是私有的(private),其他为公共的(public)
【例9.5】私有属性示例(private.py)
class A:
__name = 'class A' #私有类属性
def get_name():
print(A.__name) #在类方法中访问私有类属性
#测试代码
A.get_name()
A.__name #导致错误,不能直接访问私有类属性
3.4特殊属性
其实就类似java中的构造方法、equals方法、tostring方法等
【例9.9】自定义属性示例
class CustomAttribute(object):
def __init__(self):
pass
def __getattribute__(self, name):
return str.upper(object.__getattribute__(self, name))
def __setattr__(self, name, value):
object.__setattr__(self, name, str.strip(value))
#测试代码
o = CustomAttribute()
o.firstname=' mary '
print(o.firstname)
4.方法
4.1声明类中的方法
实例方法示例。定义类Person4,创建其对象,并调用对象函数
class Person4: #定义类Person4
def say_hi(self, name): #定义方法say_hi
self.name = name #把参数name赋值给self.name,即成员变量name(域)
print('您好, 我叫', self.name)
p4 = Person4() #创建对象实例
p4.say_hi('Alice') #调用对象实例的方法
4.2静态方法
静态方法与类的实例无关,与类有关,可以通过类名.方法名直接调用
静态方法的定义如下:
访问静态方法的语法如下:
代码示例如下:
class MathTools:
@staticmethod
def add(x, y):
"""静态方法,用于计算两个数的和"""
return x + y
@staticmethod
def multiply(x, y):
"""静态方法,用于计算两个数的乘积"""
return x * y
# 调用静态方法,不需要创建类的实例
result_add = MathTools.add(5, 3)
result_multiply = MathTools.multiply(4, 2)
print(f"5 + 3 = {result_add}")
print(f"4 * 2 = {result_multiply}")
输出结果如下:
5 + 3 = 8
4 * 2 = 8
4.3类方法
类方法:属于类本身的方法
类方法和静态方法最大的区别就是:类方法需要被实例化才能够使用,静态方法不需要被实例化就能使用
类方法的定义语法
访问类方法的语法
类方法代码示例:
class Person:
# 类变量,存储所有实例的姓名
people_names = []
def __init__(self, name):
# 实例变量,存储个人的姓名
self.name = name
@classmethod
def add_name(cls, name):
"""类方法,用于添加姓名到类变量people_names中"""
cls.people_names.append(name)
@classmethod
def get_names(cls):
"""类方法,用于获取类变量people_names中的所有姓名"""
return cls.people_names
# 使用类方法添加姓名
Person.add_name("Alice")
Person.add_name("Bob")
# 使用类方法获取所有姓名
all_names = Person.get_names()
print("所有人员姓名:", all_names)
4.4私有方法和公有方法
两个下划线开头,但不以两个下划线结束的方法是私有的(private),其他为公共的(public)
以双下划线开始和结束的方法是Python的专有特殊方法。不能直接访问私有方法,但可以在其他方法中访问,例如构造函数和new方法、del方法等
私有方法代码示例:
class Account:
def __init__(self, balance=0):
self.__balance = balance # 私有属性
def deposit(self, amount):
"""存款方法,向账户中增加金额"""
if amount > 0:
self.__balance += amount
print(f"存入{amount}元,当前余额为{self.__balance}元")
else:
print("存款金额必须大于0")
def withdraw(self, amount):
"""取款方法,从账户中取出金额"""
if amount > 0 and amount <= self.__balance:
self.__balance -= amount
print(f"取出{amount}元,当前余额为{self.__balance}元")
else:
print("取款金额无效或余额不足")
def __check_balance(self, amount):
"""私有方法,检查余额是否充足"""
if self.__balance >= amount:
print("余额充足")
return True
else:
print("余额不足")
return False
# 创建账户实例
account = Account(100)
# 存款
account.deposit(50)
# 取款
account.withdraw(30)
# 尝试调用私有方法(这将导致错误)
# account.__check_balance(20) # 这行代码将无法正常运行,因为__check_balance是私有方法
在这个例子中,Account
类有一个私有属性__balance
,用来存储账户的余额。deposit
和withdraw
是两个公共方法,分别用于存款和取款。__check_balance
是一个私有方法,用于检查账户余额是否充足。
私有方法__check_balance
只能在类内部被调用,例如在withdraw
方法中,我们可以通过self.__check_balance(amount)
来调用它。如果你尝试在类的外部直接调用私有方法,Python将会抛出一个AttributeError
。
请注意,Python中的私有化只是一种约定,并不是强制的。即使方法或属性以两个下划线开头,外部代码仍然可以通过_ClassName__MethodName
的方式访问到它们,但这是一种不推荐的做法,因为它违反了封装的原则。
4.5方法重载
可以定义多个重名的方法,只要保证方法签名是唯一的
方法签名包括三个部分:方法名、参数数量和参数类型
方法重载代码示例:
class Person22: #定义类Person22
def say_hi(self, name): #定义类方法say_hi,带两个参数
print('您好, 我叫', self.name)
def say_hi(self, name, age): #定义类方法say_hi,带三个参数
print('hi, {0}, 年龄:{1}'.format(name,age))
p22 = Person22() #创建对象
p22.say_hi('Lisa', 22) #调用对象的方法
#p22.say_hi('Bob') #TypeError: say_hi() missing 1 required positional argument: 'age'
5.继承
派生类:Python支持多重继承,即一个派生类可以继承多个基类
基本语法如下:
声明派生类时,必须在其构造函数中调用基类的构造函数
代码示例如下:
创建基类Person,包含两个数据成员name和age;创建派生类Student,包含一个数据成员stu_id
class Person: #基类
def __init__(self, name, age): #构造函数
self.name = name #姓名
self.age = age #年龄
def say_hi(self): #定义基类方法say_hi
print('您好, 我叫{0}, {1}岁'.format(self.name,self.age))
class Student(Person): #派生类
def __init__(self, name, age, stu_id): #构造函数
Person.__init__(self, name, age) #调用基类构造函数
self.stu_id = stu_id #学号
def say_hi(self): #定义派生类方法say_hi
Person.say_hi(self) #调用基类方法say_hi
print('我是学生, 我的学号为:', self.stu_id)
p1 = Person('张王一', 33) #创建对象
p1.say_hi()
s1 = Student('李姚二', 20, '2018101001') #创建对象
s1.say_hi()
5.1 类成员对父类方法的继承和重写
代码示例如下:
class Dimension: #定义类Dimensions
def __init__(self, x, y): #构造函数
self.x = x #x坐标
self.y = y #y坐标
def area(self): #基类的方法area()
pass
class Circle(Dimension): #定义类Circle(圆)
def __init__(self, r): #构造函数
Dimension.__init__(self, r, 0)
def area(self): #覆盖基类的方法area()
return 3.14 * self.x * self.x #计算圆面积
class Rectangle(Dimension): #定义类Rectangle(矩形)
def __init__(self, w, h): #构造函数
Dimension.__init__(self, w, h)
def area(self): #覆盖基类的方法area()
return self.x * self.y #计算矩形面积
d1 = Circle(2.0) #创建对象:圆
d2 = Rectangle(2.0, 4.0) #创建对象:矩形
print(d1.area(), d2.area()) #计算并打印圆和矩形面积
6.对象的特殊方法
包含许多以双下划线开始和结束的方法,称之为特殊方法
例如,创建对象实例时自动调用其__init__方法,a<b时,自动调用对象a的__lt__方法
特殊方法 | 含义 |
---|---|
__lt__ , __add__ 等 | 对应运算符 < 、+ 等。 |
__init__ , __del__ | 创建或销毁对象时调用。 |
__len__ | 对应于内置函数 len() 。 |
__setitem__ , __getitem__ | 按索引赋值、取值。 |
__repr__(self) | 对应于内置函数 repr() 。 |
__str__(self) | 对应于内置函数 str() 。 |
__bytes__(self) | 对应于内置函数 bytes() 。 |
__format__(self, format_spec) | 对应于内置函数 format() 。 |
__bool__(self) | 对应于内置函数 bool() 。 |
__hash__(self) | 对应于内置函数 hash() 。 |
__dir__(self) | 对应于内置函数 dir() 。 |
7.对象的引用、浅拷贝和深拷贝
7.1浅拷贝
浅拷贝是创建一个新对象,其内容是原对象中包含的对象的引用拷贝。这意味着,如果原对象中包含的是可变对象,那么在拷贝中对这些可变对象的修改将会影响到原对象。
优点:拷贝速度快,因为不需要复制对象中的每个元素。 缺点:如果原对象中的元素是可变的,那么拷贝和原对象将共享这些元素。
代码示例如下:
import copy
original_list = [1, 2, [3, 4], 5]
shallow_copied_list = copy.copy(original_list)
# 修改拷贝中的可变对象
shallow_copied_list[2].append(6)
print("原始列表:", original_list) # 输出: [1, 2, [3, 4, 6], 5]
print("浅拷贝列表:", shallow_copied_list) # 输出: [1, 2, [3, 4, 6], 5]
7.2深拷贝
深拷贝是创建一个新对象,并且递归地拷贝原对象中包含的所有对象。这意味着,深拷贝会创建一个完全独立的拷贝,其中包含的所有对象都是原对象中对象的副本。
优点:拷贝后的对象与原对象完全独立,修改拷贝不会影响原对象。 缺点:拷贝速度慢,因为需要递归复制对象中的每个元素。
代码示例如下:
import copy
original_list = [1, 2, [3, 4], 5]
deep_copied_list = copy.deepcopy(original_list)
# 修改拷贝中的可变对象
deep_copied_list[2].append(6)
print("原始列表:", original_list) # 输出: [1, 2, [3, 4], 5]
print("深拷贝列表:", deep_copied_list) # 输出: [1, 2, [3, 4, 6], 5]
二、数据结构
1.思维导图
2.集合
集合数据类型是没有顺序的简单对象的聚集,且集合中元素不重复
Python集合数据类型包括可变集合对象(set)和不可变集合对象(frozenset)
创建集合和常用集合方法的代码示例
# 创建一个空集合
empty_set = set()
# 创建一个包含一些元素的集合
my_set = {1, 2, 3, 4, 5}
# 打印集合
print("空集合:", empty_set) # 输出: set()
print("包含元素的集合:", my_set) # 输出: {1, 2, 3, 4, 5}
# 添加元素到集合
my_set.add(6)
print("添加元素6后的集合:", my_set) # 输出: {1, 2, 3, 4, 5, 6}
# 移除元素从集合
my_set.remove(4)
print("移除元素4后的集合:", my_set) # 输出: {1, 2, 3, 5, 6}
# 集合的并集
union_set = my_set.union({7, 8, 9})
print("集合的并集:", union_set) # 输出: {1, 2, 3, 5, 6, 7, 8, 9}
# 集合的交集
intersection_set = my_set.intersection({1, 2, 3, 4, 5, 10})
print("集合的交集:", intersection_set) # 输出: {1, 2, 3, 5}
# 集合的差集
difference_set = my_set.difference({1, 2, 3})
print("集合的差集:", difference_set) # 输出: {5, 6}
# 集合的对称差分
symmetric_difference_set = my_set.symmetric_difference({5, 6, 10})
print("集合的对称差分:", symmetric_difference_set) # 输出: {1, 2, 3, 10}
# 集合推导式
squares_set = {x**2 for x in range(10)}
print("集合推导式创建的平方数集合:", squares_set) # 输出: {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
3.字典
python中的字典其实就跟java中的map集合是一样的概念,只是不同的叫法,一种存储键值对的数据结构
代码示例如下:
# 创建一个空字典
empty_dict = {}
# 创建一个包含一些键值对的字典
my_dict = {'name': 'Alice', 'age': 25, 'city': 'New York'}
# 打印字典
print("空字典:", empty_dict) # 输出: {}
print("包含键值对的字典:", my_dict) # 输出: {'name': 'Alice', 'age': 25, 'city': 'New York'}
# 访问字典中的值
print("名字:", my_dict['name']) # 输出: Alice
print("年龄:", my_dict.get('age')) # 输出: 25,使用get方法可以避免KeyError
# 添加新的键值对到字典
my_dict['email'] = 'alice@example.com'
print("添加邮箱后的字典:", my_dict) # 输出: {'name': 'Alice', 'age': 25, 'city': 'New York', 'email': 'alice@example.com'}
# 修改字典中的值
my_dict['age'] = 26
print("修改年龄后的字典:", my_dict) # 输出: {'name': 'Alice', 'age': 26, 'city': 'New York', 'email': 'alice@example.com'}
# 删除字典中的键值对
del my_dict['city']
print("删除城市后的字典:", my_dict) # 输出: {'name': 'Alice', 'age': 26, 'email': 'alice@example.com'}
# 检查键是否存在于字典中
if 'name' in my_dict:
print("字典中包含键 'name'") # 输出: 字典中包含键 'name'
# 获取字典的所有键
keys = my_dict.keys()
print("字典的所有键:", keys) # 输出: dict_keys(['name', 'age', 'email'])
# 获取字典的所有值
values = my_dict.values()
print("字典的所有值:", values) # 输出: dict_values(['Alice', 26, 'alice@example.com'])
# 获取字典的所有键值对
items = my_dict.items()
print("字典的所有键值对:", items) # 输出: dict_items([('name', 'Alice'), ('age', 26), ('email', 'alice@example.com')])
# 字典推导式
squares_dict = {x: x**2 for x in range(6)}
print("字典推导式创建的平方数字典:", squares_dict) # 输出: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
三、网络编程和通信
1.思维导图
2.基本概念
- 计算机网络是由传输介质连接在一起的一系列设备(网络节点)组成
- 在计算机网络中,用于规定信息的格式以及如何发送和接收信息的一套规则、标准或约定称为网络协议
- 网络编程就是通过网络协议与其他计算机进行通讯
- TCP/IP协议模型把TCP/IP协议族分成四个层次:图18-2
3.基于socket的网络编程
套接字:网络中两个应用程序之间通信的端点
3.1TCP
基于套接字的TCP Server的网络编程一般包括以下基本步骤
(1)创建socket对象。
(2)将socket绑定到指定地址上。
(3)准备好套接字,以便接收连接请求。
(4)通过socket对象方法accept,等待客户请求连接。
(5)服务器和客户机通过send和recv方法通信(传输数据)。
(6)传输结束,调用socket的close方法以关闭连接。
基于套接字的TCP Client的网络编程一般包括以下基本步骤
(1)创建socket对象。
(2)通过socket对象方法connect连接服务器。
(3)客户机和服务器通过send和recv方法通信(传输数据)。
(4)传输结束,调用socket的close方法以关闭连接。
3.2UDP
基于套接字的UDP Server的网络编程一般包括以下基本步骤
(1)创建socket对象。
(2)将socket绑定到指定地址上。
(3)服务器和客户机通过send和recv方法通信(传输数据)。
(4)传输结束,调用socket的close方法以关闭连接。
基于套接字的UDP Client的网络编程一般包括以下基本步骤
(1)创建socket对象。
(2)客户机和服务器通过send和recv方法通信(传输数据)。
(3)传输结束,调用socket的close方法以关闭连接。
4.基于http的网路编程
-
客户端-服务器模型:网络编程通常遵循客户端-服务器模型,其中服务器监听客户端的请求并提供响应。
-
URL和URI:统一资源定位器(URL)和统一资源标识符(URI)用于指定网络上的资源位置。
-
HTTP请求:客户端发送HTTP请求到服务器,请求包含方法(如GET、POST、PUT、DELETE等)、请求头和可能的请求体。
-
HTTP响应:服务器处理请求后,返回HTTP响应,响应包含状态码、响应头和响应体。
-
Python中的HTTP库:
http.client
:Python标准库中的一个模块,提供了HTTP客户端的功能。requests
:一个第三方库,简化了HTTP请求的发送和响应的处理。urllib
:Python标准库中的一个模块,提供了解析URL和发送请求的功能。
示例代码如下:
import requests
# 发送GET请求
response = requests.get('http://example.com')
# 检查响应状态码
if response.status_code == 200:
# 处理响应内容
print(response.text)
else:
print("请求失败,状态码:", response.status_code)
5.基于 poplib 和 smtplib 的网络编程
在Python中,poplib
和smtplib
是两个用于处理电子邮件的库,它们分别用于与POP3和SMTP服务器交互。
示例代码
使用poplib检索邮件:
import poplib
# 连接到POP3服务器
pop_server = 'pop.example.com'
username = 'your_username'
password = 'your_password'
pop3 = poplib.POP3(pop_server)
pop3.user(username)
pop3.pass_(password)
# 列出邮件
num_messages = len(pop3.list()[1])
for i in range(num_messages):
print(pop3.retr(i+1))
# 断开连接
pop3.quit()
使用smtplic发送邮件
import smtplib
from email.mime.text import MIMEText
# 创建SMTP对象并连接
smtp_server = 'smtp.example.com'
username = 'your_username'
password = 'your_password'
smtp_obj = smtplib.SMTP(smtp_server)
smtp_obj.starttls() # 启动TLS
smtp_obj.login(username, password)
# 创建邮件内容
msg = MIMEText('Hello, this is a test email.')
msg['Subject'] = 'Test Email'
msg['From'] = 'your_email@example.com'
msg['To'] = 'recipient_email@example.com'
# 发送邮件
smtp_obj.sendmail('your_email@example.com', 'recipient_email@example.com', msg.as_string())
# 断开连接
smtp_obj.quit()
四、并行计算:进程、线程和协程
1.思维导图
2.并发处理概述
- 进程是操作系统中正在执行的不同应用程序的一个实例
- 线程是进程中的一个实体,是被操作系统独立调度和分派处理器时间的基本单位
- 线程的优缺点
并发处理,因而特别适合需要同时执行多个操作的场合
解决用户响应性能和多任务的问题
引入了资源共享和同步等问题
- 协程(Coroutine)又称微线程、纤程,协程不是进程或线程,其执行过程更类似于函数调用
3.基于线程的并发处理(使用Threading库)
3.1使用Thread对象创建线程
通过创建Thread的对象可以创建线程:
Thread(target=None, name=None, args=(), kwargs={}) #构造函数
通过调用Thread对象的start方法可以启动线程。Thread对象的常用方法如下。
- t.start():启动线程。
- t.is_alive():判断线程是否活动。
- t.name:属性:线程名。对应于老版本的方法getname()和setname()。
- t.id:返回线程标识符。
threading模块包含以下若干实用函数。
- threading.get_ident():返回当前线程的标识符。
- threading.current_thread():返回当前线程。
- threading.active_count():返回活动的线程数目。
- threading.enumerate():返回活动线程的列表。
代码示例:
import threading, time, random
def timer(interval):
for i in range(3):
time.sleep(random.choice(range(interval))) #随机睡眠interval秒
thread_id = threading.get_ident() #获取当前线程标识符
print('Thread:{0} Time:{1}'.format(thread_id, time.ctime()))
if __name__=='__main__':
t1 = threading.Thread(target=timer, args=(5,)) #创建线程
t2 = threading.Thread(target=timer, args=(5,)) #创建线程
t1.start(); t2.start() #启动线程
4.基于进程的并行计算(使用multiprocessing标准库)
创建和使用进程
multiprocessing模块包含以下若干实用函数。
- cpu_count():可用的CPU核数量。
- current_process():返回当前进程。
- active_children():活动的子进程。
- log_to_stderr():函数可设置输出日志信息到标准错误输出(默认为控制台)
代码示例
import time, random
import multiprocessing as mp
def timer(interval):
for i in range(3):
time.sleep(random.choice(range(interval))) #随机睡眠interval秒
pid = mp.current_process().pid #获取当前进程ID
print('Process:{0} Time:{1}'.format(pid, time.ctime()))
if __name__=='__main__':
p1 = mp.Process(target=timer, args=(5,)) #创建进程
p2 = mp.Process(target=timer, args=(5,)) #创建进程
p1.start(); p2.start() #启动线程
p1.join(); p2.join()
5.基于asyncio的异步IO编程(asyncio标准库)
- 通过async关键字定义一个异步函数,调用异步函数返回一个协程(coroutine)对象。协程也是一种对象,协程不能直接运行,需要把协程加入到事件循环中,由后者在适当的时候调用协程
- 使用asyncio.get_event_loop()方法可以创建一个事件循环对象,然后使用其run_until_complete()方法将协程注册到事件循环
- 在异步函数中,可以使用await关键字,针对耗时的操作(例如网络请求、文件读取等IO操作)进行挂起
代码示例如下:
import asyncio, time
async def do_some_work(n): #使用async关键字定义异步函数
print('等待:{}秒'.format(n))
await asyncio.sleep(n) #休眠一段时间
return '{}秒后返回结束运行'.format(n)
start_time = time.time() #开始时间
coro = do_some_work(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(coro)
print('运行时间: ', time.time() - start_time)