【1.0】基础串联

【一】解释型语言和编译型语言

  • C语言:

    • C语言的代码可以在不同平台上进行编译,因为C是一种面向过程的编程语言,没有对特定平台的依赖。
    • 为了在不同平台上进行编译,需要使用相应平台的编译器来将C代码编译为可执行文件。
    • 跨平台运行指的是通过编写平台无关的代码,使得代码可以在多个不同的操作系统上运行。
  • JAVA:

    • Java语言被称为“一次编译,处处运行”,这是因为它采用了跨平台的设计思想。

    • 在Java中,源代码会被编译成字节码文件(.class文件),而不是直接编译成机器码。

    • 字节码文件可以在任何支持Java虚拟机(JVM)的操作系统上运行。JVM是一个软件层,提供了一个独立于硬件平台的运行环境。

    • Java虚拟机负责将字节码文件解释执行或者进行及时编译以提高执行效率。

    • Java有不同的版本和平台

      • JDK(Java Development Kit)

        • 岗位:Java开发工程师、软件工程师、Java开发团队负责人等。
        • 工作内容:使用JDK提供的开发工具和资源,进行Java应用程序的开发、调试、测试和部署。编写Java代码,使用JDK中的编译器进行编译,运行Java应用程序。
      • JRE(Java Runtime Environment)

        • 岗位:Java应用支持工程师、技术支持工程师等。
        • 工作内容:安装、配置和维护JRE环境,为用户提供Java应用程序的运行支持。解决Java应用程序运行时的问题,进行故障诊断和修复。
      • JVM(Java Virtual Machine)

        • 岗位:Java虚拟机工程师、性能优化工程师等。
        • 工作内容:设计、开发和优化Java虚拟机,负责执行Java字节码文件。处理内存管理、垃圾回收、即时编译等问题,提升Java应用程序的性能和稳定性。
      • Java SE(Java Standard Edition)

        • 岗位:Java应用开发工程师、桌面软件开发工程师等。
        • 工作内容:使用Java SE提供的核心类库和API,开发桌面应用程序、命令行工具等。包括文件操作、网络通信、多线程编程等。
      • Java ME(Java Platform, Micro Edition)

        • 岗位:嵌入式软件工程师、移动应用开发工程师等。
        • 工作内容:使用Java ME开发小型应用程序,适用于资源受限的嵌入式设备和移动设备。包括开发手机应用、嵌入式系统应用等。
      • Java EE(Java Platform, Enterprise Edition)

        • 岗位:Java后端开发工程师、Web开发工程师、企业应用架构师等。
        • 工作内容:使用Java EE构建企业级应用程序,涉及分布式计算、Web服务、消息传递等领域。开发大型Web应用、企业级系统、中间件等。
      # JDK(Java Development Kit):
      JDK是Java开发工具包,它包含了用于开发Java应用程序的工具和资源。
      # 主要包括以下组件:
      - 编译器(javac):用于将Java源代码编译成字节码文件(.class文件)。
      - 运行时环境(JRE):JDK内置了一个供开发者测试和运行Java应用程序的JRE。
      - 调试器(jdb):用于调试Java应用程序,允许开发者在运行时跟踪、检查和修改代码。
      - 文档生成器(javadoc):用于从源代码中自动生成文档,有助于开发者编写和维护项目文档。
      
      # JRE(Java Runtime Environment):
      JRE是Java运行时环境,提供了Java应用程序运行所需的一系列库和组件。
      # 主要包括以下组件:
      - Java虚拟机(JVM):JVM是JRE的核心组件,负责执行Java字节码文件。
      - Java类库:JRE包含了一系列易于使用的类库,用于实现常见的功能,如文件操作、网络通信等。
      - Java插件:JRE还包括用于在浏览器中运行Java小程序(Applet)的插件。
      
      # JVM(Java Virtual Machine):
      JVM是一个在计算机上模拟执行Java字节码的虚拟机。它是Java的核心部分,负责解释和执行Java字节码,并提供一系列运行时环境支持。
      # JVM具有以下特点:
      - 跨平台性:由于JVM与底层硬件无关,Java字节码可以在任何支持JVM的平台上运行。
      - 内存管理:JVM负责动态分配内存,并自动进行垃圾回收(Garbage Collection)。
      - 安全机制:JVM通过访问控制、异常处理等机制提供了安全性保障。
      
      # Java SE(Java Standard Edition):
      Java SE是Java的标准版,提供了开发和部署Java应用程序所需的基本功能和API。
      它包括核心类库、Java虚拟机、开发工具等。
      
      # Java ME(Java Platform, Micro Edition):
      Java ME是Java的微型版,主要用于嵌入式设备和移动设备上开发小型应用程序。
      Java ME提供了精简的Java API和配置文件,以适应资源受限的环境。
      
      # Java EE(Java Platform, Enterprise Edition):
      Java EE是Java的企业版,提供了构建企业级应用程序所需的一系列API和规范。
      Java EE支持分布式计算、Web服务、消息传递等企业级应用开发。
      它包含了许多技术,如Servlet、JSP、EJB、JPA等。
      
  • GO:

    • Go语言是一种编译型语言,类似于C语言。与C语言不同的是,Go语言有垃圾回收机制,更加安全、易用。
    • Go语言可以进行跨平台编译,即通过在一个特定平台上编译Go代码,可以生成其他平台的可执行文件。例如,Windows平台上可以编译出Mac平台的可执行文件。
  • Python:

    • Python是一种强类型语言,它在变量使用前需要声明变量的类型,并且变量的类型不能随意改变。
    • Python是一种解释型语言,不需要显式的编译过程,代码在运行时逐行被解释执行。
  • JavaScript:

    • JavaScript通常在浏览器上运行,用于实现网页的交互功能。
    • 除了在浏览器上运行,还可以使用Node.js将JavaScript代码作为服务器端脚本语言来运行。
  • PHP:

    • PHP是一种广泛用于web开发的编程语言。
    • PHP主要用于在服务器端生成动态网页内容,生成的内容会在发送给客户端浏览器前进行解析和处理。
    • PHP支持与数据库的交互,可以用于构建复杂的web应用程序。

【1】解释型语言

(1)介绍

  • 解释型语言是一种在运行时逐行解释并执行的语言。
  • 源代码不需要事先经过编译,而是直接由解释器逐行解析并执行。
  • 常见的解释型语言包括Python、Ruby和JavaScript等。

