当前位置:   article > 正文

学习python第一周

学习python第一周

内容:错误、调试和测试,IO编程,进程和线程,正则表达式

# 一、错误、调试和测试

## 错误:也就是异常以及异常处理。异常(Exception)是指在程序运行过程中发生的错误或异常情况,导致程序无法正常执行下去的情况。当出现异常时,程序将会抛出(raise)相应的异常对象,如果没有适当的异常处理机制,程序将会终止并显示错误信息。

Python中的异常处理机制允许您捕获和处理异常,以避免程序异常终止,并采取适当的措施来处理异常情况。下面是异常处理的基本语法结构:

  1. try:
  2.     可能会出现异常的代码块
  3.     ...
  4. except ExceptionType1:
  5.     # 处理 ExceptionType1 类型的异常
  6.     # ...
  7. except ExceptionType2:
  8.     # 处理 ExceptionType2 类型的异常
  9.     # ...
  10. else:
  11.     # 当没有异常发生时执行的代码
  12.     # ...
  13. finally:
  14.     # 无论是否发生异常,都会执行的清理代码
  15.     # ...

异常处理使用try语句块来包裹可能会发生异常的代码。在try块中,您可以编写可能引发异常的代码。如果在try块中发生异常,程序将立即跳转到匹配的except块,根据异常类型选择处理代码。如果没有匹配的except块,异常将会继续传播给上一级调用。

在except块中,您可以编写处理特定类型异常的代码。可以指定多个except块,每个块处理不同类型的异常。如果没有指定具体的异常类型,except块将处理所有类型的异常。

else块是可选的,用于处理当没有发生异常时的代码。它在没有异常发生时执行。

finally块也是可选的,它包含无论是否发生异常都会执行的清理代码。不管是否发生异常,finally块中的代码都会被执行。

以下是一个示例,演示如何使用异常处理:

  1. try:
  2.     x = 10 / 0  # 除以零,引发 ZeroDivisionError 异常
  3.     print("这行代码不会执行")
  4. except ZeroDivisionError:
  5.     print("除以零错误")
  6. except:
  7.     print("其他异常发生")
  8. else:
  9.     print("没有发生异常")
  10. finally:
  11.     print("最终清理代码")

在上述示例中,由于除以零会引发ZeroDivisionError异常,程序会跳转到相应的except ZeroDivisionError块,输出"除以零错误"。然后,程序执行finally块中的清理代码,输出"最终清理代码"。

通过使用异常处理机制,您可以优雅地处理和恢复异常情况,保证程序的稳定性和可靠性。在实际开发中,根据不同的情况和需求,您可以选择捕获特定类型的异常并采取相应的处理措施。

## 调试:调试是一种用于识别和修复代码错误(bug)的过程。调试允许程序员逐步执行代码并观察程序在执行过程中的状态,以找出代码中的问题。通过调试,您可以跟踪程序的执行路径,查看变量的值以及检查代码的行为,从而更好地理解程序运行时发生的事情。

Python提供了多种调试工具和技术,其中一些常见的方法包括:

打印语句:通过在代码中插入打印语句来输出变量的值或特定的调试信息。这是最简单的调试方法之一,但对于大型程序或复杂问题可能不够有效。

断点:您可以在代码中设置断点,当程序执行到断点时,程序会停止执行,以便您可以检查当前的变量状态、栈跟踪等信息。可以使用pdb(Python调试器)库来设置和管理断点。

日志记录:通过在关键位置插入日志记录语句,可以记录程序执行期间的信息。您可以使用内置的logging模块或第三方库(例如loguru、logging等)进行日志记录。

调试器:Python提供了多个交互式调试器,例如pdb、ipdb和pdb++等。这些调试器可以让您在运行时逐行执行代码,并提供了更丰富的功能,如查看变量、检查堆栈、设置断点等。

IDE(集成开发环境)的调试功能:许多Python IDE(例如PyCharm、Visual Studio Code等)提供了集成的调试器,使您可以在开发环境中进行代码调试。这些调试器通常提供了可视化的界面和丰富的功能,如逐行执行、变量查看、堆栈追踪等。

无论使用哪种方法,调试都是一种帮助程序员识别和解决问题的关键技能。通过调试,您可以更有效地理解程序的行为并修复错误,从而提高代码的质量和可靠性。

## 测试分为单元测试和文档测试:

单元测试:单元测试是一种测试方法,用于验证程序中最小可测试单元的行为是否正确。通过编写自动化的测试代码,可以提高代码质量、简化调试过程,并支持持续集成和重构等开发实践。

单元测试的主要特点包括:

独立性:每个单元测试都应该独立于其他测试,以确保测试结果不会受到其他单元的影响。

自动化:单元测试通常以自动化的方式执行,可以通过编写测试代码和使用测试框架(如unittest、pytest等)来实现。

小范围:单元测试的重点是测试最小的可测试单元,如函数、方法或类的特定功能。

快速执行:由于单元测试的规模较小,因此它们通常可以在短时间内执行,从而允许频繁运行以进行快速反馈。

单元测试的好处包括:

提高代码质量:单元测试可以帮助发现和修复代码中的错误,提高代码的健壮性和可靠性。

简化调试:当单元测试失败时,可以迅速定位到具体的单元,从而更容易调试和修复问题。

支持重构:通过编写单元测试,可以更安全地进行代码重构,因为测试用例可以确保在重构后代码的行为仍然正确。

支持持续集成:单元测试是持续集成(Continuous Integration)流程中的重要组成部分,可以确保新代码的集成不会破坏现有的功能。

文档测试:文档测试(doctest)是Python中一种自动化的测试技术,它结合了文档编写和代码测试的过程。通过在代码注释中编写示例代码和期望的输出结果,您可以使用doctest模块来提取这些示例代码并自动运行它们,以验证代码的正确性。

文档测试也可以理解为,利用doctest模块,在python文件中调用‘doctest.testmod()’来运行注释里面的代码。也就是可以运行注释里面的代码。

# 二、IO编程

接下来学习的就是IO编程啦。

IO编程分为:文件读写、StringIO和BytesIO、操作文件和目录、序列化。

## 文件读写:

文件读取: 

要读取文件,可以按照以下步骤进行操作:

打开文件:使用open()函数打开文件,并指定文件路径和打开模式(如读取模式"r")。例如:file = open("file.txt", "r")。

读取文件内容:可以使用不同的方法读取文件的内容。例如:

read()方法:一次性读取整个文件内容。例如:content = file.read()。

readline()方法:逐行读取文件内容。例如:line = file.readline()。

关闭文件:在读取完文件后,应使用close()方法关闭文件,以释放系统资源。例如:file.close()

## StringIO和BytesIO:

StringIO和BytesIO是Python中的两个内置类,用于在内存中读写字符串和字节数据。

StringIO: StringIO类允许您像操作文件一样操作字符串。它是基于内存的I/O类,提供了读取和写入字符串的功能。您可以像读写文件一样使用它,但实际上是在内存中进行操作,而不是在磁盘上创建临时文件。

使用StringIO时,您可以:

使用StringIO()构造函数创建一个StringIO对象。

使用write()方法将字符串写入StringIO对象。

使用read()方法从StringIO对象中读取字符串。

使用getvalue()方法获取StringIO对象中当前的字符串值。

以下是一个使用StringIO的示例:

  1. try:
  2.     x = 10 / 0  # 除以零,引发 ZeroDivisionError 异常
  3.     print("这行代码不会执行")
  4. except ZeroDivisionError:
  5.     print("除以零错误")
  6. except:
  7.     print("其他异常发生")
  8. else:
  9.     print("没有发生异常")
  10. finally:
  11.     print("最终清理代码")

BytesIO: BytesIO类与StringIO类类似,但它处理的是字节数据而不是字符串。它提供了在内存中读写字节数据的功能。

使用BytesIO时,您可以:

使用BytesIO()构造函数创建一个BytesIO对象。

使用write()方法将字节数据写入BytesIO对象。

使用read()方法从BytesIO对象中读取字节数据。

使用getvalue()方法获取BytesIO对象中当前的字节数据值。

以下是一个使用BytesIO的示例:

  1. from io import BytesIO
  2. # 创建一个BytesIO对象
  3. bytes_io = BytesIO()
  4. # 写入字节数据
  5. bytes_io.write(b'\x48\x65\x6C\x6C\x6F\x2C\x20\x57\x6F\x72\x6C\x64\x21')
  6. # 从BytesIO对象中读取字节数据
  7. content = bytes_io.getvalue()print(content)  # 输出: b'Hello, World!'
  8. # 关闭BytesIO对象
  9. bytes_io.close()

StringIO和BytesIO类在处理字符串和字节数据时非常方便,特别适用于模拟文件操作或将数据存储在内存中的场景。它们提供了简单的API,使得读写操作更加灵活和高效。