(2)特点:

  • 简单易学:

    • 解释型语言通常使用简洁的语法,对初学者更加友好。
  • 跨平台性:

    • 由于解释型语言的运行依赖于解释器,因此在不同平台上都可以运行。
  • 动态性:

    • 解释型语言具有更高的动态性,可以在运行时进行代码修改和调试,更适合交互式开发环境。

【2】编译型语言

(1)介绍

  • 编译型语言是一种需要事先将源代码通过编译器转换成机器码(或字节码)的语言,然后再执行。
  • 编译器在编译阶段会对整个源代码进行静态分析、优化和转换。常见的编译型语言包括C、C++和Java等。

(2)特点:

  • 执行效率高:

    • 编译型语言将源代码转换成机器码后执行,效率通常比解释型语言高。
  • 静态类型检查:

    • 编译型语言通常需要在编译阶段进行类型检查,可以提前发现潜在的错误。
  • 可移植性差:

    • 由于编译型语言的执行依赖于目标机器的硬件和操作系统,因此可移植性相对较差。

【二】强类型动态语言和弱类型动态语言

强类型:不同数据类型之间不能做转换(如加减) - Python、JavaScript

弱类型:不同数据类型之间能做转换(如加减) - PHP

【1】强类型动态语言

(1)介绍

  • 强类型动态语言是指在编译或运行时对数据类型进行严格检查的语言
  • 要求变量在使用之前必须明确指定其类型,并且不允许隐式类型转换
  • 类型错误将导致编译错误或运行时错误。
  • 常见的强类型动态语言包括Python、Ruby和JavaScript等。

(2)特点:

  • 类型安全:

    • 强类型动态语言对类型进行严格检查,可以在编译或运行时捕获类型错误,提高代码的健壮性。
  • 易于调试:

    • 由于类型检查的严格性,错误往往可以被快速发现和修复,提高了调试效率。
  • 代码可读性高:

    • 在强类型动态语言中,变量的类型信息显式表达,使得代码易于理解和维护。

【2】弱类型动态语言

(1)介绍

  • 弱类型动态语言是指在编译或运行时对数据类型进行较宽松检查的语言
  • 允许隐式类型转换,并且在操作中动态确定变量的类型
  • 常见的弱类型动态语言包括PHP和Perl等。

(2)特点:

  • 灵活性:

    • 弱类型动态语言允许在不同类型之间进行隐式转换,提供了更大的灵活性和方便性。
  • 开发效率高:

    • 由于变量的类型可以在运行时动态确定,弱类型动态语言的开发效率通常较高。
  • 容错性较差:

    • 弱类型动态语言对类型的宽松检查可能导致一些隐藏的错误,在编译时无法捕获,需要更加小心地处理类型相关的问题。

【三】Python全栈开发可从事方向

【1】Python后端开发

(1)做网站(前后端):

  • 使用Python的Web框架(如Django、Flask等)进行后端开发,与前端页面进行交互,实现完整的网站功能。
  • 可以使用模板引擎(如Jinja2)生成动态HTML页面,也可以提供API接口供其他应用调用。

(2)前端可以是app:

  • 使用Python的移动应用开发框架(如Kivy、PyQt等)进行移动应用开发,实现与后端交互、展示数据和实现业务逻辑。

(3)小程序的Python后端:

  • 使用Python的框架(如Django、Flask等)开发小程序后端,处理小程序发送的请求,与数据库进行交互并返回相应的数据。

(4)构建RESTful API:

  • 使用Python的Web框架搭建API服务,实现前后端分离的架构,支持不同客户端(如Web、移动应用等)的请求和数据交互。

(5)数据库设计与优化:

  • 使用Python的数据库ORM库(如SQLAlchemy、Peewee等)进行数据库表结构设计、查询和性能优化,提升数据存储和访问效率。

(6)编写业务逻辑:

  • 根据需求和业务规则,使用Python编写后端业务逻辑,包括用户认证授权、数据处理和计算、消息队列等功能的实现。

【2】自动化运维

(1)收集服务器软硬件信息(cmdb):

  • 使用Python编写脚本或应用程序,通过调用系统命令或使用第三方库获取服务器的软硬件信息,将其存储到CMDB(配置管理数据库)中进行统一管理和查询。

(2)jumpserver(堡垒机):

  • 使用Python开发或配置JumpServer,实现服务器访问的权限控制和审计能力,提高服务器的安全性。

(3)sql审批:

  • 使用Python编写相关脚本或应用程序,对数据库操作进行审批和记录,确保数据库的操作符合规范和安全要求。

(4)监控网站情况:

  • 使用Python的监控库(如Prometheus、Nagios等)开发监控系统,实时监测网站的状态、性能以及异常情况,并及时发送通知。

(5)日志收集处理:

  • 使用Python编写日志收集脚本,将服务器或应用程序的日志数据收集并进行处理,例如存储、分析和可视化展示。

(6)配置管理与自动化部署:

  • 使用Python编写自动化脚本或配置管理工具(如Ansible、SaltStack等),实现服务器配置的自动化管理和应用的快速部署。

(7)故障监控和告警:

  • 利用Python的监控库(如Zabbix、Prometheus等)对服务器的性能、服务状态进行监测,并通过邮件、短信等方式发送告警信息。

(8)自动化测试环境搭建:

  • 搭建自动化测试环境,使用Python编写自动化测试脚本或利用工具(如Jenkins)进行自动化测试任务调度和结果分析。

【3】自动化测试

(1)selenium:

  • 使用Python的Selenium库进行Web应用的自动化测试,模拟用户在浏览器中的操作,如点击、输入等,并进行相关断言和验证。

(2)appnium:

  • 使用Python编写Appium自动化测试脚本,实现对移动应用的UI自动化测试,包括模拟用户操作和验证应用的功能和性能。

(3)pytest:

  • 使用Python的pytest框架编写测试用例和测试脚本,进行自动化测试的管理和执行。

【4】数据分析

(1)数据清洗和预处理:

  • 使用Python的数据处理库进行数据清洗、缺失值处理、异常值检测以及数据格式转换等工作,确保数据的准确性和可用性。

(2)数据可视化:

  • 利用Python的可视化库(如Matplotlib、Plotly等)进行数据的可视化展示,帮助业务用户理解和分析数据。

(3)实时大数据分析:

  • 基于Python的分布式计算框架(如Spark、Dask等),进行实时大数据的处理和分析,包括数据挖掘、机器学习模型训练和推理等。