## 操作文件和目录:Python的os模块提供了丰富的功能来处理文件和目录。您可以使用这些函数来执行常见的文件和目录操作,例如获取当前工作目录、创建目录、删除文件、遍历目录等。根据您的需求,可以使用适当的os模块函数来实现所需的文件和目录操作。

下面是一些常见的文件和目录操作的示例:

获取当前工作目录:

  1. import os
  2. current_dir = os.getcwd()
  3. print(current_dir)
  4. 创建目录:
  5. import os
  6. new_dir = os.path.join(current_dir, 'new_directory')
  7. os.mkdir(new_dir)

切换工作目录:

  1. import os
  2. target_dir = os.path.join(current_dir, 'target_directory')
  3. os.chdir(target_dir)

检查文件或目录是否存在:

  1. import os
  2. file_path = os.path.join(current_dir, 'file.txt')
  3. exists = os.path.exists(file_path)

删除文件:

  1. import os
  2. file_path = os.path.join(current_dir, 'file.txt')
  3. os.remove(file_path)

删除目录:

  1. import os
  2. target_dir = os.path.join(current_dir, 'target_directory')
  3. os.rmdir(target_dir)

遍历目录中的文件和子目录:

  1. import os
  2. for root, dirs, files in os.walk(current_dir):
  3.     for file in files:
  4.         file_path = os.path.join(root, file)
  5.         print(file_path)

重命名文件或目录:

  1. import os
  2. old_name = os.path.join(current_dir, 'old_name.txt')
  3. new_name = os.path.join(current_dir, 'new_name.txt')
  4. os.rename(old_name, new_name)

## 序列化:序列化是将对象转换为可存储或传输的形式的过程,以便稍后可以重新创建该对象。在序列化期间,对象的状态和数据被编码为字节流或文本格式,以便在需要时进行存储、传输或持久化。

常见的序列化方法是使用内置的pickle模块和json模块。

使用pickle模块:

pickle模块允许您将Python对象转换为字节流形式,并将其保存到文件或数据库中,或者通过网络传输。它还可以从字节流中还原对象。

以下是使用pickle进行序列化和反序列化的示例:

  1. import pickle
  2. # 序列化对象
  3. data = {'name': 'Alice', 'age': 25}
  4. serialized_data = pickle.dumps(data)
  5. # 将序列化数据保存到文件
  6. with open('data.pickle', 'wb') as file:
  7.     file.write(serialized_data)
  8. # 反序列化对象
  9. with open('data.pickle', 'rb') as file:
  10.     deserialized_data = pickle.load(file)
  11. print(deserialized_data)  # 输出: {'name': 'Alice', 'age': 25}

使用json模块:

json模块提供了一种将Python对象序列化为JSON格式的方式。JSON是一种轻量级的数据交换格式,常用于跨平台数据传输。

以下是使用json进行序列化和反序列化的示例:

  1. import json
  2. # 序列化对象
  3. data = {'name': 'Alice', 'age': 25}
  4. serialized_data = json.dumps(data)
  5. # 将序列化数据保存到文件
  6. with open('data.json', 'w') as file:
  7.     file.write(serialized_data)
  8. # 反序列化对象
  9. with open('data.json', 'r') as file:
  10.     deserialized_data = json.load(file)
  11. print(deserialized_data)  # 输出: {'name': 'Alice', 'age': 25}

json模块还提供了dump()和load()函数,用于直接将对象序列化到文件或从文件中反序列化对象。

请注意,使用pickle和json模块进行序列化时,不是所有类型的对象都可以被序列化。例如,无法序列化包含函数或文件对象的对象。另外,反序列化时应注意从受信任的来源获取数据,以防止安全风险。

# 三、进程和线程

线程和进程包括多进程、多线程、ThreadLocal、进程vs.线程、分布式进程。

## 多进程:多进程是指同时运行多个独立的进程,每个进程都有自己的内存空间和执行环境。在操作系统中,进程是资源分配的基本单位,每个进程都有独立的地址空间,可以执行自己的代码,并拥有自己的执行上下文、堆栈、文件描述符等。

使用多进程的主要目的是实现并行计算和提高系统的资源利用率。通过将任务分配给多个进程同时执行,可以利用多核处理器的并行计算能力,加快任务的处理速度。此外,多进程还可以提高系统的稳定性和可靠性,因为每个进程都是相互独立的,一个进程的崩溃或异常不会影响其他进程的执行。

在Python中,可以使用multiprocessing模块来实现多进程编程。该模块提供了创建和管理进程的类和函数,使得编写多进程程序更加方便。通过multiprocessing模块,可以创建新的进程,启动它们执行指定的任务,进行进程间通信,以及控制进程的行为等。

以下是一个使用multiprocessing模块创建并启动多个进程的简单示例:

  1. import multiprocessing
  2. def worker():
  3.     print('Worker process')
  4. if __name__ == '__main__':
  5.     # 创建并启动两个进程
  6.     process1 = multiprocessing.Process(target=worker)
  7.     process2 = multiprocessing.Process(target=worker)
  8.     
  9.     process1.start()
  10.     process2.start()
  11.     
  12.     process1.join()
  13. process2.join()

## 多线程、ThreadLocal:在Python中,可以使用threading模块来实现多线程编程。threading模块提供了创建和管理线程的类和函数,使得在Python中使用多线程变得相对简单。

下面是使用threading模块创建和启动线程的基本步骤:

导入threading模块:

import threading

定义线程的执行函数,也就是线程要执行的任务。这个函数可以是一个普通的函数或者是一个类的方法。

  1. def my_thread_func():
  2.     # 执行线程任务的代码
  3.     pass

创建Thread对象,传入要执行的函数和参数。

my_thread = threading.Thread(target=my_thread_func, args=(arg1, arg2))

启动线程。

my_thread.start()

通过调用start()方法,线程将会在后台异步执行my_thread_func函数。

此外,还可以使用threading模块提供的其他方法和属性来管理线程,如join()方法等待线程执行完毕、is_alive()方法检查线程是否在运行、name属性获取线程的名称等。

需要注意的是,Python的多线程编程存在全局解释器锁(GIL)的限制,这使得在某些情况下多线程并不能充分利用多核处理器的能力。如果需要充分利用多核处理器,可以考虑使用multiprocessing模块来进行多进程编程。

此外,还可以使用其他第三方库,如concurrent.futures和queue模块,来更方便地进行多线程编程和线程间的通信与协调。

下面是一个简单的示例代码,演示了如何使用threading模块创建和启动两个线程:

  1. import threading
  2. import time
  3. def print_numbers():
  4.     for i in range(1, 6):
  5.         print(i)
  6.         time.sleep(1)
  7. def print_letters():
  8.     for letter in 'ABCDE':
  9.         print(letter)
  10.         time.sleep(1)
  11. # 创建线程
  12. thread1 = threading.Thread(target=print_numbers)
  13. thread2 = threading.Thread(target=print_letters)
  14. # 启动线程
  15. thread1.start()
  16. thread2.start()
  17. # 等待线程执行完毕
  18. thread1.join()
  19. thread2.join()
  20. print('All threads have finished execution.')

以上代码中,print_numbers函数和print_letters函数分别在两个线程中执行,每隔1秒打印一次数字和字母。主线程等待两个子线程执行完毕后输出"All threads have finished execution."。

## 进程vs.线程:

进程(Process):

进程是操作系统中的一个独立执行单元,具有独立的内存空间和系统资源。

每个进程都有自己的地址空间,包括代码、数据和堆栈等。

进程之间相互独立,一个进程的崩溃不会影响其他进程的执行。

进程间通信需要通过操作系统提供的机制,如管道、共享内存、消息队列等。

线程(Thread):

线程是进程的子任务,是进程中的一个执行流程。

多个线程可以共享同一个进程的地址空间和系统资源。

同一进程中的线程之间可以直接通信,共享内存数据。

线程的创建、销毁和切换比进程更轻量级,开销较小。

由于共享同一地址空间,线程之间的数据共享和通信比进程更方便快捷。

下面是进程和线程之间的一些比较:

并发性:

进程是独立执行的,因此多个进程可以同时执行,实现真正的并行处理。

线程是进程内的子任务,多个线程共享同一进程的资源,在多核处理器上可以实现并行运行。

资源开销:

进程的创建和销毁开销较大,需要分配独立的地址空间和系统资源。

线程的创建和销毁开销较小,因为它们共享同一进程的资源。

通信和同步:

进程之间的通信需要使用操作系统提供的机制,如管道、共享内存、消息队列等。

线程之间可以直接共享进程的内存空间,可以通过共享变量来进行通信和同步,但需要注意线程安全性。

编程模型:

进程间的切换需要保存和恢复整个进程的状态,因此进程间的切换开销较大。

线程间的切换开销相对较小,因为它们共享进程的资源。

在实际应用中,进程和线程的选择取决于具体的需求。通常情况下,如果需要并行处理和更高的系统资源隔离性,可以使用进程;如果需要更高的执行效率和方便的数据共享,可以使用线程。