(4)其他

  • 使用Python的数据分析库(如Pandas、NumPy等)对大规模的数据进行清洗、转换和统计分析,并使用可视化工具(如Matplotlib、Seaborn等)展示和呈现分析结果。
  • 利用Python的机器学习库(如Scikit-learn、TensorFlow等)进行数据挖掘、模型训练和预测,以实现更深入的数据分析和洞察。
  • 可以结合数据库(如MySQL、PostgreSQL等)和大数据处理框架(如Hadoop、Spark等)进行数据存储和分布式计算,提高数据分析的效率和处理能力。

【5】爬虫

(1)动态网页爬取:

  • 使用Python的无头浏览器库(如Selenium、Pyppeteer等)模拟用户行为,爬取动态加载的网页内容。

(2)数据存储与处理:

  • 将爬取的数据存储到数据库(如MySQL、MongoDB等)或其他数据存储介质,并进行数据清洗、去重和分析等工作。

(3)反爬虫策略应对:

  • 研究反爬虫机制并使用Python编写相应代码,如设置代理IP、使用随机User-Agent、处理验证码等,以应对网站的反爬虫限制。

(4)其他

  • 使用Python的爬虫框架(如Scrapy、BeautifulSoup等)编写爬虫程序,从指定的网页中提取数据,并进行清洗、存储和后续处理。
  • 可以结合多线程、分布式、代理IP等技术手段提升爬虫的抓取速度和稳定性。
  • 遵守合法、合规的爬虫原则,并遵循网站的使用规范,确保爬虫的合法性和持续可用性。

【6】量化交易

  • 使用Python的量化交易平台(如vn.py、rqalpha等)开发策略,并利用Python的金融数据分析库(如Pandas、NumPy等)进行数据处理和策略回测。
  • 使用Python的交易所API(如币安API、股票交易所API等)获取实时行情和交易数据,并进行交易执行和持仓管理。
  • 结合机器学习和深度学习等技术,开发高频交易、量化套利等策略,并进行模拟和实盘交易。

【7】人工智能/图像处理

  • 使用Python的深度学习框架(如TensorFlow、PyTorch等)进行图像识别、目标检测、语音识别等人工智能相关的任务。
  • 使用Python的图像处理库(如OpenCV、PIL等)进行图像的预处理、特征提取和图像增强等操作。
  • 应用领域包括人脸识别、智能图像分析、自然语言处理等。

【8】安全方向

(1)端口扫描:

  • 使用Python编写端口扫描工具,对目标主机进行端口的快速扫描,发现潜在的漏洞和安全风险。

(2)弱口令扫描:

  • 开发Python脚本或应用程序,通过字典破解或暴力破解密码,对系统中存在的弱口令进行扫描和检测,并提供相应的安全建议。

(3)SQL注入:

  • 使用Python编写SQL注入检测工具,对Web应用进行检测,发现潜在的SQL注入漏洞,并给出修复建议。

(4)CSRF攻击:

  • 使用Python编写CSRF攻击模拟脚本,测试Web应用的CSRF防护措施的有效性,并提供相应的修复方案。

(5)XSS攻击:

  • 使用Python编写XSS攻击模拟脚本,测试Web应用的XSS防护措施的有效性,并提供相应的修复方案。

【9】网络方向

(1)网络协议设计与调优:

  • 使用Python编写网络应用程序,实现自定义的网络协议通信,并对网络传输进行性能调优和安全加固。

(2)网络流量分析与监测:

  • 利用Python的网络库和数据处理库,对网络流量进行深度分析和监测,识别潜在的安全问题和异常流量。

(3)网络安全防护:

  • 使用Python开发网络安全工具,如入侵检测系统(IDS)、防火墙等,对网络进行实时监控和攻击防护。

(4)其他

  • 使用Python的网络库(如socket、Twisted等)进行网络应用开发,实现网络通信、数据传输和协议解析等功能。
  • 编写Python脚本或应用程序,对网络设备进行配置和管理,实现网络拓扑的自动化维护和实时监控。
  • 研究和应用Python的网络安全库(如Scapy、Paramiko等),进行网络嗅探、攻防、流量分析等相关工作。

【10】物联网方向

(1)硬件交互 - socket:

  • 使用Python的socket库实现与硬件设备的通信,通过网络或串口与设备进行数据交互,并实现相应的业务逻辑。

(2)物联网平台开发:

  • 使用Python构建物联网平台,支持设备接入管理、数据采集和云端控制等功能,实现物联网设备的集中管理和监控。

(3)边缘计算与数据处理:

  • 利用Python在边缘设备上进行数据的预处理和分析,减少数据传输量,提高物联网应用的实时性和效率。

(4)安全与隐私保护:

  • 研究和应用Python的物联网安全框架和加密算法,确保物联网设备的安全通信和数据隐私保护。

(5)其他

  • 使用Python的物联网平台(如AWS IoT、Azure IoT等)进行物联网设备的接入、数据收集和远程控制。
  • 利用Python的Web框架和数据库,开发物联网应用的后台系统,实现设备管理、数据分析和可视化展示。

【11】其他方向参考

(1)Docker容器化:

  • 使用Python开发Docker镜像和容器,实现应用的快速部署和跨平台运行。

(2)消息队列和异步任务:

  • 利用Python的消息队列工具(如RabbitMQ、Celery等)处理异步任务和分布式系统中的消息传递。

(3)WebScraping:

  • 使用Python编写爬虫程序,从网页中提取数据或信息,并进行后续处理和分析。

(4)微服务架构:

  • 基于Python的框架(如Flask、FastAPI等)开发微服务,实现模块化和可扩展的应用程序。

(5)DevSecOps:

  • 结合DevOps和安全的概念,在软件生命周期中加入安全性考虑,进行漏洞扫描、安全测试等工作。
  • DevOps工程师:
    • 负责开发、测试、部署和运维整个软件生命周期的自动化流程,包括持续集成、持续交付和持续部署等。
    • 利用Python的脚本编写能力和工具链,实现自动化的构建系统、部署系统和监控系统。

(6)区块链应用开发:

  • 利用Python开发智能合约、去中心化应用程序(DApp)等区块链应用。
  • 例如开发智能合约、分布式应用程序以及创建和管理区块链网络等。

(7)语音识别和处理:

  • 使用Python库(如SpeechRecognition、pydub等)实现语音识别和语音处理功能。

(8)推荐系统:

  • 使用Python实现推荐算法和推荐系统,对用户行为和偏好进行分析和预测。

(9)物联网云平台:

  • 基于Python的物联网云平台开发,实现设备连接、数据收集和远程控制等功能。

(10)多媒体应用开发:

  • 使用Python的多媒体库(如pygame、pyglet等)开发游戏、图形编辑器等应用

(11)虚拟化和云计算:

  • Python在虚拟化技术和云计算领域也有重要应用,可用于自动化虚拟机管理、云资源管理、容器调度和云平台开发等方面。

(12)人脸识别和图像处理:

  • Python库(如OpenCV)提供了丰富的图像处理和计算机视觉函数,可用于人脸识别、图像处理和图像识别等人工智能领域的应用。

(13)自然语言处理:

  • Python的自然语言处理库(如NLTK)提供了处理和分析文本数据的工具,可用于机器翻译、情感分析、问答系统等应用。

(14)大数据处理和分布式计算:

  • Python在大数据处理和分布式计算领域也有广泛应用,例如使用PySpark进行大规模数据处理和分析。

【四】数据类型之一切皆对象

【1】一切皆对象

  • Python中的一切都被视为对象。
  • 在Python中,每个值(包括数字、字符串、函数等)都是一个对象
    • 并且每个对象都属于特定的类(class)。
    • 类定义了对象的属性和行为。
    • 因此,可以说Python是一种面向对象的编程语言。
  • 对象是类的实例化结果
    • 可以通过调用类来创建对象。
  • 例如,可以使用类名后跟括号来创建一个对象:
class MyClass:
    pass

obj = MyClass()
  • 在上面的例子中

    • MyClass是对象obj的模板
      • 我们可以使用它来创建任意数量的对象。
    • 每个对象都具有相同的属性和方法
      • 但其内部数据可能会有所不同。
  • 由于一切皆对象,因此对象可以作为参数传递给函数,也可以赋值给变量。

  • 这使得Python具有动态性和灵活性,可以方便地进行编程和扩展。

  • 总结起来
    • Python中的一切都是对象
      • 对象是类的实例化结果
    • 每个对象都具有相应的属性和方法
      • 从而实现了面向对象编程的特性。

【2】type 和 object的关系

a = int(2)
  • a = int(2) 创建了一个int类型的对象。
  • int是一个类,具体的实现是由C语言实现的,在Python中被称为内置类型。
    • 如果写了pass,看不到源码 - 实现是由C语言实现的
    • 有一部分的代码时可以看到的 - 重写方法
print(type(1))
# <class "int">
print(type(int))
# <class "type">
print(type(dict))
# <class "type">
print(type(type))
# <class "type">
print(type(object))
# <class "type">
  • type是一个类:
    • 在Python中,type本身也是一个类,它是所有对象类型的元对象(metaclass)。
    • 可以将type看作是创建类的类,通过调用type()可以创建新的类。
  • 类型对象是type的实例:
    • 在Python中,int、dict等类型都是type类的实例
    • 这意味着它们的类型也是type。
  • 继承关系(type 继承了 object/type 是 object 的类):
    • int、dict等类型并不是直接继承自object,而是通过继承type来间接继承object。
    • 也就是说,type是int、dict等类型的元类(metaclass)
    • 而object是type的超类。
  • type是自己的类(type 是 type自己的类):
    • type类本身也是一个对象,可以说type是type的实例。
    • 这是因为type是其自身的元类。
  • 所有类,除了object。都继承自object,包括type
  • int、dict都是 type 类的对象
  • int、dict 继承了 object
  • type 和 object的关系
    • type 是 object 的类
    • type 继承了 object
    • type 是 type自己的类

【3】一切皆对象的好处

(1)详解

  • 统一的操作方式:
    • 由于一切皆对象,不论是数字、字符串、函数还是其他类型的对象,都可以使用相同的方式进行操作和访问。
    • 这样可以简化代码并提高可读性,不需要记住不同类型之间的特定操作规则。
  • 灵活的赋值和传参:
    • 在Python中,变量实际上是指向对象的引用,而不是直接存储值本身。
    • 这意味着可以将一个变量赋值给另一个变量,这两个变量将指向同一个对象。
    • 无论对象是什么类型,都可以通过赋值进行传递和共享数据,这种灵活性使得编写和使用函数更加简单。
  • 动态特性:
    • Python是一门动态语言,对象的类型可以在运行时改变。
    • 通过修改对象的属性或者动态添加方法,可以根据需要改变对象的行为。
    • 这种动态特性使得Python非常灵活,并且适合进行编写脚本和快速开发。
  • 方便的扩展性:
    • 由于一切皆对象,可以很方便地扩展现有对象的功能。
    • 通过继承、组合和动态添加属性和方法,可以创建新的对象类型,并根据实际需求来定制功能。
  • 内省和元编程:
    • 一切皆对象的特性使得Python拥有强大的内省能力,可以在运行时获取对象的信息、属性和方法。
    • 这种内省能力结合元编程的概念,可以实现高级的动态代码生成和修改,从而扩展Python语言本身。

(2)示例

  • 在Python中,变量实际上是名称和对象之间的绑定关系。

    • 当我们使用赋值语句给一个变量赋值时

      • 实际上是将该变量与一个对象关联起来,并指向该对象在内存中的地址。
    • 不同类型之间的变量直接可以相关赋值

      • 其实本质,变量都是指向了一个内存地址
a = 100
a = "xxx"
  • 首先,Python解释器会创建一个整数对象,并将其存储在内存中的某个地址上。
    • 然后,将变量名a与该地址进行绑定,使得a指向这个整数对象。
  • 其次,Python解释器会创建一个字符串对象,并将其存储在内存中的另一个地址上。
    • 然后,将变量名a重新与新的地址进行绑定,使得a指向这个字符串对象。
  • 这说明了Python中变量的本质是指向内存地址的指针。
    • 通过赋值语句,我们可以修改变量的指向,使其指向不同类型的对象。
  • 总结来说
    • 变量在Python中本质上是指向内存地址的指针。
    • 通过赋值语句,我们可以修改变量的指向,使其指向不同类型的对象。

【4】深浅拷贝问题引入

在Python中,深拷贝和浅拷贝是用于复制对象的两种不同的方式。