## 分布式进程:

可以使用multiprocessing模块来实现分布式进程编程。multiprocessing模块提供了一种在多个计算机上分布执行任务的方式,可以利用网络进行进程间的通信和协调。

分布式进程编程中通常涉及两个角色:服务端(Master)和工作节点(Worker)。服务端负责分配任务和管理工作节点,工作节点接收任务并执行。工作节点可以位于不同的计算机上,通过网络进行通信。

下面是使用multiprocessing模块进行分布式进程编程的基本步骤:

导入multiprocessing.managers模块:

import multiprocessing.managers

创建一个派生自multiprocessing.managers.BaseManager的子类,用于定义服务端的行为。

  1. class MyManager(multiprocessing.managers.BaseManager):
  2.     pass

在服务端定义要共享的数据和方法。这些数据和方法将通过网络进行传输。

  1. class SharedData(object):
  2.     def __init__(self):
  3.         self.shared_variable = None
  4.     def set_variable(self, value):
  5.         self.shared_variable = value
  6.     def get_variable(self):
  7.         return self.shared_variable

在服务端注册共享数据和方法。

MyManager.register('SharedData', SharedData)

启动服务端。

  1. if __name__ == '__main__':
  2.     manager = MyManager(address=('127.0.0.1', 5000), authkey=b'mypassword')
  3.     server = manager.get_server()
  4.     server.serve_forever()

在工作节点中连接到服务端。

  1. if __name__ == '__main__':
  2.     manager = MyManager(address=('127.0.0.1', 5000), authkey=b'mypassword')
  3.     manager.connect()
  4.     shared_data = manager.SharedData()

在工作节点中执行任务,访问共享数据和方法。

  1. shared_data.set_variable('Hello from worker!')
  2. print(shared_data.get_variable())

需要注意的是,分布式进程编程中需要确保服务端和工作节点的代码能够访问相同的MyManager类和共享数据的定义。此外,还需要确保服务端和工作节点使用相同的address和authkey参数进行连接。

以上只是一个简单的示例,实际的分布式进程编程可能涉及更多的细节和复杂性,如任务的分发和结果的收集等。multiprocessing模块提供了更多的功能和工具,可用于构建更复杂的分布式系统。

# 四、正则表达式

正则表达式:可以使用re模块来进行正则表达式的匹配和处理。re模块提供了一系列函数和方法,用于操作和处理字符串。

下面是一些常用的正则表达式操作函数和方法:

re.match(pattern, string, flags=0):尝试从字符串的起始位置匹配一个模式。如果匹配成功,返回一个匹配对象;如果匹配失败,返回None。

re.search(pattern, string, flags=0):在字符串中搜索匹配指定模式的第一个位置。如果匹配成功,返回一个匹配对象;如果匹配失败,返回None。

re.findall(pattern, string, flags=0):查找字符串中所有匹配指定模式的子串,并以列表形式返回。

re.finditer(pattern, string, flags=0):查找字符串中所有匹配指定模式的子串,并以迭代器形式返回。

re.sub(pattern, repl, string, count=0, flags=0):在字符串中查找匹配指定模式的子串,并将其替换为指定的字符串。可以指定替换的次数。

这些函数和方法中的pattern参数是正则表达式模式,string参数是要匹配的字符串,flags参数用于指定匹配的选项。

下面是一个简单的示例代码,演示如何使用正则表达式在字符串中查找匹配的子串:

  1. import re
  2. # 匹配邮箱地址
  3. pattern = r'\w+@\w+\.[a-z]{2,3}'
  4. string = 'My email is john@example.com. Please contact me at john@example.com.'
  5. match = re.search(pattern, string)
  6. if match:
  7.     print('Email found:', match.group())
  8. else:
  9.     print('Email not found.')
  10. # 查找所有匹配的邮箱地址
  11. matches = re.findall(pattern, string)
  12. if matches:
  13.     print('All emails:', matches)
  14. else:
  15.     print('No emails found.')

在以上示例中,使用正则表达式模式\w+@\w+\.[a-z]{2,3}匹配邮箱地址。re.search()函数在字符串中搜索第一个匹配的邮箱地址,re.findall()函数查找所有匹配的邮箱地址。

需要注意的是,正则表达式是一种强大而灵活的工具,可以用于各种字符串匹配和处理的场景。在编写正则表达式时,需要了解正则表达式的语法规则和特殊字符的含义,并根据实际需求进行调整和优化。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号