l = [1, 2, 3, [4, 5, 6, ]]
l2 = l
print(l2 is l)
# True
  • 通过
    • l2赋值为l
    • 实际上是将l2l指向相同的内存地址。
  • 所以
    • l2 is l的结果为True
    • 表示它们是同一个对象的两个引用。
  • 因此
    • l2l的修改都会影响到另一个。

(1)浅拷贝

from copy import copy

l3 = copy(l)
print(l3 is l)
# False

l3[3][1] = 999
print(l)
# [1, 2, 3, [4, 999, 6]]
print(l3)
# [1, 2, 3, [4, 999, 6]]
  • 使用copy()函数进行浅拷贝时
    • 创建了一个新的列表对象l3
      • 并复制了原始列表l中的元素。
    • 这意味着l3l是两个独立的对象
      • 分别指向不同的内存地址。
  • 然而,对于嵌套的可变对象(如列表)
    • 浅拷贝只会复制其引用
    • 而不会创建新的内存空间。
  • 因此,l3中的嵌套列表仍然指向相同的地址
    • 所以对l3中嵌套的列表进行修改
    • 也会影响到原始的列表l

(2)深拷贝

from copy import deepcopy

l4 = deepcopy(l)
l4[3][1] = 888
print(l)
# [1, 2, 3, [4, 5, 6]]
print(l4)
# [1, 2, 3, [4, 888, 6]]
  • 通过使用deepcopy()函数进行深拷贝
    • 会递归地复制嵌套对象,包括嵌套列表。
  • 这意味着在深拷贝操作中
    • 不仅会创建一个新的顶层列表对象l4
    • 还会创建一个新的嵌套列表对象
    • 其值与原始列表中的值相同。
  • 因此
    • 在对l4中的嵌套列表进行修改时
    • 并不会影响到原始的列表l
    • 它们指向不同的内存地址。

(3)小结

  • 综上所述
    • 浅拷贝只复制顶层对象
    • 而深拷贝会递归复制整个对象结构。
  • 在涉及到可变对象嵌套的情况下
    • 深拷贝是一种更安全的选项
    • 因为它可以确保对新对象的修改不会影响原始对象。

【5】深浅拷贝问题详解

  • 深拷贝和浅拷贝是常用的操作,它们在处理对象和数据结构时非常有用。
  • 让我们详细解释深拷贝和浅拷贝的概念,并结合案例进行演示。

(1)深拷贝:

  • 深拷贝是指创建一个新的对象,该对象与原始对象完全独立。
  • 换句话说,它会递归地复制所有嵌套对象,包括它们的内容,以便我们在修改新对象时不会影响到原始对象。
  • 下面是一个示例
    • 演示了如何使用copy模块中的deepcopy()函数进行深拷贝:
import copy

list1 = [1, 2, [3, 4]]
list2 = copy.deepcopy(list1)
  • 在这个例子中,我们创建了一个列表list1,其中包含一个嵌套列表。
  • 通过调用deepcopy()函数并将list1作为参数传递给它,我们可以创建一个名为list2的新对象,它是list1的深拷贝。
  • 现在,我们来演示深拷贝是如何避免原始对象的修改的:
list2[0] = 999
print(list1)  # 输出: [1, 2, [3, 4]]
print(list2)  # 输出: [999, 2, [3, 4]]
  • 可以看到,尽管我们修改了list2的第一个元素,但list1保持不变。
  • 这是因为list1list2是独立的对象,它们各自占用着不同的内存空间。

(2)浅拷贝:

  • 浅拷贝是指创建一个新对象,并将原始对象的元素复制到新对象中。
  • 然而
    • 如果原始对象包含可变的对象(如列表)
    • 则新对象中的这些元素仍然与原始对象中相应元素共享相同的内存地址。
  • 下面是一个示例
    • 演示了如何使用copy模块中的copy()函数进行浅拷贝:
import copy

list1 = [1, 2, [3, 4]]
list2 = copy.copy(list1)
  • 在这个例子中
    • 我们使用copy()函数创建了list1的浅拷贝list2
  • 让我们来看一下浅拷贝在修改可变对象时的行为:
list2[0] = 999
list2[2][0] = 777
print(list1)  # 输出: [1, 2, [777, 4]]
print(list2)  # 输出: [999, 2, [777, 4]]
  • 可以看到
    • 当我们修改list2中的第一个元素时
    • 只有list2受到影响
    • list1保持不变。
  • 但是
    • 当我们修改list2中嵌套列表的元素时
    • list1也会随之改变。
  • 这是因为浅拷贝只复制了列表的引用
    • 而没有创建新的内存空间来存储嵌套列表。

(3)综上所述

  • 深拷贝和浅拷贝在处理对象和数据结构时有不同的行为:

    • 深拷贝创建一个完全独立的对象,递归地复制所有嵌套对象。

    • 浅拷贝创建一个新对象,但它共享原始对象中可变对象的引用。

【五】字典为什么叫哈希类型

字典(Dictionary)中的键(Key)必须是不可变类型,这是因为字典使用哈希表(Hash Table)来实现。

  • 哈希表是一种用于快速查找的数据结构,它通过将键映射到特定位置来实现快速访问。
  • 由于哈希表的实现方式,要求字典中的键必须是不可变类型。
    • 不可变类型意味着对象在创建后不能被修改
      • 例如整数、浮点数、字符串、元组等。
    • 这是因为在使用哈希表时,根据键的哈希值来计算位置
      • 如果键是可变类型且在后续操作中发生改变
      • 那么哈希值也会发生变化
      • 导致无法准确访问之前存储的键值对。
  • 同时,字典中的键还需要满足可哈希性(Hashable)。
    • 可哈希性是指对象必须具有一个唯一的哈希值,且该哈希值在对象的生命周期内保持不变。
    • 可哈希性是判断一个对象是否可以作为字典的键的重要条件。
  • 综上所述
    • 字典的键必须是不可变类型且具有可哈希性
    • 这样才能保证字典的正常使用和高效性能。
  • 字典类型被称为哈希类型是因为在其内部实现中使用了哈希表数据结构。
    • 哈希表是一种高效的数据结构
    • 它通过哈希函数将键映射到一个固定大小的数组索引上
    • 从而实现快速的插入、查找和删除操作。
  • 字典类型中的每个键值对都使用哈希函数将键转换成一个唯一的哈希值,并根据该哈希值将键值对存储在哈希表的相应位置上。
    • 当需要对字典进行查找或插入操作时,系统会利用哈希函数计算键的哈希值,并在哈希表中快速找到对应位置的数据。
  • 使用哈希表作为字典类型的内部实现,使得字典可以在平均情况下以常数时间(O(1))完成插入、查找和删除操作。
    • 这些高效的操作特性是由于哈希表能够提供快速的键值对检索能力
    • 即使字典中存储的键值对数量很大也能保持高性能。
  • 因此
    • 字典类型被称为哈希类型是因为其内部实现使用了哈希表这种高效的数据结构来实现快速的插入、查找和删除操作。

【六】可变类型和不可变类型

【1】可变类型

  • 可变类型是指在创建后可以被修改的对象。
  • 与不可变类型相反,可变类型的值可以进行增、删、改等操作,而不会改变其身份标识。
  • 列表(List):

    • 列表是一种用于存储多个元素的可变有序容器。

    • 可以通过索引来访问、添加、删除和修改列表中的元素。

  • 字典(Dictionary):

    • 字典是一种键值对的可变集合,其中的键和对应的值可以任意修改、添加和删除。
  • 集合(Set):

    • 集合是一种无序的、可变的容器,它内部元素的顺序是不确定的。

    • 可以用于去重、判断元素是否存在以及各种集合操作(如交集、并集、差集等)。

  • bytearray:

    • bytearray是一种可变的字节数组,它可以存储整数范围内的字节,且可以进行修改。
  • 在使用可变类型时需要注意的是,由于其可变性,可能会带来一些问题。
    • 例如,在对可变类型进行浅拷贝(shallow copy)或传递给函数时
      • 实际上是传递对象的引用
      • 而不是新建一个独立的对象。
    • 这意味着对于同一个可变对象的修改
      • 将会在所有引用对象中都生效。
    • 这可能会导致意外的副作用。
  • 为了避免这些问题,有时需要采取适当的措施来确保可变对象不会无意中被修改。
    • 可以使用深拷贝(deep copy)创建一个独立的可变对象副本
    • 或使用不可变类型来替代可变类型
    • 在确保不需要修改的情况下使用元组等不可变容器。

【2】不可变类型

  • 不可变类型是指在创建后不能被修改的对象。
  • 与可变类型相反,不可变类型的值一旦确定就无法再次改变。
  • 数字(Number):

    • 包括整数(int)、浮点数(float)、复数(complex)等。

    • 数字类型的值在创建后不可修改,需要重新赋值来改变其值。

  • 字符串(String):

    • 字符串是由字符组成的不可变序列,在创建后不能进行修改。

    • 可以通过切片和拼接等操作来创建新的字符串对象。

  • 元组(Tuple):

    • 元组是有序且不可变的集合,其中的元素在创建后不能被修改。

    • 可以通过索引访问元素,但不能对元素进行增、删、改操作。

  • 不可变集合(frozenset):

    • 不可变集合是一种不可变的集合类型,其中的元素不可被修改。
    • 与可变集合不同,不可变集合本身不能增加或删除元素。

不可变类型具有以下特点:

  • 身份标识不变:不可变对象的身份标识在创建后保持不变,即对象的内存地址不会发生变化。
  • 值不能修改:不可变对象创建后,其值不能再被修改,任何尝试修改值的操作都将引发异常。

使用不可变类型时的注意事项:

  • 需要注意的是

    • 虽然不可变对象本身的值不能被修改

    • 但对于包含可变对象的不可变类型(如包含列表的元组)来说

      • 可变对象的值是可以被修改的。
    • 这会导致不可变类型的某些部分看起来是可变的。

  • 不可变类型对于并发操作具有线程安全性

    • 因为不需要考虑修改的同步问题。
  • 在使用不可变类型时

    • 通常可以避免副作用和意外修改的风险
    • 使代码更加可靠和稳定。

【七】Python中的参数传递是值传递还是引用传递

  • Python中的参数传递都是copy一份传递过去,由于一切皆对象,传过去的都是地址
  • 在Python中又区分可变类型和不可变类型
    • 可变类型在函数中修改会影响原始的值
    • 不可变类型,不会影响原始的值

在Python中,参数传递是按对象引用传递的方式进行的,也可以称为"传对象引用"。

  • 这意味着在函数调用时
    • 参数的值不是直接传递给函数
    • 而是传递了对象的引用(内存地址)。
  • 当我们将一个对象作为参数传递给函数时
    • 实际上传递的是该对象的引用(内存地址)。
    • 函数内部可以通过该引用来访问和操作原始对象。
  • 需要注意的是
    • Python的对象分为可变(mutable)和不可变(immutable)两种类型。
      • 对于不可变类型的对象(如整数、字符串、元组等)
      • 无论是在函数内还是外部对它们进行修改
      • 都会创建新的对象。
      • 因此,看起来像是通过值传递。
    • 对于可变类型的对象(如列表、字典等)
      • 在函数内部对它们的修改会影响到原始对象的值
      • 因为它们在内存中是可变的
      • 而函数传递的是对象的引用。
  • 下面通过示例代码演示参数传递的方式:
def update_list(lst):
    lst.append(4)

my_list = [1, 2, 3]
update_list(my_list)
print(my_list)  # 输出 [1, 2, 3, 4]

def update_string(str):
    str += " World"

my_str = "Hello"
update_string(my_str)
print(my_str)  # 输出 "Hello"

  • 在第一个示例中
    • 我们将一个列表对象传递给函数
    • 并在函数内部修改了该列表。
    • 这种修改会反映到原始列表对象上。
  • 而在第二个示例中
    • 我们将一个字符串对象传递给函数
      • 并在函数内部进行了字符串拼接操作。
    • 虽然函数内部的操作看起来修改了字符串
      • 但实际上是重新创建了一个新的字符串对象
      • 并没有改变原始的字符串对象。

综上所述,可以说Python的参数传递是按对象引用传递的方式进行的,具体表现根据对象的可变性而定。

【八】字符编码

【1】计算机的计量单位

计算机的计量单位主要包括存储容量、处理速度和数据传输速率等。

(1)存储容量的计量单位:

  • 位(bit):表示最小的存储单位,取值为0或1。
  • 字节(Byte):8个位组成一个字节,是计算机中常用的最小存储单位。
  • 千字节(KB):1 KB = 1024 字节,约等于1000 字节。
  • 兆字节(MB):1 MB = 1024 KB,约等于1000 KB。
  • 吉字节(GB):1 GB = 1024 MB,约等于1000 MB。
  • 太字节(TB):1 TB = 1024 GB,约等于1000 GB。
  • 拍字节(PB):1 PB = 1024 TB,约等于1000 TB。
  • ASCII(美国标准信息交换码):
    • ASCII 是最早的字符编码之一
      • 于 1963 年开发
      • 使用 7 位二进制数表示 128 个字符
      • 包括英文字母、数字和一些常用符号。
  • 扩展 ASCII:
    • 为了满足其他语言的字符需求
      • 扩展 ASCII 字符编码在 ASCII 的基础上增加了一个额外的 8 位来表示更多字符。
    • 其中,ISO-8859 系列是常见的扩展 ASCII 编码
      • 如 ISO-8859-1(西欧字符集)。
  • Unicode(统一码):
    • 为了解决不同国家和地区字符集的混乱问题,Unicode 应运而生。
    • Unicode 采用 16 位的编码空间,能够表示更多的字符
    • 目前版本的 Unicode 可以容纳超过 13 万个字符。
    • 最常见的编码方式是 UTF-8、UTF-16 和 UTF-32。
  • UTF-8:
    • UTF-8 是一种变长字符编码方式
    • 可以用 1 至 4 个字节来表示一个字符。
    • 它兼容 ASCII 编码
      • 在表示 ASCII 字符时只需使用 1 个字节。
  • UTF-16:UTF-16 使用固定的 16 位编码表示字符,可以表示 Unicode 码点从 U+0000 到 U+FFFF 的字符。对于码点大于 U+FFFF 的字符,UTF-16 采用了一种特殊的编码方式来表示。
  • UTF-32:UTF-32 使用 32 位的编码空间来直接表示 Unicode 码点,每个字符都占用 4 个字节。UTF-32 简化了字符编码和处理过程,但相对于 UTF-8 和 UTF-16,它需要更多的存储空间。
  • UTF-8 是一种变长字符编码方式
    • 它可以用不同长度的字节来表示不同范围的 Unicode 字符。

具体的表示规则如下:

  • 对于 ASCII 字符(码点范围:U+0000 到 U+007F)

    • UTF-8 使用一个字节表示,即 8 位二进制数,前面以 0 开头。
  • 对于以字节 110xxxxx 开头的连续 2 个字节

    • 表示的是 Unicode 码点范围为 U+0080 到 U+07FF 的字符。
    • 其中,xxxxx 是对应字符在 UTF-8 编码中的后 5 位。
  • 对于以字节 1110xxxx 开头的连续 3 个字节

    • 表示的是 Unicode 码点范围为 U+0800 到 U+FFFF 的字符。
    • 其中,xxxx 是字符在 UTF-8 编码中的后 4 位。
  • 对于以字节 11110xxx 开头的连续 4 个字节

    • 表示的是 Unicode 码点范围为 U+10000 到 U+10FFFF 的字符。
    • 其中,xxx 是字符在 UTF-8 编码中的后 3 位。
  • 这种变长编码方式使得 UTF-8 可以灵活地表示多种字符

    • 根据字符所处的 Unicode 码点范围
    • 使用不同长度的字节进行编码。
  • 相比于定长编码方式(如 UTF-16 和 UTF-32)

    • UTF-8 在表示 ASCII 字符时非常节省空间
    • 而且对于常用的字符集(如拉丁字母、汉字等)
    • 使用较少的字节进行编码。
  • 这使得 UTF-8 成为广泛应用于互联网、操作系统和软件开发中的一种字符编码方式。

(2)处理速度的计量单位:

  • 赫兹(Hz):指代每秒钟的处理周期数。常见的处理器时钟频率单位有 MHz(百万赫兹)、GHz(十亿赫兹)等。

(3)数据传输速率的计量单位:

  • 比特每秒(bps):数据传输速率的基本单位,表示每秒传输的比特数。
  • 千比特每秒(Kbps):1 Kbps = 1024 bps,约等于1000 bps。
  • 兆比特每秒(Mbps):1 Mbps = 1024 Kbps,约等于1000 Kbps。
  • 吉比特每秒(Gbps):1 Gbps = 1024 Mbps,约等于1000 Mbps。

【九】闭包函数

  • 闭包是指在一个函数内部定义的函数,并且这个内部函数可以访问到其外部函数的变量,即使外部函数已经执行完毕并返回了。

下面是一个简单的闭包函数示例:

def outer_func(x):
    def inner_func(y):
        return x + y
    return inner_func

closure = outer_func(10)
result = closure(5)
print(result)  # 输出 15
  • 在上述示例中
    • outer_func 是外部函数,它接受一个参数 x
    • outer_func 内部,定义了一个嵌套的函数 inner_func,它接受一个参数 y,并返回 x + y 的结果。
    • outer_func 最后返回了 inner_func 这个函数对象。
  • 当调用 outer_func(10) 时,会返回一个内部函数 inner_func,并且在内部函数中,x 的值被保留为 10。
  • 然后,可以将返回的 inner_func 赋值给一个变量 closure
  • 随后,我们通过 closure(5) 调用 inner_func,传入参数 5。
  • 此时,inner_func 内部可以访问到外部函数 outer_func 中的变量 x(其值为 10),然后将 5 加上 10,得到结果 15。
  • 闭包的特点是内部函数可以访问到外部函数的变量,即使外部函数已经执行完毕。
  • 这种特性提供了一种机制,可以在函数内部创建数据的私有性,防止泄露到全局作用域中,同时也可以实现数据的持久化,使得函数对象能够记住之前的状态。
  • 闭包在很多情况下非常有用,例如函数工厂、回调函数等场景。

【十】函数被称为一等公民(First-Class Citizen)

这意味着函数在语言中拥有与其他数据类型相同的地位和能力。

以下是函数作为一等公民的一些特点:

  • 函数可以被赋值给变量

    • 可以将函数对象赋值给一个变量,从而使变量引用该函数。
  • 函数可以作为参数传递:

    • 函数可以作为另一个函数的参数传递给它,使得可以在函数内部调用传递的函数对象。
  • 函数可以作为返回值返回:

    • 函数可以作为另一个函数的返回值,使得可以返回一个函数对象。
  • 函数可以存储在数据结构中:

    • 函数可以像其他数据类型一样存储在列表、字典或其他数据结构中。
  • 函数可以动态创建:

    • 在运行时,可以根据需要动态创建函数对象。
  • 函数作为一等公民的特性,使得在Python中可以更加灵活地使用函数。
  • 它们可以用来实现回调函数、高阶函数、装饰器等功能,使得代码更加模块化、可重用和可扩展。
  • 通过利用函数的一等公民地位,Python提供了丰富的函数式编程特性,并且能够轻松地处理函数作为数据的各种操作和组合。

【十一】装饰器

  • 装饰器在Python中是一种用于修改函数或类行为的特殊语法。
  • 它是一种以函数作为参数并返回函数的高阶函数。
  • 通过装饰器,可以在不修改原始函数定义的情况下,给函数添加额外的功能或修改其行为。

以下是装饰器的详细解释:

【1】函数装饰器:

  • 函数装饰器是最常见的装饰器类型。
    • 它是一个接受函数作为参数,并返回一个新函数的函数。
  • 装饰器函数可以在调用被装饰函数之前或之后执行一些额外的操作
    • 例如记录日志、统计执行时间、验证权限等。
    • 通过使用@语法糖将装饰器应用到函数上,可以方便地在函数定义时对其进行修饰。

示例:

def decorator(func):
    def wrapper(*args, **kwargs):
        # 执行一些额外的操作
        result = func(*args, **kwargs)
        # 执行一些额外的操作
        return result
    return wrapper

@decorator
def my_function():
    # 函数逻辑
    pass

【2】类装饰器:

  • 除了函数装饰器,Python还支持类装饰器。
  • 类装饰器是指实现了__call__方法的可调用对象,该对象可以像函数装饰器一样接受函数作为参数并修改其行为。
  • 与函数装饰器相比,类装饰器更适合用于全局级别的装饰操作,因为类实例可以在整个应用程序的生命周期内保持状态。

示例:

class Decorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        # 执行一些额外的操作
        result = self.func(*args, **kwargs)
        # 执行一些额外的操作
        return result

@Decorator
def my_function():
    # 函数逻辑
    pass

【3】多层装饰器:

  • 可以将多个装饰器串联起来对函数进行修饰。
  • 多层装饰器的执行顺序是从上到下,即最上层装饰器最先执行,最底层装饰器最后执行。

示例:

def decorator1(func):
    def wrapper(*args, **kwargs):
        # 执行一些额外的操作
        result = func(*args, **kwargs)
        # 执行一些额外的操作
        return result
    return wrapper

def decorator2(func):
    def wrapper(*args, **kwargs):
        # 执行一些额外的操作
        result = func(*args, **kwargs)
        # 执行一些额外的操作
        return result
    return wrapper

@decorator1
@decorator2
def my_function():
    # 函数逻辑
    pass
  • 装饰器是Python中强大且常用的特性,它可以有效地提高代码的可重用性、可读性和可维护性。
  • 通过使用装饰器,可以将与函数相关的逻辑封装在独立的装饰器函数或类中,并在需要时轻松应用到任意函数上。

【十二】语法糖

  • 语法糖(Syntactic sugar)是编程语言中一种用于增强代码可读性或简化书写的语法特性,它并不提供新的功能,只是对现有的语法进行了更友好、更简洁的表达。

【1】列表推导式(List Comprehension):

  • 列表推导式是一种创建新列表的简洁方式。
  • 它可以通过对现有列表进行迭代,并在迭代过程中根据特定条件对元素进行筛选、转换等操作来生成新的列表。

示例:

numbers = [1, 2, 3, 4, 5]
squares = [x ** 2 for x in numbers]  # 使用列表推导式生成平方数列表

【2】字典推导式(Dictionary Comprehension):

  • 字典推导式与列表推导式类似,用于以简洁的方式生成新的字典。
  • 它可以通过迭代键值对,并在迭代过程中根据特定条件对键或值进行转换、筛选等操作来生成新的字典。

示例:

numbers = [1, 2, 3, 4, 5]
squares_dict = {x: x ** 2 for x in numbers}  # 使用字典推导式生成键为数字,值为平方数的字典

【3】装饰器语法糖(Decorator Syntax Sugar):

  • 装饰器是一种用于修改函数或类行为的特殊语法。
  • Python中使用@符号来应用装饰器,这种语法糖使得装饰器的应用在代码中更加直观和简洁。

示例:

def decorator(func):
    def wrapper(*args, **kwargs):
        # 执行一些额外的操作
        result = func(*args, **kwargs)
        # 执行一些额外的操作
        return result
    return wrapper

@decorator  # 使用装饰器语法糖修饰my_function函数
def my_function():
    # 函数逻辑
    pass

【十三】面向切面编程(AOP)/面向对象编程(OOP)

面向切面编程(Aspect-Oriented Programming,AOP)和面向对象编程(Object-Oriented Programming,OOP)是两种不同的编程范式。

【1】面向切面编程(AOP)

  • 面向切面编程(AOP)是一种用于解决系统中横切关注点的编程范式。
  • 横切关注点通常涉及多个不同模块或组件中的重复性代码,例如日志记录、安全性检查、事务管理等。AOP通过将这些横切关注点从主要业务逻辑中分离出来,并以切面(Aspect)的形式进行统一管理和维护。
  • 在AOP中,我们定义切面来描述横切关注点的行为,然后通过称为切点(Pointcut)的方式指定在哪些地方应用这些切面。
  • AOP的核心原则是解耦和增强,通过将关注点的实现从主要业务代码中分离出来,提高了系统的可维护性和可重用性。

【2】面向对象编程(OOP)

  • 面向对象编程(OOP)是一种计算机编程的方法论,它将程序中的各个组件(对象)抽象成类,通过封装、继承和多态等概念来设计和构建软件系统。
  • 面向对象编程的核心思想是将问题拆解成一系列的对象,并定义它们的属性和行为,通过交互和消息传递来实现系统功能。
  • 面向对象编程提供了封装性、继承性和多态性等特性,使得代码更易于理解、扩展和维护。

【3】小结

  • 相比于面向对象编程,面向切面编程强调横切关注点的模块化和复用,可以减少代码的冗余性和提高代码的可读性。
  • 它在很多领域都有广泛应用,例如日志记录、事务管理、异常处理等。通常情况下,面向对象编程与面向切面编程可以结合使用,在系统中共同发挥各自的优势,提高软件开发的效率和质量。
posted @ 2023-07-25 15:30  Chimengmeng  阅读(14)  评论(0编辑  收藏  举报