python基础

1. 计算机语言的发展与分类

1.1 程序语言的介绍

编程语言(programming language)可以简单的理解为一种计算机和人都能识别的语言。一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。

  • 计算机编程语言:Java、C、C++、PHP、Python、VB、VF、C#(C Sharp)、HTML、SQL、Ruby、Go、易语言、汇编、VHDL等

  • 编程语言热门指数:https://www.tiobe.com/tiobe-index/

    2022年12月排行

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.2 计算机编程语言的发展

  1. 机器语言:计算机只能识别二进制(计算机内部的元件通过高低电压来表示信息,高电压是1,低电压是0),早期编程用二进制实现,比如:10100010;

    面试问题:什么是二进制?

    二进制逢二进一

  2. 汇编语言:通过指令集来表示具体的操作,不同硬件的指令集不同,程序可移植性差,比如mov ax bx;

  3. 高级语言:跟人的自然语言接近,易于理解,比如C/C++/Java/Python等。

1.3 语言的分类

1.3.1 按照程序的执行方式

  • 编译型

    • 编译型语言以C/C++为代表;
    • 编译型语言的程序在运行前需要先编译成机器语言,机器语言能够被计算机识别,因此不需要解释就直接运行;C语言的编译器有GCC,C++的编译器有G++
  • 解释型

    • 解释型语言以Python/Ruby/Perl为代表

    • 解释型语言的程序不需要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次;

      Java语言较为特殊,先把.java文件编译成.class字节码文件(十六进制),然后再由JVM解释运行,所以Java既是编译型也是解释型;

  • 编译型与解释型的优劣势对比:

    1. 编译型语言的优势在于运行效率更高,对系统的资源要求更低,一般来说编译型语言用于实现后台的复杂逻辑,比如MySQL数据库、Nginx服务器、CPython等都是用C语言编写的;
    2. 解释型语言的优势在于跨平台性更好(相对而言编译型的可移植性差,C语言程序进行移植后,要重新编译),经常用于脚本的开发。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.3.2 按照程序的设计思想来分

  • 面向过程(Procedure Oriented Programming,简称POP)

    • 面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。

      在这里插入图片描述
    • 举个例子,下五子棋,面向过程的设计思路是首先分析解决这个问题的步骤:
      (1)开始游戏(2)黑子先走(3)绘制画面(4)判断输赢(5)轮到白子(6)绘制画面(7)判断输赢(8)返回步骤2,重复步骤2到7 (9)输出最后结果。
    • 代表语言:C语言
    • 应用场景:
      • 适合编写系统软件:编译器,JVM,驱动,操作系统内核
      • 嵌入式设备的编程
  • 面向对象(Object Oriented Programming,简称OOP)

    • 面向对象是一种以对象为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。(简单说面向对象是完成一件事情,只需要找到某个或某些对象,一一组装完成即可)

      在这里插入图片描述
    • 在下五子棋的例子中,用面向对象的方法来解决的话,首先将整个五子棋游戏分为三个对象:
      (1)黑白双方,这两方的行为是一样的
      (2)棋盘系统,负责绘制画面
      (3)规则系统,负责判定犯规、输赢等
      然后赋予每个对象一些属性和行为:第一类对象(黑白双方)负责接受用户输入,并告知第二类对象(棋盘系统)棋子布局的变化,棋盘系统接收到了棋子的变化,并负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

    • 代表语言:Java ,C++,C#,PHP,JavaScript,Python

      • Java
        • 面向对象的语言
        • 跨平台
      • web开发
        • Android应用开发
        • 服务端应用接口开发
        • 微信公众号开发
      • C++
        • 主要用于底层代码的开发(比如一些大型的游戏,比如lol

        • 一个不完全面向对象的编程语言,也可以面向过程

        • 之前用作MFC界面的开发

        • 现在更多用于数据计算

        • 数据仓库的开发

      • C#
        • 读音:Csharp
        • 微软公司的面向对象的语法
        • 运行在.NET framework
        • 主要开发Windows桌面应用,Windows store 应用
        • 配合asp.net,开发基于Windows Server服务器的web应用
      • PHP
        • 脚本语言,慢慢走向标准化面向对象语言
        • 主要开发动态网页
        • web开发
      • JavaScript
        • Java没有关系
        • 脚本语言
        • 主要用作网页的交互以及动效
      • Python
        • 脚本语言
        • 语法很简单,优美
        • 可以做web开发
        • 数据计算开发
        • 非常适合做自动化测试
  • 面向过程语言和面向对象语言优缺点对比
    • 面向过程:
      • 优点:性能比面向对象高,因为面向对象的类调用时需要实例化;比如单片机、嵌入式开发、linux/unix等一般采用面向过程开发,性能是最重要的因素。
      • 缺点:没有面向对象易维护、易复用、易扩展。
    • 面向对象:
      • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护,适合开发复杂、庞大的大型系统。
      • 缺点:性能比面向过程低。

2. 认识 Python

人生苦短,我用 Python —— Life is short, I use Python

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.1 Python 的起源

Python 的创始人为吉多·范罗苏姆(Guido van Rossum),江湖人称“龟叔”

  1. 1989 年的圣诞节期间,吉多·范罗苏姆为了在荷兰首都阿姆斯特丹打发时间,决心开发一个新的解释程序,作为 ABC 语言的一种继承;
  2. ABC 是由吉多参加设计的一种教学语言,就吉多本人看来,ABC 这种语言非常优美和强大,是专门为非专业程序员设计的。但是 ABC 语言并没有成功,究其原因,吉多认为是非开放造成的。吉多决心在 Python 中避免这一错误,并获取了非常好的效果;
  3. 之所以选中 Python(蟒蛇) 作为程序的名字,是因为他是 BBC 电视剧——蒙提·派森的飞行马戏团(Monty Python’s Flying Circus)的爱好者;
  4. 1991 年,第一个 Python 解释器 诞生,它是用 C 语言实现的,并能够调用 C 语言的库文件。

2.2 Python的组成

  • Python的语法

  • Python的标准内置库

  • Python的第三方库

  • Python解释器

2.2.1 解释器

计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言,计算机才能执行程序。将其他语言翻译成机器语言的工具,被称为编译器

编译器翻译的方式有两种:一个是编译,另外一个是解释。两种方式之间的区别在于翻译时间点的不同。当编译器以解释方式运行的时候,也称之为解释器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如 C、C++
  • 解释型语言:解释型语言编写的程序不进行预先编译,以文本方式存储程序代码,会将代码一句一句直接运行。在发布程序时,看起来省了道编译工序,但是在运行程序的时候,必须先解释再运行

编译型语言和解释型语言对比:

  • 速度 —— 编译型语言比解释型语言执行速度快
  • 跨平台性 —— 解释型语言比编译型语言跨平台性好

2.1.2 Python 的设计目标

1999 年,吉多·范罗苏姆向 DARPA (美国国防高级研究计划局)提交了一条名为 “Computer Programming for Everybody” 的资金申请,并在后来说明了他对 Python 的目标:

  • 一门简单直观的语言并与主要竞争者一样强大
  • 开源,以便任何人都可以为它做贡献
  • 代码像纯英语那样容易理解
  • 适用于短期开发的日常任务

这些想法中的基本都已经成为现实,Python 已经成为一门流行的编程语言

2.1.3 Python 的设计哲学

  • 优雅

  • 明确

  • 简单

    • Python 开发者的哲学是:用一种方法,最好是只有一种方法来做一件事

    • 如果面临多种选择,Python 开发者一般会拒绝花俏的语法,而选择明确没有或者很少有歧义的语法

2.2 为什么选择 Python

  • 代码量少

    同一样问题,用不同的语言解决,代码量差距还是很多的,一般情况下 PythonJava1/5,所以说 人生苦短,我用 Python

  • web应用开发:flaskdjango

  • 自动化测试:seleniumrequests

  • 网络爬虫

  • 自动化运维

  • 人工智能领域

  • 科学计算

  • 游戏开发

  • ……

测试人员选择Python的理由

  • 测试人员的编程能力相对较弱,而Python作为一种脚本语言,不仅功能强大,而且语法优美,支持多种自动化工具,而且学习上手比较容易;
  • 高级语言,不用考虑底层逻辑,不用研究语言的本身,专注于解决问题;
  • Python的社区发展比较好,有着非常多的文档和支持库;
  • 对于一定编程基础的人员,使用Python作为自动化测试的语言可以非常流畅,几乎没有学习成本。

当前时代,作为测试,在面试中掌握Python和Java之一就足够了。

2.3 Python 特点

  • Python 是完全面向对象的语言

    • 函数模块数字字符串都是对象,在 Python 中一切皆对象

      面向对象的思维方式
      • 面向对象 是一种 思维方式,也是一门 程序设计技术
      • 要解决一个问题前,首先考虑 由谁 来做,怎么做事情是 的职责,最后把事情做好就行!
      • 对象 就是
      • 要解决复杂的问题,就可以找多个不同的对象各司其职,共同实现,最终完成需求
    • 完全支持继承、多重继承等

  • Python 拥有一个强大的标准库,Python 语言的核心只包含 数字字符串列表字典文件 等常见类型和函数,而由 Python 标准库提供了 系统管理网络通信文本处理数据库接口图形系统、等额外的功能

  • Python 社区提供了大量的第三方模块,使用方式与标准库类似。它们的功能覆盖 科学计算人工智能机器学习Web 开发数据库接口图形系统 多个领域

学Python主要学:基础语法 + 标准库或者第三方库(会用就可以了)

2.4 Python 的优缺点

2.4.1 优点

  • 简单、易学
  • 免费、开源
  • 面向对象
  • 丰富的库
  • 可扩展性
    • 如果需要一段关键代码运行得更快或者希望某些算法不公开,可以把这部分程序用 CC++ 编写,然后在 Python 程序中使用它们
  • ……

2.4.2 缺点

  • 速度慢:Python 程序比 Java、C、C++ 等程序的运行效率都要慢。
  • 源代码加密困难:不像编译型语言的源程序会被编译成目标程序,Python 直接运行源程序,因此对源代码加密比较困难。其实,这两个缺点并不是什么大问题,首先,由于目前计算机的硬件速度越来越快,软件工程往往更关注开发过程的效率和可靠性,而不是软件的运行效率;至于第二个问题就更不是问题了,现在软件行业的大势本就是开源,就像 Java 程序同样很容易反编译,但丝毫不会影响它的流行。

3. 安装Python

  • 我们平时编写和调试代码是在Windows电脑上写,所以需要在Windows上安装Python环境。

  • 认识python官网:https://www.python.org/

    • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      • 安装的路径,建议用默认路径(

      默认是C:\Users\Administrator\AppData\Local\Programs\Python\PythonXX

      注意:不要安装在中文路径中(如果路径中有中文的就自定义安装)

      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      • 如果没有勾选添加环境变量,则需要手动把python的安装目录及安装目录下的Scripts目录添加到PATH变量中。也就是:C:\Users\Administrator\AppData\Local\Programs\Python\Python37\C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Scripts\

      检查是否安装完成

      在dos窗口输入python -V回车,如果返回有python的相关信息,则安装成功

      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  • 上课安装的版本:3.11.0

4. 第一个Python程序

4.1 第一个 Hello Python 程序

4.1.1 Python 源程序的基本概念

  1. Python 源程序就是一个特殊格式的文本文件,可以使用任意文本编辑软件Python 的开发
  2. Python 程序的 文件扩展名 通常都是 .py

4.1.2 演练步骤

  • 新建 study_Python 目录
  • study_Python 目录下新建 hello_python.py 文件
  • 使用 记事本编辑 hello_python.py 并且输入以下内容:
print("hello python")
print("hello world")
  • 在终端中输入以下命令执行 hello_python.py
python hello_python.py

printpython 中我们学习的第一个 函数

print 函数的作用,可以把 “” 内部的内容,输出到屏幕上

4.2 Python 2.x3​​.x 版本简介

目前市场上有两个 Python 的版本并存着,分别是 Python 2.xPython 3.x

新的 Python 程序建议使用 Python 3.0 版本的语法

  • Python 2.x过去的版本
    • 解释器名称是 python2
  • Python 3.x现在和未来 主流的版本
    • 解释器名称是 python
    • 相对于 Python 的早期版本,这是一个 较大的升级
    • 为了不带入过多的累赘,Python 3.0 在设计的时候 没有考虑向下兼容
      • 许多早期 Python 版本设计的程序都无法在 Python 3.0 上正常执行
    • Python 3.0 发布于 2008 年
    • 到目前为止,Python 3.0 的稳定版本已经有很多年了
      • Python 3.3 发布于 2012
      • Python 3.4 发布于 2014
      • Python 3.5 发布于 2015
      • Python 3.6 发布于 2016
  • 为了照顾现有的程序,官方提供了一个过渡版本 —— Python 2.6
    • 基本使用了 Python 2.x 的语法和库
    • 同时考虑了向 Python 3.0 的迁移,允许使用部分 Python 3.0 的语法与函数
    • 2010 年中推出的 Python 2.7 被确定为 最后一个Python 2.x 版本

提示:如果开发时,无法立即使用 Python 3.0(还有极少的第三方库不支持 3.0 的语法),建议

  • 先使用 Python 3.0 版本进行开发
  • 然后使用 Python 2.6Python 2.7 来执行,并且做一些兼容性的处理

4.3 执行 Python 程序的三种方式

4.3.1 解释器 python2 / python3

Python 的解释器
# 使用 python 2.x 解释器
$ python2 xxx.py

# 使用 python 3.x 解释器
$ python3 xxx.py
其他解释器(知道)

Python 的解释器 如今有多个语言的实现,包括:

  • CPython —— 官方版本的 C 语言实现
  • Jython —— 可以运行在 Java 平台
  • IronPython —— 可以运行在 .NET 和 Mono 平台
  • PyPy —— Python 实现的,支持 JIT 即时编译

4.3.2 交互式运行 Python 程序

  • 直接在终端中运行解释器,而不输入要执行的文件名
  • 在 Python 的 Shell 中直接输入 Python 的代码,会立即看到程序执行结果

1) 交互式运行 Python 的优缺点

优点:

  • 适合于学习/验证 Python 语法或者局部代码

缺点:

  • 代码不能保存
  • 不适合运行太大的程序

2) 退出 官方的解释器

1> 直接输入 exit()

>>> exit()

2> 使用热键退出

在 python 解释器中,按热键 ctrl + d或者ctrl + z 可以退出解释器

4.4 Python 的 IDE —— PyCharm

4.4.1 集成开发环境(IDE

集成开发环境(IDE,Integrated Development Environment)—— 集成了开发软件需要的所有工具,一般包括以下工具:

  • 图形用户界面
  • 代码编辑器(支持 代码补全自动缩进
  • 编译器/解释器
  • 调试器(断点单步执行
  • ……

Java:Idea, Eclipes

前端:HBuider,Vscode

Python:PyCharm

SQL:Navicat

4.4.2 PyCharm 介绍

  • PyCharm 是 Python 的一款非常优秀的集成开发环境

  • PyCharm 除了具有一般 IDE 所必备功能外,还可以在 WindowsLinuxmacOS 下使用

  • PyCharm 适合开发大型项目

    • 一个项目通常会包含 很多源文件
    • 每个 源文件 的代码行数是有限的,通常在几百行之内
    • 每个 源文件 各司其职,共同完成复杂的业务功能

4.4.3 PyCharm 安装

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:勾选以上选项。

PyCharm的配置

创建项目:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

选择设置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

背景颜色和工具的字体大小

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码内容的字体设置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建Python文件: 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

文件里的抬头设置:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# -*-coding:utf-8 -*- #
# ---------------------------------------------------------------------------
# ProjectName:   ${PROJECT_NAME}
# FileName:      ${FILE_NAME}
# Author:       xxxxxxx
# Datetime:     ${DATE} ${TIME}
# Description:
# 命名规范:文件名全小写+下划线,类名大驼峰,方法和变量小写+下划线连接,
# 常量大写,变量和常量用名词,方法用动词
# ---------------------------------------------------------------------------

4.4.4 PyCharm 快速体验

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 文件导航区域 能够 浏览定位打开 项目文件
  • 文件编辑区域 能够 编辑 当前打开的文件
  • 控制台区域 能够:
    • 输出程序执行内容
    • 跟踪调试代码的执行
  • 右上角的 工具栏 能够 执行(SHIFT + F10) / 调试(SHIFT + F9) 代码

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 通过控制台上方的单步执行按钮(F8),可以单步执行代码

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

pycharm:无法加载文件activate.ps1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

virtualenv无法加载文件activate.ps1,因为在此系统上禁止运行脚本,Windows10系统

原因是 Windows 权限执行策略改变不允许。以管理员身份打开 PowerShell,执行如下操作:

cmd里面执行命令:

  1. cd C:\Windows\System
cd C:\Windows\System>
  1. 执行 Get-ExecutionPolicy
Get-ExecutionPolicy

# 输出为:
Restricted

表明当前是严格受限模式,需要设置打开,在PowerShell里面执行命令:

  1. 执行 Set-ExecutionPolicy Bypass
Set-ExecutionPolicy Bypass

重启Pycharm即可解决问题。

4.4.5 PyCharm翻译插件的安装与配置

  1. 第一步:在File–>Settings–>Plugins下安装Translation插件
  2. 第二步:在File–>Settings–>Tools–>Translation下选择翻译引擎为微软翻译
  3. 第三步:在File–>Settings–>Keymap–>Plugins–>Translation–>Translate下添加一个自己顺手的翻译快捷键

4.4.6 PyCharm调试代码(后面用到再讲)

调试的过程分为三步:

  1. 第一步:在你想要调试的地方,打上断点
  2. 第二步:使用调试模式来运行这个 python 程序
  3. 第三步:使用各种手段开始代码调试
a. 图文教程

1、首先第一步和第二步,我用下面这张图表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2、点击上图中的小蜘蛛,开启调试模式后,在 PyCharm 下方会弹出一个选项卡。这个选项卡的按键非常多,包括:

  • 变量查看窗口
  • 调试控制窗口
  • 线程控制窗口
  • 程序控制窗口

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在变量查看窗口,你可以查看当前程序进行到该断点处,所有的普通变量和特殊变量,你每往下执行一行代码,这些变量都有可能跟着改变。

如果你的程序是多线程的,你可以通过线程控制窗口的下拉框来切换线程。

以上两个窗口,都相对比较简单,我一笔带过,下面主要重点讲下调试控制按钮和程序控制按钮。在调试控制窗口,共有 8 个按钮。

3、设置好断点,debug运行,然后 F8 单步调试,遇到想进入的函数 F7 进去,想出来在 shift + F8,跳过不想看的地方,直接设置下一个断点,然后 F9 过去。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4、在程序控制窗口,共有 6 个按钮,他们的作用分别又是什么呢?同时看下面这张图就行了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

b. 调试相关的快捷键
  • Shift + F9:调试当前文件
  • F8:单步执行,不进入函数
  • F7:单步执行,进入函数
  • Alt + Shift +F7:单步执行,只进入自己写的函数
  • Shift + F8:跳出函数体
  • F9:运行到下一断点
  • Alt + F9:运行到光标处
  • Alt + F8 计算表达式(可以更改变量值使其生效)

4.5 Python本地环境和虚拟环境

  • Python的本地环境,就是Python在当前系统的环境。

  • Python的虚拟环境

    • 使不同Python应用的之间环境相互独立;

    • 当其中一个应用环境升级不影响其他应用的环境,也不会影响本地环境,因为虚拟环境是将本地环境进行复制(Python默认安装之后的环境,不带安装的第三方库),当我在虚拟环境进行 pip install 时,只会安装到选择的虚拟环境中;

    • 它可以防止系统中出现包管理混乱和版本的冲突。

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.6 Python的标识符,命名规则与规范

标识符:python中的标识符是用于识别变量、函数、类、模块以及其他对象的名字叫标识符。

命名规则:所有需要自己命名的地方都必须要遵守以下规则

  1. 可以包含数字、字母、_,但是不能以数字开头;

  2. 标识符不能是Python中的关键字(保留字),也不建议使用python中的函数名作为标识符,但可以把关键字(保留字)作为标识符的一部分;

    Python的关键字:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

举例:如下哪些标识符是合法的?

  1. 小明
  2. @#¥%&……*(
  3. 1003hello
  4. hello world
  5. python
  6. def
  7. class
  8. abc

标识符命名规范:模块名全小写+下划线,类名用大驼峰法,方法和变量小写+下划线连接,常量大写,变量用名词,方法用动词,一般以英文单词命名,不能随意地写abc等名字,在一个项目组里,一定要有统一的规范,这样代码的可阅读性才会更好。

规则与规范:规则是强制的,必须遵守,不遵守会导致程序出错;规范是非强制的,但最好遵守,目的是提高代码的可读性。

4.7 代码入门演示

4.7.1 创建项目,目录,Python包,Python文件

第一步:创建项目

image-20221228115302566

第二步:在项目中新建Python Package

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第三步:在Python Package中新建Python File

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第四步:在Python File中编写代码。

Python Package与Directory的区别:

  1. Python Package是Python包,里面会自动创建一个__init__.py文件,Python Package里面用于存放Python代码文件,Python Package里面的对象可以导入到其它Python文件中执行;
  2. Directory就是一个普通的目录,里面不会自动创建一个__init__.py文件,目录用来存放静态资源,比如图片。

4.7.2 第一行代码:hello world

#输出hello world
print("hello world")

输出函数:print

作用:实现输出一个结果到控制台。

举例:

#输出数字
print(12312)
#输出字符串,字符串要加引号
print("hello world")
#输出汉字字符串
print("中国")
代码的注释
  • 单行注释:用#实现,选中多行按Ctrl+/可以快速添加单行注释;
  • 多行注释:用三单引号(‘’‘…’‘’ )或三双引号(“”“…”“”)实现
Pycharm常用快捷键
  • 快速注释:Ctrl+/
  • 快速换行:Shift+Enter:不管光标在哪个位置,都能够在当前行的下面新起一行
  • 快速复制:Ctrl+d
  • 快速撤销:Ctrl+z

4.7.2 演练扩展 —— 认识错误(Bug)

a. 关于错误

  • 编写的程序不能正常执行,或者执行的结果不是我们期望的
  • 俗称 Bug,是程序员在开发时非常常见的,初学者常见错误的原因包括:
    1. 手误
    2. 对已经学习过的知识理解还存在不足
    3. 对语言还有需要学习和提升的内容
  • 在学习语言时,不仅要学会语言的语法,而且还要学会如何认识错误和解决错误的方法

每一个程序员都是在不断地修改错误中成长的,在学习代码的过程中,要不断积累典型报错的解决方法,来提升自己的代码能力。

要求:每个同学要整理自己的常见问题(FAQ),老师不定时检查

b. 第一个演练中的常见错误

(1) 手误,例如使用 pirnt("Hello world")

NameError: name 'pirnt' is not defined

名称错误:'pirnt' 名字没有定义

(2)将多条 print 写在一行

SyntaxError: invalid syntax

语法错误:语法无效

每行代码负责完成一个动作

(3)缩进错误

IndentationError: unexpected indent

缩进错误:不期望出现的缩进
  • Python 是一个格式非常严格的程序设计语言
  • 目前而言,大家记住每行代码前面都不要有空格,后面讲到逻辑控制的时候才需要加空格

5. Python的数据类型

Python的六个标准的数据类型

  • Number(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)

5.1 Python变量的声明与使用

  • 变量:作用是存储程序运行中的值,变量的值在程序运行中是可以变化的,变量必须先声明再使用。

  • Python变量的声明:变量名=值

    # 比如:
    a=1
    

    注意:

    • 在python中变量是没有数据类型的,变量的数据类型取决于赋值的类型,这与其他语言不一样。

    • 比如在Java中声明变量的时候需要指定变量的数据类型,赋的值的类型必须跟变量的类型保持一致,否则会报错,比如声明一个变量a赋值为1要写成int a=1,意思是先声明一个int类型的变量a,再对a赋值为1,变量的类型必须与赋值的类型保持一致,在Java中int a=3.5的写法是错误的。

    • Java是先定义类型再根据类型赋值,而python是根据值来确定类型

    # 声明一个变量num1,赋值为整数8
    num1 = 8
    # 查看num1的类型
    print(type(num1))
    # 使用变量num1
    print(num1)
    
    # 声明一个变量str1,赋值为字符串hello world
    str1 = "hello world"
    # 查看str1的类型
    print(type(str1))
    # 使用变量str1
    print(str1)
    # 多个变量同时定义
    number1, str2 = 1, "你好"
    print(number1, str2)
    
  • 常量:Python没有常量的概念,Python程序一般通过约定俗成的变量名全大写的形式表示这是一个常量。然而这种方式并没有真正实现常量,其对应的值仍然可以被改变。后来,Python提供了新的方法实现常量:即通过自定义类实现常量。这要求符合“命名全部为大写”和“值一旦被绑定便不可再修改”这两个条件。

    # Java中常量举例:声明常量PRICE,赋值为10,声明后这个常量的值就固定为10,不能再对它赋值
    final int PRICE = 10;
    
    # 如果python中要定义常量,如下
    PI = 3.14
    MONTH = 12
    

5.2 数值型

Python中的数值型包括:int(整型)、float(浮点数)、complex(复数),在Python2中还有long(长整型)

5.2.1 int

int(整型):表示一个整数,包括正整数、负整数

num_1=100
print(type(num_1))

type()函数:type函数的作用是查询变量的数据类型

num_1=100
print(type(num_1))
输出:<class 'int'>
解释:int表示num_1是int类型的变量。

字符串转int:int()表示把其它数据类型转成int类型的,比如int(“100”)->100

5.2.2 float

float(浮点数):表示一个小数,但在计算机中float只能表示一个近似值,不能表示精确值,这是因为浮点数的机制决定的(浮点数必须包含一个小数点,否则会被当做int类型处理)

a=3.11
b=1.5
print(a-b)	#输出1.6099999999999999而并不是1.61

如果要表示精确值,可以使用Decimal对象来实现

#通过实例化Decimal对象来表示精确小数
from decimal import Decimal
print(Decimal("3.11")-Decimal("1.5"))

5.2.3 其它进制的表示与相互转换

常用的进制:二进制、八进制、十进制、十六进制等

  • 二进制(Binary)

    • 由0, 1组成,逢二进一

    • 二进制的声明:在数字前加0b表示二进制

      # 声明一个变量a,赋值为二进制1001
      a=0b1001
      # 输出a的值,输出的是十进制9
      print(a)
      
  • 八进制(Octal)

    • 由0, 1, … ,7组成,逢八进一

    • 八进制的声明:在数字前加0o表示八进制

      #声明一个变量a,赋值为八进制0011
      a=0o0011
      #输出a的值,输出的是十进制9
      print(a)
      
  • 十进制(Decimal)

    • 由0, 1, … ,9组成,逢十进一

    • 十进制的声明:Python中默认的数值就是十进制的,因为人习惯的是十进制

  • 十六进制(Hexadecimal)

    • 由0, 1, … ,9, A, B, C, D, E, F,逢十六进一

    • 十六进制的声明:在数字前加0x表示二进制

      #声明一个变量a,赋值为十六进制000F
      a=0X000F
      #输出a的值,输出的是十进制15
      print(a)
      

进制的转换(了解)

  • 二进制与十进制之间的相互转换

    #二进制转换为十进制:用从左往右的第一个数乘以2的第一个数所在位置的次方加上第二个数乘以2的第二个数所在位置的次方
    例:
    0b100101
      543210(二进制数的位置编号,从右往左从0开始编号)
         
    十进制数等于:1x2^5+0x2^4+0x2^3+1x2^2+0x2^1+1x2^0 = 37
    
    #十进制转换二进制
    十进制如何转二进制:将该数字不断除以2直到商为零,然后将余数由下至上依次写出,即可得到该数字的二进制表示,
    如:
    11 除以 251
    5  除以 221
    2  除以 210
    1  除以 201
    # 11转换为二进制为:1011
    
  • 八进制与十进制之间的相互转换

    #八进制转换为十进制
    方法:八进制数从低位到高位(即从右往左)计算,第0位的权值是80次方,第1位的权值是81次方,第2位的权值是82次方,依次递增下去,把最后的结果相加的值就是十进制的值了。
    如:
    八进制的45
    转换为10进制:5x8^0+4x8^1 = 37
    
    #十进制转换为八进制和二进制差不多
    方法:采用除8取余法(和二进制方法类似)
    
  • 十六进制与十进制之间的相互转换

    #十六进制转换为10进制,和二进制,八进制方法相同
    #十六进制的数:0x123
    #转换为十进制:1x16^2+2x16^1+3x16^0 = 291
    
    #十进制转换为十六进制
    方法:采用除16取余法(和二进制类似)
    
**进制转换函数**
- bin():把十进制转成二进制
- oct():把十进制转成八进制
- hex():把十进制转成十六进制
- int(x,base=y):表示把y进制的字符串x转换成十进制的整数

- 举例:
  print(bin(10)) # 十进制转换为二进制
  print(oct(10)) # 十进制转换为八进制
  print(hex(17)) # 十进制转换为十六进制
  print(int("1000",base=2))#表示把2进制的字符串“1000”转换成十进制的整数
  print(int("1002",base=2))#思考:输出是什么?

5.3 字符串-str

5.3.1 Python中的引号

Python中的引号:单引号’‘、双引号""、三单引号’‘’ ‘’'或者三双引号"“” “”",一般情况下三种引号可以通用,但必须成对出现;

双引中的单引以及单引中的双引可以正常输出,但如果要输出单引中的单引以及双引中的双引则需要使用转义字符;

三引号还可以用来做多行的注释。

5.3.2 字符串的声明

字符串:字符串是一串有序的字符。

字符串的声明:使用引号来声明(单引、双引、三引都可以)或者通过str()来声明一个字符串。

#声明一个空字符串
str1=""
print(type(str1),len(str1))
print(str1)

#声明一个非空字符串(单引号)
str2='hello world'
print(type(str2))
print(str2)

#声明一个非空字符串(双引号)
str3="hello world"
print(type(str3))
print(str3)

#声明一个非空字符串(三引号)
str4="""hello world"""
print(type(str4))
print(str4)

# 把int类型转成字符串
str5=str(100)
print(type(str5))
print(str5)

5.3.3 字符串的切片

字符串是有序的,字符串中的每个字符是有索引下标的,字符串的索引有两种:

  • 正序索引:从左往右开始下标依次为0,1,2,…

  • 倒序索引:从右往左开始下标依次为-1,-2,…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

字符串切片语法:

  1. str[start : end : ​step=1]:表示从索引为start的位置取到索引为end-1的位置,注意它的规则是左闭右开规则,省略start表示从开头取,省略end表示取到末尾,步长默认为1,字符串切片得到的是一个字符串。

    步长为正:表示从左往右取;步长为负:表示从右往左取。

  2. str[index]:表示取索引为index的元素,如果index超出了范围,会报IndexError异常。

# 切片 str[起始值:终止值:步长] 起始值从0开始
str3 = "123456789"
print(str3[4])    		# 取当前位置的值
print(str3[1:4])  		# 步长不输默认为1
print(str3[:4]) 
print(str3[4:])   		# 终止值不输默认取到最后
print(str3[:]) 
print(str3[::-1])		# 反着把字符串中的字符取出来
print(str3[4:7:1]) 		# 区间左闭右开
# 1.取13579 
# 2.取2468
# 3.取987654321   print(str3[::-1])
# 4.取97531
# 5.取753
# 步长为正:从左往右取;步长为负:从右往左取
print(str3[-1:-5])
print(str3[-1:-5:-1])
print(str3[-5:-1])
print(str3[-5:-1:-1])

# 取区间不在范围内的不会报错,返回空字符串,
# 去找这个范围内的值是否存在。如果是单个值不在范围内,则会报错。
print(str3[20])
print(str3[20:30])

5.3.4 字符串的常用函数

Pycharm中,代码提示的含义:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.3.4.1 len()函数

作用:计算字符串的长度,字符串的长度就是有几个字符

举例:

str1="hello world"
print(len(str1))
5.3.4.2 ord()函数

作用:是返回字符的ascii

举例:

print(ord('a'))
print(chr(97))
print(ord('A'))
5.3.4.3 chr()函数

作用:跟ord()相反,它根据ascii码返回对应的字符

举例:

print(chr(99))

5.3.5 字符串的常用方法(内置方法/内建方法)

5.3.5.1 find()方法

格式:find(self, sub, start=None, end=None)

作用:在字符串中找第一个出现的子串的下标,如果找不到返回-1,可以传入start和end在指定的范围内找

举例:

str2 = 'nihao,feichanghao'
# 查询第一个a元素的下标索引
print(str2.find('a'))    
# 查询下标索引位置从10到17的区间中的第一个a的下标索引
print(str2.find('a',10,17))
5.3.5.2 index()方法

作用:在字符串中找第一个出现的子串的下标,如果找不到抛ValueError异常

举例:

print(str2.index('e'))   
print(str2.index('4'))

find()和index()方法的区别:find()如果在指定字符中没有找到则返回-1;而index则会抛出ValueError异常

5.3.5.3 rfind()方法

格式:rfind(self, sub, start=None, end=None)

作用:在字符串中找最后一个出现的子串的下标,如果找不到返回-1

5.3.5.4 rindex()方法

格式:rindex(self, sub, start=None, end=None)

作用:在字符串中找最后一个出现的子串的下标,如果找不到抛ValueError异常

5.3.5.5 format()方法

作用:实现字符串的格式化输出

举例:

#场景一:format方法中的值按位置替换字符串中的{}
print("我叫{},我来自{},今年{}岁了。".format('jack','成都','10')) 

#场景二:以索引的方式来表示替换参数,{0}位置用传入的第一个变量替换,{1}位置用传入的第二个变量替换,{2}位置用传入的第三发变量替换。可以允许传入的参数比需要的多,但不能比需要的少
print("我叫{0},我来自{1},今年{2}岁了。".format('jack','成都','10')) 
print("我叫{1},我来自{1},今年{2}岁了。".format('jack','成都','10'))
print("我叫{0},我来自{1},今年{2}岁了。".format('jack','成都','10','北京'))
print("100+200={0}".format(100+200))

#场景三:在大括号中除了用下标之外,还可以用变量名,这种情况下传入的参数顺序就没有关系了。
print("我叫{name},我来自{city},今年{age}岁了。".format(name='jack',city='成都',age='10'))
print("我叫{name},我来自{city},今年{age}岁了。".format(city='成都',age='10',name='jack'))
print(str2)

在Python中除了用format方法实现格式化输出之外,还可以使用格式符来实现格式化输出,常用的格式符有如下:

  • %s 字符串

  • %d 十进制整数

  • %f 浮点数

参数必须位置、数量一一对应,格式化输出举例:

name = "小王"
age = 9
#如果不适用格式化输出,会多出一个空格
print("老王的儿子叫",name,"今年",age,"岁了")

#使用格式化输出
print("老王的儿子叫%s今年%d岁了"%(name,age))

# %.2f表示四舍五入保留2位小数输出
print("%.2f"%1.33333)

# 也可以使用round函数实现四色五入,保留n位小数
print(round(1.34356789,4))
5.3.5.6 count()方法

作用:统计子串出现的次数

举例:

str2 = "elgjlajlnvayzdfad"
print(str2.count('e'))   
print(str2.count('ay'))
print(str2.count('z'))
5.3.5.7 join()方法

作用:传入一个可迭代对象,把可迭代对象中的每个元素通过调用它的字符串来拼接,返回拼接后的字符串。

举例:

# 在字符串中拼接“_”,把每个字符分开
str2="todayissundaya"
print("_".join(str2))

#错误的,join()方法传入的参数必须是迭代对象(包括字符串、列表、元组等等)
print("_".join(100))  

#join方法中传入列表,但列表的元素必须是字符串
print("".join(["1","3","5"]))

#join方法中传入元组,但元组的元素必须是字符串
print("".join(("1","3","5")))
5.3.5.8 replace()方法

格式:replace(self, old, new[, count])

作用:替换旧的字符串成新字符串,count参数可选,表示替换几个,如果不传count就表示全部替换

举例:

print(str2.replace('a','A'))  #把str2中的a替换成A

print(str2.replace('a','A',1)) #只替换一次,把a替换成A

print(str2.replace('a','A',2).replace('A','a',1)) #把str2中的第2个a替换成A
5.3.5.9 split()方法

格式:split(self, sep, maxsplit=-1)

作用:将一个字符串分裂成多个字符串返回列表的方法,详细即通过指定分隔符对字符串进行切片,如果参数maxsplit 有指定值,则仅分隔 maxsplit 个子字符串

举例:

print("hello".split("l"))		# 输出['he', '', 'o'],思考为什么会出现一个空字符串?
print("hello".split("l", 1)) 	# 输出['he', 'lo']

注意:当分割的子串出现在边界上,或者连续出现分割的子串时,在返回的列表中会出现空字符串。

5.3.5.10 rstrip()/lstrip()/strip() 方法

作用:

rstrip()方法:去除字符串右边的空格

lstrip() 方法:去除字符串左边的空格

strip() 方法:去除字符串两边的空格

举例:

str1 = " jlkj agjl "
# 去除开头的空格
print(str1.lstrip())
#去除结尾的空格
print(str1.rstrip())
#去除两边的空格
print(str1.strip())
5.3.5.11 capitalize()方法

作用:将字符串的首字母大写,其余字母全部小写

举例:

print(str1.capitalize())
print("today is wednesday".capitalize())
5.3.5.12 upper()

作用:把字母全部转换为大写

举例:

print(str1.upper())
5.3.5.13 lower()

作用:把字母全部转换为小写

举例:

print(str1.lower())
5.3.5.14 title()

作用:字母的首字母都大写,标题化

举例:

str3 = "hello new world"
print(str3.title())
5.3.5.15 endswith()方法

作用:判断字符串以xx结尾

举例:

print(str2.endswith("x"))  
5.3.5.16 startswith()方法

作用:判断字符串以xx开头

举例:

print(str2.startswith("t"))  
5.3.5.17 以is开头的方法

所有is开头的方法返回结果都是布尔值(True或者False)

  • isalnum():字符串中所有字符都是字母或数字且非空则返回True
  • isalpha():字符串中所有字符都是字母且非空则返回True
  • isdigit():字符串中所有字符都是数字且非空则返回True
  • isupper(): 字母都为大写且非空返回True
  • islower():字母都为小写且非空返回True
  • istitle():单词的首字母都为大写且非空返回True
print("we123".isalnum())
print("".isalnum())
print("%^*".isalnum())
print("we123".isalpha())

举例:从键盘输入密码,判断密码是否是由数字或字母组成,如果是返回输入正确,否则返回输入错误

str1 = input("请输入密码:")
if str1.isalnum():
   print("输入正确")
else:
   print("输入错误")

print函数是输出函数。程序执行的输出函数,不需要使用变量接收,因为print函数只是输出。

input函数是输入函数,如果有输入,就需要使用变量接收输入的内容。不管输入的内容是否为空。

5.3.5 转义字符

在编程语言中,定义了一些特殊字符来表示特定的含义,这被称为转义字符,之所以叫转义是因为字符原本的含义发生了改变。比如:

\':表示单引号,在单引中输出单引时需要用这个转义符

\" :表示双引号,在双引中输出双引时需要用这个转义符

\""":表示三引号,在三引中输出三引时需要用这个转义符

\\:表示反斜线,在输出反斜线的时候需要用到

\t :表示水平制表符,作用类似于tab健,在输出需要有间隔时使用

\n:换行符,在输出时,需要换行时使用

':单引号,双引号里面可以直接使用单引号

print("i'm a boy")

":双引号,单引号里面可以直接使用双引号

\ :反斜杠\,如果刚好遇到"\test"这种,系统会把\t识别成转义字符,这时要在前面再加一个\,如果路径很长,可以在最前面加个r

print("\\test")
print(r"c:\test\rh.txt")

\x:表示后面的字符是十六进制数

Python 字符串前面加r, b, f的含义

r:字符串前加 r

  • 去掉反斜杠的转移机制。

例:r"\n\n” # 表示一个普通生字符串 \n\n,而不表示换行了。

b:b" "前缀表示:字符串是bytes类型。

  • 网络编程中,服务器和浏览器只认bytes 类型数据。
在 Python3 中,bytesstr 的互相转换方式是

str.encode('utf-8')

bytes.decode('utf-8')

f:字符串前加 f

  • 以 f 开头表示在字符串内支持大括号内的python 表达式
print(f'{name.upper()}')

输出:
TOM

python 用反斜杠 转义字符。如下表:

转义字符描述实例
\\反斜杠符号>>> print("\\") \
\'单引号>>> print('\'') '
\"双引号>>> print("\"") "
\a响铃>>> print("\a")执行后电脑有响声。注意只能在dos下执行才会有响铃的效果
\b退格(Backspace)>>> print("Hello \b World!") Hello World!
\n换行>>> print("\n") >>>
\v纵向制表符>>> print("Hello \v World!") Hello World! >>>
\t横向制表符>>> print("Hello \t World!") Hello World! >>>
\r回车,将 \r 后面的内容移到字符串开头,并逐一替换开头部分的字符,直至将 \r 后面的内容完全替换完成。>>> print("Hello\rWorld!") World!

5.3.6 字符串的驻留机制(了解)

在编程语言中,=表示赋值,==表示等于,is表示判断两个对象是否是同一个对象(内存地址),==与is的区别是:==比较的是值,is比较的是内存地址。

字符串的驻留机制:在存放两个相同的字符串时,是否申请新的内存空间来分别存放取决于字符串的复杂程度(有多种类型的字符,如果只有一种即便很长那也不算复杂)

str1="helloworldnihao"
str2="helloworldnihao"
str3="monday%^ sunday"
str4="monday%^ sunday"

print(str1==str2)
print(str1 is str2)
print(id(str1))
print(id(str2))
print(str3==str4)
print(str3 is str4)
print(id(str3))
print(id(str4))

在PyCharm上运行和在dos下运行是有区别的

5.4 列表-list

5.4.1 列表的声明

  • 列表:列表是一组有序的元素,它里面的元素可以是数字、字符串、甚至可以是列表,元素跟元素之间用逗号分隔,列表是可变的。

  • 列表的声明:通过[]list()或列表推导式来声明(一维列表、多维列表)

  • 声明一个列表:

    #声明一个空列表
    list1=[]
    print(type(list1),len(list1))
    
    #声明一个非空列表
    list2=['a',1,5,'hello',[2,4,6,'world']]
    print(type(list2),len(list2))
    
    #通过创建list对象来声明一个列表
    list1 = list("hello")
    print(list1)
    num = 231314
    print(list(num)) # 错误的,不能转换
    
    #通过列表推导式来声明一个列表
    print([i for i in range(1,101) if i%2==0])
    
  • 一维列表与二维列表

    • 一维列表:[1, 2, 3]
    • 二维列表:[[1, 2, 3], [4, 5, 6], [7, 8, 9]] ---->二维列表的结构在自动化中会使用
  • 修改列表中的元素

    list2=['a',1,5,'hello',[2,4,6,'world']]
    print(list2) # 修改前
    # 修改列表中的5为99
    list2[2] = 99
    print(list2) # 修改后
    

5.4.2 列表的切片

  • 列表的切片:规则与字符串的切片类似,但如果你切一个范围它返回的是一个列表。

  • 举例:

    list1 = [1,3,4,5,6,7,8,9,0,123,4566,888,[345,567,999],"hello,中国"]
    print(list1[1]) # 取下标索引为1的元素
    print(list1[:6]) # 取下标索引默认从0开始到6结束步长默认为1的元素
    print(list1[::]) # 全部默认,取整个列表
    print(list1[::-1]) # 全部默认,取整个列表的倒序
    print(list1[12][1]) # 取列表中的列表里的元素
    print(list1[13][1]) # 取列表中的字符串里的元素
    

5.4.3 列表的常用函数

5.4.3.1 len()函数
  • 作用:计算列表的长度,即计算列表中的元素个数

  • 举例:

    list1 = [1,2,3,4,"niaoogho","你好""a","z","A"]
    print(len(list1))
    
5.4.3.2 max()函数
  • 作用:取列表中的最大值

  • 举例:

    # 字母排序是根据ASCII码排序
    # 汉字排序是根据编码格式来排序
    list1 = [1,2,3,4,"niaoogho","你好""a","z","A"]
    print("最大值是:",max(list1))
    
5.4.3.3 min()函数
  • 作用:取列表中的最小值

  • 举例:

    # 字母排序是根据ASCII码排序
    # 汉字排序是根据编码格式来排序
    list1 = [1,2,3,4,"niaoogho","你好""a","z","A"]
    print("最小值是:",min(list1))
    
5.4.3.4 sorted()函数
  • 格式:sorted(list, reverse=False)

  • 作用:对列表的元素排序,返回排序后的新列表,默认是升序

  • 举例:

    list1=[1,5,2,-1]
    # 升序排列
    print(sorted(list1))
    # 降序排列
    print(sorted(list1, reverse=True))
    

5.4.4 列表的常用方法

5.4.4.1 append()方法
  • 作用:在列表的末尾增加一个元素

  • 举例:

    list1 = [1,2,4,5,65,"真的"]
    print("添加前:",list1)
    list1.append(300)
    print(list1)
    list1.append([400,500]) # 添加一个列表,作为一个元素添加在末尾
    print(list1)
    
5.4.4.2 insert()方法
  • 格式:insert(se7lf, index, value)

  • 作用:在列表中下标为index的位置插入元素value(操作之后元素value的下标为index)添加单个元素

  • 举例:

    list1 = [1,2,4,5,65,"真的"]
    # 在元素下标为3的位置插入999
    print("添加前:",list1)
    list1.insert(3,999)
    print("添加后:",list1)
    # 在超出索引位置添加?-会添加在列表的最后
    list1.insert(8,88)
    print("超出索引添加:",list1)
    
5.4.4.3 extend()方法
  • 格式:extend(可迭代对象),可迭代对象比如字符串、列表

  • 作用:列表的拼接,添加多个元素

  • 举例:

    list1 = ["你好"]
    list2=[111,222,333]
    # list1.extend([10001,10002,10003])
    list1.extend(list2) 
    print(list1)
    # +号拼接,结果和extend一样
    print("两个列表相加:",list1+list2)
    # ,号拼接只是把两个放一起,没有变成一个
    print("两个列表逗号拼接:",list1,list2)
    
5.4.4.4 index()方法
  • 作用:从列表中找出某个值,第一个匹配项的索引位置

  • 举例:

    list1 = [1,2,3,5,7,"zheng","很好"]
    print(list1.index(2)) # 返回2的下标索引位置
    # 如果查询的不在列表内?-报错:ValueError: 8 is not in list
    print(list1.index(8))
    
5.4.4.5 count()方法
  • 作用:计算元素出现的次数

  • 举例:

    list1 = [1,2,3,1,7,"zheng","很好"]
    print(list1.count(1))
    
5.4.4.6 remove()方法
  • 格式:remove(value)

  • 作用:删除列表中的某个元素:括号中传入要删除的元素值,不返回删除元素,如果被删除的元素有多个,则只删第一个

  • 举例:

    list1 =["a","b","d","c"]
    print("删除前",list1)
    print(list1.remove('a')) # 打印删除时是否返回
    print("删除后",list1)
    
5.4.4.7 pop()方法
  • 格式:pop(index)

  • 作用:传入要删除元素的下标,pop方法会返回删除的元素,如果不传默认删除最后一个

  • 举例:

    list1 =["a","b","d","c"]
    print("删除的元素是:",list1.pop(3))
    print(list1)
    
5.4.4.8 del list1[index]
  • 作用:删除列表或列表中的数据

  • 举例:

    list1=[[2, 4, 6, 'world'], 'hello', 5, 1]
    # 根据下标索引删除,删单个元素
    del list1[1]
    print(list1)
    # 根据下标索引删除,删区间
    del list1[1:3]
    print(list1)
    # 删除整个列表
    del list1
    print(list1)
    
5.4.4.9 clear()方法
  • 作用:清空列表,得到一个空列表

  • 举例:

    list1.clear()
    print(list1) # 得到一个空列表[]
    
5.4.4.10 copy()方法
  • 作用:复制列表

  • 举例:

    list1 =[1,2,3,4]
    print("复制前:",list1)
    b = list1.copy()
    print("复制后:",b)
    

    赋值,copy,deepcopy的区别?

5.4.4.11 sort()方法
  • 作用:对列表的元素排序。

  • 通过reverse参数来控制排序是升序还是降序,reverse只能传布尔值True或者False,默认是False(升序),所以如果不传入任何参数,表示默认升序排列,数字从小到大,字符根据ASCII码来排;

  • 数值跟列表以及字符串之间不能排序;

  • 通过key参数来控制排序的权重,即根据第几个字符来排序,如果有元素排序的条件相同,则最终排序结果跟存储的顺序保持一致。

    举例:

    list1 = [1,2,3,4,5]
    list2 = ["a","b","A","B","a"]
    list3 = ["中国","你好","加油"]
    # 默认升序排列
    list1.sort()
    print(list1)
    list2.sort()
    print(list2)
    list3.sort()
    print(list3)
    # 降序排列
    list1.sort(reverse=True)
    print(list1)
    
  • sort()和sorted()的区别?
    1. sort是列表中的一个方法,sorted是python里面的一个内置函数
    2. sort方法排序后不返回,sorted函数会返回一个排序后的新列表
    3. sort方法会改变原来的列表中的元素顺序,但sorted函数并不会改变原来的列表,只是返回一个新列表
5.4.4.12 reverse()方法
  • 作用:反转列表中的元素,reverse()方法和列表[::-1]的区别是前者会改变列表中的元素顺序,而后者不会改变顺序,只会把列表的元素反着取出来。

  • 举例:

    list1 = ["1","a","9","你好","heog"]
    list1.reverse()
    print(list1)
    # 切片方式反向
    print(list1[::-1])
    print(list1)
    
  • 列表与元素之间的转换

    • 通过字符串的join方法可以实现列表中的元素转字符串,通过创建list类的对象可以实现字符串的元素转列表

    • 举例:

      list1=['w', 'e', 'a' ,'r', 'e', 'g', 'o', 'o', 'd', 's', 't', 'u', 'd', 'e', 'n', 't', 's']
      print(type("".join(list1))) #列表转字符串,打印类型
      print("".join(list1))
      print(list("135"))
      

5.5 元组-tuple

5.5.1 元组的声明

  • 元组:元组是一组有序的数,用小括号()来表示,元组的元素跟元素之间用英文逗号隔开,元组跟列表的最大区别是元组中的单个元素不能增删改(元组是不可变的)。元组通常用于保护数据而存在。

  • 元组的声明:元组通过()或tuple()来声明。

  • 声明元组:

    #空元组
    tupl0=()
    print(type(tupl0),len(tupl0))
    #非空元组
    tup1=(1,2,5)
    print(type(tup1),len(tupl1))
    #用tuple实例化对象来声明元组
    num=123
    str1="你好吗"
    list1=[1,2,4,5]
    print(tuple(num)) # 报错
    print(tuple(str1))
    print(tuple(list1))
    
  • 声明一个只有一个元素的元组:需要加一个逗号来表明这是一个单元素的元组,否则解释器会理解成在外面加了一个普通的括号,打印类型为单个元素的类型。

    #一个元素的元组
    tupl2 =(2,)
    tupl3 =("nihao",)
    

5.5.2 元组的作用

  1. 可以实现对数据进行保护;

  2. 在操作不定长参数函数时,其参数的类型就是一个元组,所以可以直接将元组进行传入;

  3. 在函数或者方法中返回值可以返回多个值,默认是把多个值以元组的形式返回。

5.5.3 元组和列表的区别(面试问题)

  1. 列表用[]表示,元组用()表示;
  2. 列表可变,元组不可变:列表中的元素是可变的,可以对元素进行增删改操作;元组中的元素是不可变的,不能对元素进行增删改操作。

5.5.4 元组的切片

  • 元组的切片(有序的,可以使用下标索引),切片出来的数据还是保存在元组里的规则跟字符串、列表一致

    tup1=(1,2,(1,2,3),5,999,'fgjgjfs',[1,2,3])
    # 不在范围内
    print(tup1[1111:])
    # 取下标为2的元素
    print(tup1[2])
    # 取下标为1开始,终止值默认到最后,步长默认为1
    print(tup1[1:])
    # 倒序?
    # 取元组中的元组里的数据
    

5.5.5 元组的常用操作

  • 元组的修改(不是修改,实际是形成了一个新的元组)

    a. 转成列表间接使用列表的方法来操作,最后再转成元组

    print(tuple([1,2,3]))
    print(tuple("youseeyou"))
    list1=list(tup1)
    list1.insert(list1.index(5)+1,100)
    tup1=tuple(list1)
    print(tup1)
    

b. 也可以通过切片再拼接的方式实现。注意对字符串、列表、元组都可以用+来实现拼接操作。

  print(tup1[:4]+(100,)+tup1[4:])
  • 元组的删除

    元组只能用del 来删除整个元组

    tup1=(1,2,(1,2,3),5,999,'fgjgjfs',[1,2,3])
    del tup1
    print(tup1)
    

5.5.6 元组的常用函数

  • len()

  • max()

  • min()

其中max()和min()两个函数要保证比较的元素类型一致

5.6 字典dict

5.6.1 字典的声明

  • 字典:字典用于存放具有映射关系的数据,字典的元素是一系列的键值对,键和值之间用冒号隔开,而键值对之间用逗号隔开。在Python 3.6版本之前,字典是无序的,从Python 3.6版本开始,字典是有序的,字典的顺序就是定义字典时写的顺序。
  • 声明方式:通过{}dict()或字典推导式来声明。
  • 字典的特点:
    1. 字典由键key和对应的值value组成,比如{key1:value1, key2:value2}
    2. 字典的键必须是唯一的,值可以不唯一;
    3. 每个键与值之间用英文的冒号:隔开,两个键值对之间用英文的逗号,隔开;
    4. 值可以是任何数据类型,但键必须是不可变的,如字符串,数字或元组。
#创建空字典
dict1 ={}
print(type(dict1),len(dict1))
#创建非空字典
dict2={"名称":"乔峰","年龄":18}
#键重复的例子
dict2 = {"名称":"乔峰","年龄":18,"年龄"20}
#通过dict来实例化对象(转字典)
dict1 = dict(name="xiaoming",age=8,like1="篮球")
print(dict1)
#通过字典推导式声明{结果 for 变量 in 迭代对象} 或者 {结果 for 变量 in 迭代对象 if 布尔表达式};
people = [('小红', 18), ('小明', 45), ('小王', 22)]
dict3 = {k:v for k,v in people}
print(dict3)

5.6.2 根据key取value

方法1:需要根据键名去取,格式:字典名[键],注意这里一定是传入键名根据键名去取键值。

方法2:通过get()方法取键值

举例:

#通过key值取
dict2={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
print(dict2["名称"])
print(dict2["喜欢"][0])
#通过get方法去取键值
print(dict2.get("年龄"))

两种方法区别:方法1,如果key不存在,报KeyError;方法2则返回None

5.6.3 获取字典的所有键,值,以及键值对

  • keys():获取字典的所有键,返回一个可迭代序列

  • values():获取字典的所有值,返回一个可迭代序列

  • items():获取字典的所有键值对,返回一个可迭代序列

注意:以上函数返回的是一个可迭代序列,而不是列表,但可以通过list()转成列表。

5.6.4 修改字典

  • 修改已存在的键值对
dict2={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
#找到已在的key,然后重新赋值,修改的是value
dict2["名称"] = "段誉"
print(dict2)
  • 新增一个键值对

    在字典中增加一个元素(键值对),格式:字典名[键名]=键值,如果键名在字典中不存在就会增加,如果已存在就会修改它的键值

dict2={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
dict2["虚竹"] = "憨子"
print(dict2)
  • 新增多个键值对

    update()方法:作用是在字典中增加多个键值对,相当于字典的拼接,如果键名重复就覆盖原来的值

    举例:

dict1={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
dict2={"学历":"高中","毕业时间":2010,"city":"绵阳"}
dict1.update(dict2)
print(dict1)
dict1.update({"性别":"Female","公司":"华为"})
print(dict1)
# 字典相加不能用+
print(dict1+dict2)#错误的

5.6.5 字典的删除

5.6.5.1 del关键字

作用:删除整个字典或者根据键删除指定的键值对

举例:

dict1={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
#删除某个键值对-用del 字典名[键名]的方式来删除指定的键值对
del dict1["名称"]
print(dict1)

#删除整个字典
del dict1
print(dict1)
5.6.5.2 pop()方法

格式:pop(key[,default])

作用:根据键名删除键值对,并返回删除的值,如果找不到键就返回传入的default参数或者KeyError

举例:

dict1={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
print(dict1.pop("年龄"))
print("删除后:",dict1)
5.6.5.3 popitem()方法

作用:删除并返回字典的最后一个键值对,并以元组返回删除的键值对。

举例:

dict1={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
print(dict1.popitem())
print("删除后:",dict1)
5.6.5.4 clear()方法
  • 作用:清空字典
  • 举例:
dict1={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
dict1.clear()
print(dict1)
5.6.5.5 copy()
  • 作用:复制字典
  • 举例:
dict1={"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]}
dict2=dict1.copy()
print(dict1)
print(dict2)

5.6.6 字典的嵌套

字典中的key可以是数值型,字符串,元组(不可变数据类型),不能是list和dict;value可以是数值型,字符串,元组,列表或者另一个字典(可以是任何数据类型)。

dict1={"信息":{"名称":"乔峰","年龄":18,"喜欢":["阿朱","阿紫"]},"级别":[1,2,4],"排名":(1,2,3)}
#1.求年龄
print(dict1["信息"]["年龄"])
#2.求"阿朱"
print(dict1["信息"]["喜欢"][0])
#3.修改年龄为20
dict1["信息"]["年龄"] =20
print(dict1)

5.6.7 在格式化输出方法format()中传入字典

#字符串
print("小明喜欢%s,今年%d岁了"%("小花",9))
#1.字典(类型要对应)
print("小明喜欢%(name)s,今年%(age)d岁了"%{"name":"小花","age":9})
##format -这里传参时,*表示是元组,**表示字典,这里传的是字典,是一个整体,需要用**来解包,把字典解包为单个键值对,才能取到
print("小明喜欢{name},今年{age}岁了".format(**{"name":"小花","age":9}))
dict1={"name":"小花","age":9}
print("小明喜欢{name},今年{age}岁了".format(**dict1))

5.7 集合set

5.7.1 集合的声明

  • 集合:集合是一个无序的不重复元素序列。

  • 集合的声明:集合通过{}或set()或集合推导式来声明。注意:创建一个空集合必须用 set() 而不是 {},因为 {} 是用来创建一个空字典。

  • 集合的特点:

    1. 集合是无序的;
    2. 集合的元素不重复,对重复的元素会自动去重;
    3. 集合中的元素可以是任意不可变的数据类型;
    # 定义空集合
    set1 = set()
    set2 = {}会表示字典类型
    print(type(set1),len(set1))
    
    # 定义非空集合
    set1 = {9,1,5,6,5,1,2,2}
    set2 = {"k","k","k","khjj"}
    print(type(set1),len(set1))
    print(type(set2),len(set2))
    
    # 如下集合是非法的,报TypeError: unhashable type: 'list'
    set3 = {[1,2],"abc"}
    
    # 集合是无序的,所以重复执行如下两行代码输出可能不一样
    set4 = {1, 3, 5, "hello"}
    print(set4)
    
  • 利用集合中元素不可重复的特点对列表、元组、字符串去重。

    # 例1:对列表去重,只保留不重复的元素
    list1=[1,1,1,2,3,2]
    # 得到的是一个集合
    print(type(set(list1)))
    # 列表转换成集合
    print(set(list1))
    # 集合转换成列表
    print(list(set(list1)))
    
    # 例2:对元组去重,只保留不重复的元素
    tup1=(1,1,1,3,3,3)
    #元组转换成集合
    print(set(tup1))
    #集合转换成元组
    print(tuple(set(tup1)))
    
    # 例3:利用集合对字符串去重
    str1 = "aaaaffffcccc"
    print((set(str1)))
    print("".join(set(str1)))
    

5.7.2 集合的运算

集合的运算包括:交集、并集、差集、对称差集

  • 交集:取两个集合的公共部分,也就是同时存在于两个集合中的元素。可以通过运算符&或者集合的intersection()方法来实现集合的交集运算。

    set1={9,1,5,6,5,1,2,2}
    set2={1,6,2,20,50}
    # 运算符
    print(set1&set2)
    # 方法
    print(set1.intersection(set2))
    
  • 并集:两个集合中的所有元素,重复的元素只算一次。可以通过运算符|或者集合的union()方法来实现集合的并集运算。

    #运算符
    print(set1|set2)
    #方法
    print(set1.union(set2))
    
  • 差集:set1-set2的结果存在于set1中但不存在于set2中的元素(set1减去set1和set2的交集)。可以通过运算符-或者集合的difference()方法来实现集合的差集运算。

    #运算符
    print(set1-set2)
    #方法
    print(set1.difference(set2))
    
  • 对称差集:取两个集合的交集,然后根据交集取两个集合的补集,最后将补集取并集(并集减去交集),即去除两个集合的交集,各自剩下的元素组成一个新的集合。可以通过运算符^或者集合的symmetric_difference()方法来实现集合的对称差集运算。

    #运算符
    print(set1^set2)
    #方法
    print(set1.symmetric_difference(set2))
    

集合的使用举例:利用集合来判断两个字典的差异。

expect={"name":"张三","age":20,"sex":"男"}
actual={"name":"李四","heigh":170,"sex":"男"}
#实现断言:如果预期和实际不一致,要打印出它们的差异,输出:预期是{"name":"张三","age":20}实际是{"name":"李四","heigh":170}
set_expect=set(expect.items())
set_actual=set(actual.items())
print(set_expect)
print(set_actual)
print("预期是{}实际是{}".format(dict(set_expect-set_actual),dict(set_actual-set_expect)))

5.7.3 集合的常用函数

5.7.3.1 len()函数
  • 格式:len(集合)

  • 作用:计算集合中元素的个数

  • 举例:

    set1 = {1,2,(2,3),"hello"}
    print(len(set1))
    

5.7.4 集合的常用方法(了解)

5.7.4.1 add()方法
  • 格式:add(x)

  • 作用:将元素x添加到集合中,如果元素已存在,则不进行任何操作

  • 举例:

    set1 = {1,2,3,4,5}
    set1.add("nihao") #不能添加列表和字典
    print(set1)
    
5.7.4.2 update()方法
  • 格式:update(x)

  • 作用:可以添加元素,参数x可以是字符串、列表、元组、字典等可迭代类型的数据

  • 举例:

    set1 = {1,2,3,4,5}
    set1.update([1,6,7])#要添加可迭代的对象,添加字典时,只添加key值
    print(set1)
    
5.7.4.3 remove()方法
  • 格式:remove(x)方法

  • 作用:将元素x从集合中移除,如果元素不存在,则会拋KeyError异常

  • 举例:

    #remove()方法举例
    set1 = {1,2,3,4,5}
    set1.remove(3) #不会返回删除的值
    print(set1)
    
5.7.4.4 discard()方法
  • 格式:discard(x)方法

  • 作用:移除集合中的元素,且如果元素不存在,不会做任何操作也不会拋异常

  • 举例:

    #discard()举例
    set1.discard(3) #也不会返回删除的值
    print(set1)
    
5.7.4.5 pop()方法
  • 格式:pop()

  • 作用:随机删除集合中的一个元素

  • 举例:

    #pop()方法举例
    set1.pop() #不用传参数,且随机删除一个元素(每次删除的都是集合排序里的第一个元素),并返回删除的元素
    print("删除前",set1)
    print(set1.pop())
    print("删除后",set1)
    
5.7.4.6 del关键字
  • 作用:删除整个集合

  • 格式:del 集合

  • 举例:

    #del,删除整个集合
    del set1
    print(set1) # 删除后打印会报错
    
5.7.4.7 copy()方法
  • 作用:复制集合

  • 举例:

    set1 = {1,2,(2,3),"hello"}
    set2 = set1.copy()
    print(set1,set2)
    
5.7.4.8 clear()方法
  • 作用:清空集合

  • 举例:

    set1 ={1,2,4}
    set1.clear()
    print(set1)  # 清空过后为空集合set()
    

5.8 range序列

range是不可变的序列,元素为int类型,通常和for循环结合起来使用

格式 :range(起始值=0,终止值,步长=1),表示以指定的步长从起始值取到终止值-1,取值为左闭右开规则

  • 如果只传一个值,则表示终止值:range(3)
  • 如果传两个值,则表示,起始和终止值:range(1,3)
  • 如果传三个值,则表示起始,终止值和步长:range(1,10,2)

举例:

for i in range(5):
    print(i)   # i从0取到4
    
for i in range(1,5):
    print(i)	# i从1取到4
    
for i in range(1,5,2):
    print(i)	# i取1,3

可以使用list()传入一个range序列,来生成一个整数列表。

# 生成一个列表,列表中的元素是0到10的整数
list1=list(range(11))
print(list1)

5.9 布尔型bool

  • 布尔型bool:布尔型只有两个结果即True和False,分别表示真和假,一般用在条件判断中。

  • 在Python中,布尔型跟数值型参与运算时,True相当于1,False相当于0。

  • 可以用实例化bool类的对象把一个对象转成布尔值,在Python中,空字符串、空列表、空元组、空字典、空集合都被当做是False;非空即True。

    a=True
    print(type(a))
    print(a+5)
    print(False*100)
    print(bool(111))            	#True
    print(bool("fff"))          	#True
    print(bool([1,2,4]))      		#True
    print(bool(3,))      			#True
    print(bool({"A":1}))      		#True
    print(bool({3,'a'}))        	#True
    print(bool(""))           		#False
    print(bool([]))        	 		#False
    print(bool(()))        	 		#False
    print(bool({}))        		    #False
    print(bool(set()))          	#False
    print(type({}))
    print(type(set()))
    

5.10 特殊类型-NoneType

None是python中的一个特殊的常量,表示一个空的对象,在函数没有返回值的时候,输出函数的执行结果得到的就是None。None对应的数据类型是 NoneType。

print(type(None))
list1=["nihao",2,3]
print(list1.remove(2)) #删除函数,这个只是删除数据没有返回数据,所以打印的是None

关于None的几点说明:

  1. 数据为空不代表空对象,例如空字符串、空列表等都不是None;
  2. None不支持任何运算也没有任何内建方法;
  3. None与其他任何数据类型比较都返回False;
  4. None有自己的数据类型NoneType,不能创建其他NoneType对象(它只有一个值None);
  5. null为java里的空,和Python中的None类似;
  6. 在后面使用 的JSON数据中,使用 null表示值为空,转成Python对象 ,对应的就是None;
  7. 可以将None赋值给任何变量,也可以给None值变量赋值。

举例:

# None没有长度,所以报TypeError: object of type 'NoneType' has no len()
print(len(None))
# 空字符串的长度为0
print(len(""))

if 0 and None:
    print(1)
else:
    print(2)
print("" == None)

5.11 数据类型总结

5.11.1 容器数据类型

Python中,可包含其他对象的对象,称之为“容器”。前面讲的数据类型中,如下对象属于容器类型:

  • 字符串str
  • 列表list
  • 元组tuple
  • 集合set
  • 字典dict

int,float,bool为非容器类型。

5.11.2 可变和不可变类型

所谓可变对象,是指对象的内容是可变的,比如修改对象的内存时对象的内存地址不改变,例如 list。而不可变的对象则相反,当改变它的内容时对象的内存地址也会改变。一句话总结:可变就是值变地址不变。

  • 可变数据类型:List(列表)、Dictionary(字典)、Set(集合)
  • 不可变数据类型:Number(数字)、String(字符串)、Tuple(元组)

举例:

num1=100
print(id(num1))
num1=101
print(id(num1))
list1=[1,2]
print(id(list1))
list1.append(3)
print(list1)
print(id(list1))
5.11.3 可迭代和不可迭代的类型
  • 可迭代即可以重复的取出数据,存在__iter__()方法的,就是可迭代的对象

    • 可迭代:List(列表)、Dictionary(字典)、Set(集合)、String(字符串)、Tuple(元组)
    • 不可迭代:Number(数字)、bool(布尔类型)
  • 举例:

    #数据类型,后面能有__iter__()方法,就是可迭代的
    [].__iter__()
    "".__iter__()
    {2,1}.__iter__()
    {"name":"小明","age":18}.__iter__()
    
5.11.4 有序的和无序的类型(针对容器类型来讲的)

有序:List(列表)、String(字符串)、Tuple(元组)、Dict(字典)

无序:Set(集合)

字典在3.6之前的版本是无序的,从3.6版本开始,字典是有序的,字典的顺序就是元素存入的顺序。

5.11.5 值类型和引用类型

Python数据类型分为值类型和引用类型, 下面我们看下它们的区别:

  • 值类型:int、float、str、tuple,本身不允许被修改

  • 引用类型:list、set、dict,本身允许被修改

5.11.5.1 值类型

值类型本身不允许被修改,也就是不可变,数值的修改实际上是让变量指向了一个新的对象,所以不会产生共享内存的问题。

number1 = 11
print(id(number1))
number1=12
print(id(number1))

# 输出结果
140709030057064	140709030057096

以上的例子说明,当number1的值为11时,指向的是内存地址140709030057064,当修改了number1的值后,指向的是另外一个内存地址140709030057096。修改值类型的值,只是让它指向一个新的内存地址,并不会改变变量的值。

对驻留机制的理解?

来看下面一个例子:

# int类型的驻留
number1 = 11
number2 = number1
print(id(number1), id(number2))

# str类型的驻留
str1 = "helloworld"
str2 = "helloworld"
print(id(str1), id(str2))

# 输出结果
140724772394088 	140724772394088
1303019672240		1303019672240

Python的暂存区:

  1. Python在底层做了一定的优化,对于使用过小整数以及短字符串都会被缓存起来
  2. 之所以采用这种优化的方式,是因为python中数字和字符串一经创建都是不可修改的。所以不会出现,因使用了缓存的对象值造成“脏读”的问题。

上述中多个具有相同值的变量共享了内存地址,这是Cpython里面一个优化策略,叫驻留(interning)。CPython 还会在小的整数上使用这个优化措施,防止重复的创建”热门“数字,比如0,-1,42等等,但是CPython不会驻留所有的字符串和整数。

事实上Python 为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。而Python 对小整数的定义是 [-5, 257),只有数字在-5到256之间它们的id才会相等,超过了这个范围就不行了。同样的道理,字符串对象也有一个类似的缓冲池,超过区间范围内自然不会相等了。

总的来说,只有数值型和字符串型,并且在通用对象池中的情况下,a is b才为True,否则当a和b是int,str,tuple,list,dict或set型时,a is b均为False。

5.11.5.2 引用类型

引用类型本身是运行被修改的,也就是可变的,允许同一对象的值发生变化,但地址不变。但是需要注意一点,对可变数据类型的操作不能是直接进行新的赋值操作,比如说a = [1, 2, 3, 4, 5, 6, 7],这样的操作就不是改变值了,而是新建了一个新的对象,这里的可变只是对于类似于append、+=等这种操作。

list1 = [1]
list2 = list1
print(id(list1), id(list2))
list1.append(2)
print(list1, list2)
print(id(list1), id(list2))

# 输出结果
2815182132416 2815182132416
[1, 2] [1, 2]
2815182132416 2815182132416

思考:在这个例子中,如果既要实现list2复制list1的值,又要在list1的值发生改变后不影响list2,怎么实现呢?

list1 = [1]
list2 = list1.copy()
print(id(list1), id(list2))
list1.append(2)
print(list1, list2)
print(id(list1), id(list2))

# 输出结果
1303022457536	1303022457408
[1, 2]			[1]
1303022457536	1303022457408

赋值(=)和复制的区别:

  1. 赋值(=)相当于把list1的地址赋值给list2,list1和list2指向同一个列表
  2. 复制(copy方法)相当于把list1完全复制一遍放在一个新的内存地址中,你可以对复制后的东西任意操作而不会影响原对象

6. Python运算符

  • 算数运算符
  • 比较运算符
  • 逻辑运算符
  • 三目运算符
  • 身份运算符
  • 成员运算符
  • 赋值运算符

6.1 算数运算符

Python中的算术运算符:

以下假设变量 a=2,变量 b=3

运算符描述实例
+加 - 两个对象相加a + b 输出结果 5
-减 - 得到负数或是一个数减去另一个数a - b 输出结果 -1
*乘 - 两个数相乘或是返回一个被重复若干次的字符串a * b 输出结果 6
/除 - x 除以 yb / a 输出结果 1.5
%取模 - 返回除法的余数b % a 输出结果 1
**幂 - 返回x的y次幂a**b 为2的3次方为8
//取整除 - 向下取接近商的整数9//2结果为4,-9//2结果为-5
print(5*2) # 5乘以2
print(5/2) # 5除以2
print(5//2) # 5除以2取商
print(5%2) # 5除以2取余
print(5**3) # 5的3次方
#开方呢?125开3次方
print(125**(1/3))
#字符串、列表、元组的拼接
print("hello"+"world")
print("hello"*3)
print([1,6,8]+[1,6,8])
print([1,6,8]*3)
print((1,6,8)+(1,6,8))
print((1,6,8)*3)

几种特殊用法:

  • +运算:
    • 字符串+字符串:实现两个字符串的拼接
    • 列表+列表:实现两个列表的拼接
    • 元组+元组:实现两个元组的拼接
  • *运算:
    • 字符串*n:表示对字符串重复n次生成一个新的字符串
    • *字符串:表示把字符串解开(unpack),把一个字符串变成多个单独的字符
    • 列表*n:相当于把列表中的元素重复n次生成一个新的列表
    • *列表:表示把列表解开,把一个列表中的元素单独拿出来,常用于对函数传参
    • 元组*n:运算相当于把元组中的元素重复n次生成一个新的元组
    • *元组:表示把元组解开(unpack),把一个元组中的元素单独拿出来,常用于对函数传参
    • **字典:把字典解开(unpack),把字典中的每个键值对单独拿出来,常用于对函数传参

6.2 比较运算符

比较运算符用于比较两个变量的大小,比较运算符的运算结果是一个布尔值。比较运算符经常用在if条件判断中。

以下假设变量a为2,变量b为3:

运算符描述实例
==等于 - 比较对象是否相等(a == b) 返回 False。
!=不等于 - 比较两个对象是否不相等(a != b) 返回 True。
>大于 - 返回x是否大于y(a > b) 返回 False。
<小于 - 返回x是否小于y。所有比较运算符返回1表示真,返回0表示假。这分别与特殊的变量True和False等价。注意,这些变量名的大写。(a < b) 返回 True。
>=大于等于 - 返回x是否大于等于y。(a >= b) 返回 False。
<=小于等于 - 返回x是否小于等于y。(a <= b) 返回 True。

注意:<> 在python2.x中这个也表示不等于,在python3.x中不支持这个符号

print(5>7)
print(5>=7)
print(5<=7)
print(5!=7)
print(5==5)

6.3 逻辑运算符

逻辑运算符用于检测两个或者两个以上条件是否满足,包括:

  • and:逻辑与,表示两个条件同时满足
  • or:逻辑或,表示两个条件满足其中一个
  • not:逻辑非,表示反转运算对象的状态

逻辑运算只存在于布尔类型中,经常用在if条件判断中。

优先级:括号 > not > and > or

# and、or、not举例
print(True and True)	# True
print(True and False)   # False
print(False and True)   # False
print(False and False)  # False
print(True or True)   	# True
print(True or False)   	# True
print(False or True)   	# True
print(False or False)  	# False
print(not True)    		# False
print(not False)     	# True

# 优先级:括号>not>and>or
if 100>50 and (100<90 or 100!=200):
	print("条件成立")
else:
	print("条件不成立")

6.4 身份运算符

  • is:判断两个对象是同一个对象
  • is not:判断两个对象不是同一个对象

is比较的是两个变量的内存地址,is not和is相反。运算结果是布尔值。

a="hello world"
b="hello world"
print(id(a),id(b))
print(a is b)

is与==的区别: is比较的是内存地址,是判断两个对象是否是同一个对象,==比较的是值,是判断两个对象的值是否相等。

6.5 成员运算符

Python中的成员运算符:

  • in

  • not in

成员运算符经常用在if条件判断中。

格式:a in b,表示判断a是否是b的成员,成员运算的结果是一个布尔值。

if 'a' in 'hashdaskak':
	print("在的")

if "b" not in 'hashdaskak':
    print("不在")

#in经常用在for循环中,遍历元素
for i in [1,2,3,4,5,6,7,8]:
	print(i)

#判断某个值是否在字典的value中
dict1={"name":"张三","age":20}
if "张三" in dict1.values():
    print("在里面")

6.6 赋值运算符

以下假设变量a为2,变量b为3:

运算符描述实例
=简单的赋值运算符c = a + b 将 a + b 的运算结果赋值为 c
+=加法赋值运算符c += a 等效于 c = c + a
-=减法赋值运算符c -= a 等效于 c = c - a
*=乘法赋值运算符c *= a 等效于 c = c * a
/=除法赋值运算符c /= a 等效于 c = c / a
%=取模赋值运算符c %= a 等效于 c = c % a
**=幂赋值运算符c **= a 等效于 c = c ** a
//=取整除赋值运算符c //= a 等效于 c = c // a
:=海象运算符,可在表达式内部为变量赋值。Python3.8 版本新增的运算符

举例:

num_1 = 5
print(num_2 := num_1 + 5)
print(num_2)

#输出:
10
10

#在这个示例中,赋值表达式可以避免调用len()两次
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
if (n := len(a)) >= 10:
	print(f"List is too long ({n} elements, expected < 10)")
# 等价于以下代码
if (len(a)) >= 10:
	print(f"List is too long ({len(a)} elements, expected < 10)")

6.7 三(目)元运算符

语法:变量名= 结果值1 if 布尔表达式 else 结果值2

作用:如果布尔表达式成立就返回结果1,否则(不成立)返回结果2,在某些场景下使用三元运算符可以简化代码

举例:

#用if...else...语句实现
year1 = int(input("请输入年份:"))
if year1%2 ==0:
    print("偶数")
else:
    print("奇数")
    
#三元运算符格式
num1 = int(input("请输入一个整数:"))
print("偶数" if num1%2==0 else "奇数")

7. 流程控制

  • 条件判断
  • 循环

7.1 条件判断

条件判断用if语句实现,if语句的几种格式:

  • if...表示满足条件,则执行相应的操作,否则什么都不执行;

  • if...else...表示如果满足条件则执行if后面的操作,否则执行else后面的操作;

  • if...elif...elif...else...表示多条件判断,如果满足第一个条件就执行第一个操作,如果满足第二个条件就执行第二个操作,…,如果都不满足就执行else后面的操作。

注意:if可以独立存在,但else必须跟if配对,else是跟它前面最近的同级的if配对。

Python中的代码缩进:在python中相同缩进的代码属于同级,一般上下级之间缩进4个空格(按1次Tab

7.1.1 if...

作用:如果if后面的布尔表达式值为True就执行if下的代码块,如果为False就不执行

格式:

if 布尔表达式:
   代码块

举例:

#从键盘输入一个数字,如果大于10就输出“大于10”
num1=int(input("请输入一个数字:"))
if num1>10:
    print("大于10")

#结合布尔值(为True则执行,为False则不打印
if 8print("你好")
if "不为空"print("真的")

#结合成员运算符(in、not)-满足条件执行print,不满足则不执行
str1 = "adgcgag"
if "ad" in str1:
    print("在里面")

7.1.2 if...else...

作用:如果if后面的布尔表达式值为True就执行if下的代码块,如果为False就执行else下的代码块。

格式:

if 布尔表达式:
	代码块
  
#从键盘输入一个数字,如果大于10就输出“大于10”,如果不大于10就输出“不大于10”
num1=int(input("请输入一个数字:"))
if num1>10:
    print("大于10")
else:
    print("不大于10")

#从键盘输入一个整数,如果整数是奇数,就打印奇数,如果不是奇数,就打印偶数
third=int(input("请输入一个整数:"))
if third%2==1:
    print("奇数")
else:
    print("偶数")
#注意if和else是同级的,应该对齐。

7.1.3 if...elif...elif...else...

作用:从第一个条件开始逐一判断,遇到第一个满足的条件就执行它下面的语句,并结束判断不再往下判断。

格式:

if 布尔表达式:
    代码块
elif 布尔表达式:
    代码块
    ...	
else:
    代码块

举例:

#从键盘输入一个数字,如果大于10就输出“大于10”,如果小于10就输出“小于10”,否则输出等于10
num1=int(input("请输入一个数字:"))
if num1>10:
    print("大于10")
elif num1<10:
    print("小于10")
else:
    print("等于10")    
#举例:当遇到某个条件满足时就不会再往下判断
forth=int(input("请输入一个整数:"))
if forth<10:
    print("小于10")
elif forth>=10:
    print("大于10")
elif forth>=20:
    print("大于20")
else:
    print("其它")

7.2 循环

  • while循环

  • for循环

7.2.1 while循环

while循环:当满足循环条件时就执行循环体里的代码,直到循环条件不满足为止。中括号表示else是可选的,如果有else,那么else后面的代码会在循环正常结束后执行。使用while循环一定要有修改循环条件的语句,否则会造成死循环(在某条件下,一直执行语句,直到条件为False)。

格式:

while 布尔表达式:
	循环体
[else:
	语句块]

举例:

# 不带else语句
i=1  #给个初始值
while i<=10:	#1.条件限制
	print(i)
	i+=1 		#2.条件,递增
#两个条件要结合,如果不限制,会导致死循环

#带else语句
i=1
while i<=10:	#1.条件限制
	print(i)
	i+=1 		#2.条件,递增
else:
    print("打印完毕")

#实现1+2+3+...+100,并输出计算结果
num1=0
i=1
while i<=100:
    num1+=i
    i+=1
else:
    print(num1)
    
#思考:1*2*3*...*100 ?
  1. 当布尔表达的值为True的时候,就执行循环体的代码,直到布尔表达的值为False的时候或者被break;

  2. else语句是可选的,如果有else语句,当循环正常结束(不是被break语句结束的情况)后会执行else后面的语句,如果循环是被break掉,就不会执行else后面的操作。

7.2.2 for循环

迭代(Iteration):指重复执行某个操作,迭代最常用的表现就是遍历,经常用for循环来遍历可迭代对象(Iterable),常见的可迭代对象包括字符串、列表、元组、集合和字典等。

结构:

for 变量名 in 可迭代对象:
    循环体
[else:
   语句块]

举例:

#1.将字符串"helloworld"遍历出来添加到列表list1中
list1=[]
for i in "helloworld":
	list1.append(i)
print(list1)

#2.遍历元组中的元素
tuple1 = (1,3,5,6,7)
for i in tuple1:
    print(i)
else:
    print("遍历完成")

#3.用for循环遍历字典
dict1={"姓名":"张三","性别":"男","年龄":20}
#遍历字典,默认取到的是字典的key
for i in dict1:
	print(i)
#遍历字典的key
for i in dict1.keys():
	print(i)
#遍历字典的value
for i in dict1.values():
	print(i)
#遍历字典的键值对
for i,j in dict1.items():
	if i=="年龄":
		print(i,j)

#4. 用for循环实现1+2+3+...+100,并输出计算结果  

while循环和for循环的区别:while循环执行前,循环次数是不确定的;for循环在循环前执行次数是已经确定的。

7.2.3 循环的嵌套

7.2.3.1 while中套while循环

举例:

# 在10中找两个数i和j,满足条件j <= (i / j)时,如果i能除尽j则打印“能除尽”并打印i和j的值,如果不满足则打印“不满足条件”并打印不满足条件的i和j的值
i = 2
while (i < 10):
    j = 2
    while (j <= (i / j)):
        if not (i % j):
            print("能除尽",i,j)
        j = j + 1
    else:
        print("不满足条件",i,j)
    i = i + 1
print("算完")
7.2.3.2 for循环中套for循环

举例:

# 九九乘法表
for i in range(1,10): # 依次取数1到9
    for j in range(1,i+1): # 根据i的取值来取值,i取1时,j的范围也是1
        print("{} x {} = {}\t".format(j,i,i*j),end="") # 根据上面两个循环取出的书打印格式化数据。
    print()# 把一次循环的数据打印完成后换行

除此之外,还可以在while循环中嵌套for循环,以及在for循环中嵌套while循环。

7.2.3.3 冒泡排序

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

arr = [64, 34, 25, 12, 22, 11, 90]

# 遍历所有数组元素
for i in range(len(arr)-1):
    for j in range(0, len(arr) - i - 1):
        if arr[j] > arr[j + 1]:
            arr[j], arr[j + 1] = arr[j + 1], arr[j]
else:
	print(arr)

7.2.4 循环中的continue/break/pass语句

  • continue
  • break
  • pass
7.2.4.1 continue语句

作用:continue的作用是结束本次循环(在某一次循环中遇到continue语句时,直接结束本次循环,不执行continue后面的代码,而开始执行下一次循环)。

举例:

# 在0到10的数字中打印所有奇数,不是奇数的就不打印出来
a = 0
while a < 10:
    a += 1
    if a%2 ==0:
        continue   # 跳过本次循环,进入下一次循环
    print("奇数",a)
else:
    print("没有奇数了")

#3、输出0到100之间被3整除的整数,如果这个数能被5整除则不输出
for i in range(101):
    if i%3==0:
        if i%5==0:
            continue
        print(i)
7.2.4.2 break语句

作用:是结束本层循环,可以跳出for和while的循环体(所有循环),任何对应的循环else块将不执行。

举例:

# 整数0到10中,取能被2整除的数,取到第一个数就结束循环,不执行后面的代码
a = 0
while a < 10:
    a += 1
    if a%2 ==0:
        break # 满足上面条件了,直接结束本层所有循环,不执行后面的语句
    print("奇数",a)
else:
    print("没有奇数了")
7.2.4.3 pass语句

作用:是空语句,是为了保持程序结构的完整性。不做任何事情,一般用做占位语句

举例:

# 输出 Python 的每个字母
for str1 in 'Python':
   if str1 == 'h':
      pass # 占位,不做任何操作
   print(str1)

思考:

  • 求100以内的偶数

  • 100以内的奇数

  • 分别打印如下图形

图形1:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图形2:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 图形3:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 图形4:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# 每层不同的*数量,依次递增(差一)
for a in range(6):
 print("*"*a)
# 每层不同,递增差二
#1.方法1
for b in range(1,8,2):
 print("*"*b)
#2.方法二
b = 5
for b in range(1,b+1):
 print("*"*(2*b-1))

# 金字塔
cengshu = 7
for b in range(1,cengshu+1):
 print(" "*(cengshu-b),"*"*(2*b-1))

# 金字塔,中间为空           
for i in range(6):  #循环6次打印6行
 for j in range(5-i):   #打印空格每次循环递减
     print(' ',end='')
 for q in range(2*i+1): #打印星星
    if q==0 or  q==2*i: #判断打印星星位置,在开头,结尾和最后一行打印星星
        print('*',end='')
    else:
        print(' ',end='')
 print() #每行循环结束后换行            

作业:二分查找

8. 推导式

  • 列表推导式
  • 字典推导式
  • 集合推导式
  • 生成器推导式

推导式的作用是可以快速地生成一个列表、字典或者集合,同时简化代码。

8.1 列表推导式

列表推导式的语法:[结果 for 变量 in 可迭代对象] 或者 [结果 for 变量 in 可迭代对象 if 布尔表达式]

举例:

# 普通形式
## 以列表的形式输出100以内的正偶数
list1=[]
for i in range(1,101):
	if i%2==0:
	list1.append(i)
print(list1)

# 列表推导式举例
# 把100以内(含)的偶数放到列表中输出
print([i for i in range(1,101) if i%2==0])

# 列表推导式不加if
print([i for i in range(2,101,2)])

# 统计字符串中只出现一次的字符,以列表返回字符串
str1="helloworld"
print([i for i in str1 if str1.count(i)==1])

8.2 字典推导式

语法:{结果 for 变量 in 可迭代对象} 或者 {结果 for 变量 in 可迭代对象 if 布尔表达式};注意字典推导式的结果是键值对,即key:value

举例:

tupl1=(("姓名","张三"),("年龄",20),("体重",190),("身高",180))
print({i:j for i,j in tupl1})

#交换key和value的位置
print({j:i for i,j in tupl1})

#加判断条件
print({i:j for i,j in tupl1 if j !=190})

#统计字符串中每个字符出现的次数,以字典返回
str1="helloworld"
print({i:str1.count(i) for i in str1})

#统计字符串中只出现一次的字符,以字典返回字符及出现次数
str1="helloworld"
print({i:str1.count(i) for i in str1 if str1.count(i)==1})

8.3 集合推导式

语法:{结果 for 变量 in 可迭代对象} 或者 {结果 for 变量 in 可迭代对象 if 布尔表达式};集合推导式跟字典推导式的区别是:字典推导式的结果是键值对,集合推导式的结果是单个结果。

举例:

dict1={"姓名":"张三","年龄":20,"体重":180,"身高":180}
print({x for x in dict1.keys()})
print({x for x in dict1.values()})
# 加if判断,只取int类型的数据
print({x for x in dict1.values() if type(x)==int})

8.4 生成器推导式

语法:(结果 for 变量 in 可迭代对象)或者(结果 for 变量 in 可迭代对象 if 布尔表达式)

举例:

gen_1 = (a**2 for a in range(1,10))

#用元组形式展示数据
print(tuple(gen_1))

9. 函数

定义:函数就是完成特定功能的一个语句组,这组语句可以作为一个单位使用,并且取一个名字,这个名字叫做函数名

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数的作用:

  1. 可以通过函数名在程序不同的地方多次调用,这样就不用在不同的地方重复编写这些语句
  2. 函数是实现特定功能的一段代码,通过函数可以方便地重复使用代码,让程序的结构更清晰,方便代码的走读。
  3. 函数能提高应用的模块性,和代码的重复利用率

9.1 内置函数(内建函数)

系统内置函数,比如:len()/max()/min()/print()/input()/sum()/ord()/chr()/...;内置函数也叫内建函数。

input函数举例:

  • python3.x从控制台接收输入内容:用input()函数实现输入

  • 2.x版本中使用的是raw_input()函数

  • 语法:input(“提示语”)

  • 注意input函数默认把输入的内容当作字符串处理,如果要转换成整数,需要用强制数据类型转换比如int(a)

     num = input("请输入密码:")
     print(type(num)) # 打印输入信息的数据类型
    

eval()函数:

​ eval()函数的作用是传入一个字符串,将字符串转成引号里面本来的类型,比如eval(“100”)返回的是整数100,eval(“[1, 3, 5]”)得到的是列表[1, 3, 5]。

9.2 自定义函数

9.2.1 函数的声明与调用

定义函数的语法:

# 定义函数
def 函数名(参数列表):
	语句体

# 调用函数
函数名()

说明:

  • 函数代码块以def关键字开头,后接函数名称和圆括号(),圆括号中间放传入的参数,也可以不传入参数,()后面加一个英文的冒号;
  • 函数的第一行语句可以选择性地添加注释,用来解释函数的作用、参数、返回值等;
  • return语句结束函数,选择性地返回一个值给调用方。不带return语句就相当于返回None。

举例:

# 定义函数
def speak():
    name = "小明"
    print("你好!{}".format(name))
# 调用函数
speak()

9.2.2 return语句

return语句用于**结束函数,**选择性的向调用方返回一个表达式或者一个值。不带return语句的函数返回None

  • 情况一:同级的多个return只返回第一个return的值,因为函数执行时遇到第一个return返回后就结束了

  • 情况二:条件判断中,只返回第一个满足条件的第一个return的值,如果都不满足,则返回None

举例:

# 情况一:同级的多个return只返回第一个return的值
def add(a=1,b=2):
    print(a)
    print(b)
    return a+b
    return b-a
print(add())

# 条件判断中,只返回第一个满足条件的第一个return的值,如果都不满足,则返回None
def add(a,b):
    if a>b :
        return a-b
    if a<b :
        return a+b
print(add(3,4))
print(add(6,3))
print(add(4,4))

9.2.3 函数的说明与注释

函数的说明举例:

def f_1(a, b):
    """
    传入两个int类型的参数,返回它们的差
    :param a:第一个int类型参数
    :param b:第二个int类型参数
    :return:返回a-b
    :rtype:int类型
    """
    return a-b

可以看到上述的包括了函数及其各个参数的具体用途,以及返回值的类,这就是函数的说明,作用是提高了代码的可读性。

可以通过__doc__属性来查看函数的说明:

print(f_1.__doc__)

函数的注解举例:

def f_2(a:list, b:list)->list:
    a.append(b)
    return a

a:listb:list是对参数的注释,->list是对返回值的注释。注释可以使书写函数更方便,同时调用也会更便利。

通过__annotations__属性来查看函数的注释:

print(f_2.__annotations__)

9.2.4 函数中的参数

9.2.4.1 形参和实参
  • 形参:在定义声明时所传入的参数

  • 实参:在调用函数时所传入的参数

#name,age为形参,定义时
def speak(name,age):
	print(name)
    print(age)

#中间的值为实参,即调用时传入的值为实参
speak("小马",18)
9.2.4.2 位置参数(也叫必须参数)

在调用函数时,必须以形参的顺序传入实参,调用时的数量必须和声明时的一样,不能传多也不能传少。这种传入参数的方式叫位置参数。

#声明一个函数
def speak(name,age,like):
	print("小明的女朋友{},今年{},喜欢{}".format(name,age,like))

#调用函数
speak("小茵",18,"逛街")
9.2.4.3 关键字参数

函数调用时指定形参名称来传入实参,传入的实参顺序可以与声明的形参顺序不一致,但不能传多也不能传少,这种方式叫关键字参数。( Python 解释器能够用参数名匹配参数值)

#声明一个函数
def speak(name,age,like):
	print("小明的女朋友{},今年{},喜欢{}".format(name,age,like))

#调用函数
speak(name="小茵",age=18,like="逛街")
speak(like="逛街",name="小茵",age=18)
9.2.4.4 默认参数(也叫缺省参数)
  • 默认参数就是在声明函数的时候,给参数一个默认值,在调用时如果不对这个参数传值,就使用默认值,如果对这个参数传值,就使用实际传入的值;
  • 如果在声明函数的时候,有默认参数,需要把默认参数放到位置参数之后。
def speak(name,age,like="看电影")
	print("小明的女朋友{},今年{},喜欢{}".format(name,age,like))
    
speak("小茵",18,"逛街") #可改默认参数的值
9.2.4.5 不定长参数

不定长参数包括不定长位置参数和不定长关键字参数两种形式,分别在形参前加*和**来表示。不定长参数在实际调用时,可以传入0个、1个或多个实参。(你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述 2 种参数不同,声明时不会命名。加了星号 * 的参数在调用时会以元组(tuple)的形式导入,存放所有未命名的变量参数)

  • 不定长位置参数:形参前加*,可以接收任意多个实参,接收的实参以元组的形式参与函数内的运算。

声明:

def 函数名(*args):
   函数体

举例:

#声明一个不定长参数函数
def f_1(*args):
    print(args)
    return sum(args)

#方法一:调用不定长参数的函数,可以传入0个、1个或多个参数,传入的参数是按元组参与函数运算
print(f_1())
print(f_1(1))
print(f_1(1,2))
print(f_1(1,2,3))

#方法二:调用不定长参数的函数,将多个参数放到列表、元组或集合中传入,但需要在列表、元组或集合前加*来对列表或元组解包,把列表中的元素作为多个参数一一传给函数
# print(f_1([1,2,3]))     #错误的
print(f_1(*[1,2,3]))
print(f_1(*(1,2,3)))  
print(f_1(*{1,2,3}))
  • 不定长关键字参数:形参前加**,可以接收任意多个实参,接收的实参以字典的形式参与函数内的运算。

声明:

def 函数名(**kwargs):
	函数体

举例:

#声明一个不定长关键字参数函数
def f_1(**kwargs):
    return kwargs

#方法一:调用不定长参数的函数,可以传入0个、1个或多个参数,必须以关键字参数的形式传入,传入的参数是按字典的形式参与函数运算
print(f_1())
print(f_1(name="张三"))
print(f_1(name="张三",age=20))
print(f_1(name="张三",age=20,sex="男"))

#方法二:调用不定长参数的函数,将多个参数放到字典中传入,但需要在字典前加**
# print(f_1({"name":"张三"}))     #错误的,原因是必须以关键字参数传入
print(f_1(**{"name":"张三"}))
print(f_1(**{"name":"张三","age":20}))
print(f_1(**{"name":"张三","age":20,"sex":"男"}))

注意:在函数内同时使用不定长位置参数(*)以及不定长关键字参数(**)时,不定长位置参数必须在不定长关键字参数之前。

9.2.4.6 位置参数、默认参数以及不定长参数同时使用

如果在声明和使用函数时,同时存在位置参数、默认参数以及不定长参数,必须遵守以下顺序:1.位置参数;2.不定长位置参数;3.默认参数;4.不定长关键字参数。

# 格式化字符串-混合传值
def person(a, *tuple1,like="篮球",**dict1):
    print(a)
    print(dict1)
    print("{name}今年{age}岁了,每{}天打{}次{}".format(a,*tuple1,like,**dict1))

# 调用函数
person(2,*(3,),"足球",**{"name":"小王","age":30}) # 调用函数并传参

# 定义函数时传不同的参数类型,调用函数时传一组元素,会自动将元素放到对应的参数类型中
def f(a,*b,d1=9,**c):
    print(a) # 必须参数
    print(b) # 元组存数据
    print(c) # 字典存数据
    print(d1) # 默认参数9   
# 调用函数
f(1,2,34,5,8,d=4,e=6,g="你好")

9.2.5 匿名函数(lambda)

  • 又称之为高效函数,一次性函数,丢弃函数,也叫lambda函数(没有函数名的函数);因为在声明的时候可以直接调用(不需要先声明(定义)然后再调用)。而普通函数是先定义后再进行调用进行输出,比如是def 函数名():后面用的时候才进行调用。

  • 匿名函数的定义语法:lambda 变量名.... : 语句表达式

  • 匿名函数的调用:直接用括号把匿名函数括起来,在后面再用括号传参

# 求两个数的和
# 普通的函数
# 定义
def f_1(x,y):
    return x+y
# 调用
print(f_1(3,7))

# 匿名函数
print((lambda x,y:x+y)(3,7))

匿名函数举例

# 例子1:传入两个整数参数,以列表返回两个整数之间所有整数
x=int(input("输入x:"))
y=int(input("输入y:"))
print((lambda x,y:list(range(x,y+1)) if x<y else list(range(y,x+1)))(x,y))

# 例子2:传入两个整数参数,求该列表中的所有数的和
x=int(input("输入x:"))
y=int(input("输入y:"))
print(sum((lambda x,y:list(range(x,y+1)) if x<y else list(range(y,x+1)))(x,y)))

9.2.6 函数的递归

  • 函数内部可以调用其他函数,当然在函数内部也可以调用自己,在函数内部调用自己叫着函数的递归

  • 函数内部的代码是相同的,只是针对参数不同,处理的结果不同,当参数满足一个条件时,函数不再执行。这个非常重要,通常被称为递归的出口,否则会出现死循环。

#举例1:通过递归实现阶乘,声明一个函数,传入参数n,实现1*2*…*n
def f_1(n):
    print(n)
    if n==1:
        return 1
    return n*f_1(n-1)
print(f_1(10)) 

#举例2:通过递归实现求和运算,实现声明一个函数,传入参数n,实现1+2+...+n
def f_2(n):
    if n==1:
        return 1
    return n+f_2(n-1)
print(f_2(10))

9.2.7 函数的全局变量和局部变量

9.2.7.1 全局变量与局部变量的概念
  • 全局变量:在函数的外面定义的变量就叫全局变量,所有函数可以调用;
  • 局部变量:在函数的内部定义的变量叫局部变量,局部变量只能当前函数内调用。

全局变量与局部变量举例:

# 全局变量a
a = 1

def f_1():
    # 局部变量b
    b = 2
    # 在函数f_1中调用全局变量a和局部变量b可以成功
    print("a=", a)
    print("b=", b)

def f_2():
    # 在函数f_2中调用全局变量a可以成功,但调用局部变量b不成功,因为b只能在函数f_1中调用
    print("a=", a)
    print("b=", b)

f_1()
f_2()
9.2.7.2 global关键字

global关键字用来在函数或其他局部作用域中使用全局变量。

  1. 如果局部要对全局变量修改,而不使用global关键字。

    count = 0
    def global_test():
        count += 1
        print(count)
    global_test()
    
    # 输出:
    UnboundLocalError: cannot access local variable 'count' where it is not associated with a value
    
  2. 如果局部要对全局变量修改,应在局部声明该全局变量。 使用global 变量名来声明

    count = 0
    def global_test_1():
        global count
        count += 1
        print(count)
    def global_test_2():
        global count
        count += 3
        print(count)
        
    global_test_1()
    print(count)
    global_test_2()
    print(count)
    
    # 输出:
    1
    1
    4
    4
    
  3. 如果局部不声明全局变量,并且不修改全局变量,则可以正常使用。

    count = 0
    def global_test():
        print(count)
    global_test()
    
    # 输出
    0
    

10. 迭代、可迭代对象、迭代器、生成器(面试重点)

10.1 迭代Iteration

迭代Iteration:所谓迭代就是重复运行一段代码语句块的能力,就好比在一个容器中进行一层一层遍历数据,在应用过程中for循环最为突出。迭代就是从某个容器对象中逐个地读取元素,直到容器中没有元素为止。迭代迭代,更新换代,在上一次基础上更新成新的东西。

# 使用for循环迭代这个字符串,其实就是我们说的遍历这个字符串
for i in "hello world":
	print(i)

10.2 可迭代对象Iterable

10.2.1 什么是可迭代对象

可迭代对象Iterable:可以被迭代的类型,怎么判断是否可迭代?

所有的类型只要有__iter__()方法就是可迭代的。我们现在已知的可迭代对象有:str,list,tuple,range,set,dict_keys,dict_values,dict_items

方法名前后有两个下划线的,也叫魔法方法。

10.2.2 怎么判断可迭代对象

怎么去判断某个对象是否有__iter__()方法?

  1. dir()函数来查询是否包含__iter__()方法;
  2. 通过对象.方法的方式去调用看有没有__iter__方法;
  3. 通过内置的实例对象函数isinstance()判断,可迭代对象是Iterable类的实例,返回True就说明是可迭代对象,False则表示不是可迭代对象。isinstance()函数格式:isinstance(object, class)
# 用dir()函数打印数据的全部方法,看看是否包含__iter__()方法
print(dir(list1))

# 查看某个元素或序列是否有.__iter__()方法,有就是可迭代的对象
"hello world".__iter__()
[2].__iter__()
(1,).__iter__()
{"name":"jack"}.__iter__()
{"name":"jack"}.keys().__iter__()
range(11).__iter__()
list1=[]
a=9

# 通过内置的实例对象函数isinstance()判断
from collections.abc import Iterable,Iterator
print(isinstance(list1,Iterable)) 	# 结果是True
print(isinstance(a,Iterable)) 		# 结果是False

10.3 迭代器Iterator

10.3.1 什么是迭代器

  • 迭代器Iterator:迭代器一定是可迭代对象,迭代器中有两个特殊的方法是__iter__()__next__()方法

  • 创建迭代器的方法:使用内置的iter()函数或者__iter__()方法来创建迭代器

  • 举例:

    # 将列表转换为迭代器
    list1=[1,3,5,7,9]
    it1=iter(list1)
    it2=list1.__iter__()
    print(type(it1))
    print(type(it2))
    
    # 打印迭代器中的数据,每次只能打印一个元素,有多少元素就需要打印多少次
    # 用next()函数取迭代器中的数据
    print(next(it1)) 
    # 用__next__()方法取迭代器中的数据
    print(it1.__next__()) 
    print(it1.__next__())
    print(it1.__next__())
    print(it1.__next__())
    # print(it1.__next__())
    # print(it1.__next__())
    
  • 可迭代对象与迭代器关系示意图:

    举例:a = iter([4, 3, 6, 8])

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 迭代器的特点是:

    1. 迭代器一定是可迭代对象;

    2. 迭代器通过next()函数或者__next__()方法取值,每迭代一次取一个值,只能往前取不能后退;

    3. 当取完最后一个值的时候,再执行next()函数或者__next__()方法会报StopIteration异常,表示取完了。

10.3.2 怎么判断迭代器

判断一个对象是迭代器还是迭代对象,可以使用以下两种方法:

  1. 通过对象包含__iter__()__next__()方法来判断;
  2. 需要判断是迭代器还是迭代对象,可以通过实例对象函数isinstance()进行判断,迭代器和可迭代对象的对象类型分别是IteratorIterable(都是collections.abc模块下)
from collections.abc import Iterator,Iterable
# Iterator是判断是否是迭代器
# Iterable是判断是否是可迭代对象
print(isinstance(list1,Iterable))   			#判断list1是否是可迭代对象返回True或False
print(isinstance(list1,Iterator))   			#判断list1是否是迭代器返回True或False
print(isinstance(iter(list1),Iterator)) 		#True
print(isinstance(it1,Iterable))         		#True
print(isinstance(it1,Iterator))         		#True

#计算1+2+3+...+1000000000
#print(sum(list(range(1,1000000001))))
#print(sum(iter(range(1,1000000001))))

迭代器的优缺点:

  1. 迭代器优点:节省内存,迭代器在内存中相当于只占一个数据的空间,因为每次取值都会把上一条数据在内存中释放,加载当前的此条数据。
  2. 迭代器的缺点,不能直观的查看里面的数据。取值时不走回头路,只能一直向下取值。

10.3.3 for循环的底层原理

for循环实现遍历的底层原理:(for循环将其都封装好了,所以for循环in后面必须跟的是可迭代对象

  1. for循环可以传入可迭代对象或者迭代器对象,在循环的时候先调用可迭代对象的iter()函数来生成迭代器;
  2. 调用迭代器的next()函数来迭代每个元素;
  3. 当迭代完最后一个元素时,再继续迭代会报StopIteration异常,for循环捕捉到这个异常就知道已经迭代完了,就结束循环。

for循环的实现原理:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

也可以用for循环去遍历迭代器。

10.4 生成器Generator

10.4.1 什么是生成器

生成器Generator:在 Python 中,生成器的本质就是一个迭代器,使用了yield 的函数被称为生成器(generator)。

10.4.2 生成器怎么创建

创建生成器的方法:

  • 方法一:
  1. 第一步:定义一个包含yield语句的函数
  2. 第二步:调用第一步创建的函数得到一个生成器
# 例1:使用yield关键字把函数变成装饰器
def generator_1():
    yield 1
 # 创建生成器,如下表示创建一个生成器,赋值给gen
gen=generator_1()
print(type(gen))

# 在函数中可以使用多个yield
def generator_2():
    yield 1
    yield 2
    yield 3
    
# 也可以一个yield语句返回多个值,跟使用return类似,此时将返回一个元组
def generator_3():
    yield 1, 2
  • 方法二:使用生成器推导式来创建生成器
# 例2:通过生成器推导式创建生成器
print(type((i for i in range(11))))
print((i for i in range(11)))#是一个生成器对象

10.4.3 return与yield的区别

return的作用:

  1. 给调用者返回值;
  2. 执行遇到第一个return语句时就结束函数。

yield的作用:

  1. 给调用者返回值;
  2. yield把函数变成了生成器;
  3. 生成器运行时遇到yield后先返回再挂起;
  4. 在函数中可以使用多个yield。

10.4.4 生成器的运行

带有 yield 的函数执行过程比较特别:

  1. 调用该函数的时候不会立即执行代码,而是返回了一个生成器对象;

  2. 当使用next()函数或者__next__()方法作用于返回的生成器对象时,函数开始执行,在遇到 yield 的时候会『暂停』,并返回当前的迭代值; 也可以使用for循环来迭代生成器对象,因为在for循环中会自动调用next()函数;

  3. 当再次使用 next() 的时候,函数会从原来『暂停』的地方继续执行,直到遇到 yield语句,如果没有 yield 语句,则抛出异常;

整个过程看起来就是不断地 执行->中断->执行->中断 的过程。一开始,调用生成器函数的时候,函数不会立即执行,而是返回一个生成器对象;然后,当我们使用 next() 作用于它的时候,它开始执行,遇到 yield 语句的时候,执行被中断,并返回当前的迭代值,要注意的是,此刻会记住中断的位置和所有的变量值,也就是执行时的上下文环境被保留起来;当再次使用 next() 的时候,从原来中断的地方继续执行,直至遇到 yield,如果没有 yield,则抛出异常。简而言之,就是 next() 使函数执行, yield 使函数暂停。

# 在定义函数时,使用yield来返回值
def generator_1():
    yield 1
    yield 2
    yield 3

# 直接这个函数的调用结果,得不到返回的值
print(generator_1())

# 使用next()函数或者__next__()方法来运行该函数,但如下每次执行时都返回第一个值,因为每次调用的时候都会创建一个生成器
print(next(generator_1()))
print(next(generator_1()))
print(generator_1().__next__())

# 可以调用一次函数,对返回结果赋值给一个变量,这样可以获取生成器所有返回的值
def generator_1():
	yield 1
	yield 2
	yield 3
# 调用函数,返回一个生成器对象,赋值给变量gt
gt=generator_1()
# 通过next()函数来执行这个生成器对象
print(next(gt))
print(next(gt))

# 使用for循环执行生成器
for i in gt:
    print(i)

10.4.5 send()方法

通过send()方法也可以执行生成器,同时可以向生成器传入值。

send()方法与next()函数的区别:

  1. send()方法与next()函数都用来执行生成器;
  2. send()方法会将传入的值赋给上次中断时yield语句的执行结果,然后再执行生成器,从而实现与生成器方法的交互;
  3. 在执行生成器时,如果第一次执行使用send()方法,因为没有挂起的yield语句来接收传入的值,所以会报TypeError异常。
  4. 简单地说, send() 方法就是 next() 函数的功能,加上传值给 yield 。
# 例1:第一次使用send()方法执行生成器
def generator_1():
  yield 1
  yield 2
  yield 3

gt1=generator_1()
# 如下代码会报错,因为第一次需要先使用next()函数来执行生成器
print(gt1.send(100))
print(gt1.send(200))

# 例2:第一次使用next()执行生成器,第二次开始使用send()执行生成器并传入值
def generator_2():
  a=yield 1
  b=yield a
  yield b
  c=yield 2
  print(c)
  yield c

gt2=generator_2()
# 首先使用next()函数执行生成器,返回1之后挂起
print(next(gt2))
# 使用send()方法传入100给yield 1的执行结果,也就是a,然后再执行yield a,返回100,再挂起
print(gt2.send(100))
# 使用send()方法传入2.5给yield a的执行结果,也就是b,然后再执行yield b,返回2.5,再挂起
print(gt2.send(2.5))
# 使用send()方法传入'abc'给yield b的执行结果,但并没有引用他,然后执行yield 2,返回2,再挂起
print(gt2.send('abc'))
# 传入efg给yield 2的执行结果,也就是c,然后再打印c,最后再执行yield c,返回efg
print(gt2.send('efg'))

10.4.6 生成器与迭代器的区别

  • 生成器:
    1. 生成器本身是一种特殊的迭代器,也就是说生成器就是迭代器。
    2. 生成器会自动实现迭代器协议,也就是说只要我们yield后,自动就生成了next对象包括StopIteration等结构。
    3. 生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。生成器不仅“记住”了它的数据状态,生成还记住了程序执行的位置。
  • 迭代器:
    1. 迭代器是一种支持next()操作的对象。它包含了一组元素,当执行next()操作时,返回其中一个元素;
    2. 当所有元素都被返回后,再执行next()报异常StopIteration
    3. 生成器一定是可迭代的,也一定是迭代器对象。
  • 它们的区别:
  1. 迭代器是访问容器的一种方式,也就是说容器已经出现。我们是从已有元素拓印出一份副本,只为我们此次迭代使用。而生成器则是自己生成元素的。也就是前者是从有到有的复制,而后者则是从无到有的生成。
  2. 在用法上生成器只需要简单函数写法,配合yield就能实现。而迭代器真正开发中很难使用到。我们可以把生成器看做,python给我们提供的特殊接口实现的迭代器。

最后,附上一张图来解释容器、可迭代对的、迭代器、生成器之间的关系:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10.4.7 生成器使用举例:处理大量数据

生成器的优势在于在没有牺牲很多的速度情况下,内存占用更小,在一定的业务场景下,支持大数据的操作。

举例:通过列表和生成器分别处理1亿条数据对比

# 通过列表实现,如下代码在执行时可观察电脑的内存使用情况,内存会被占满
a = []
for i in range(100000000):
  	temp = ['你好']*2000
  	a.append(temp)

[[你好,你好,...,你好],[你好,你好,...,你好],...,[你好,你好,...,你好]]
for ele in a:
	# print(ele)
  	continue

# 通过生成器实现,如下代码在执行时可观察电脑的内存使用情况,内存占用不大
def get_list_element():
    for i in range(100000000):
    	temp = ['你好']*2000
        yield temp
# 创建一个生成器
a = get_list_element()
for ele in a:
	# print(ele)
	continue

11. 高阶函数

11.1 map()函数

map()函数语法:map(function,iterable)

  • 参数:

    • function:函数
    • iterable:一个或多个序列
  • 返回值:迭代器对象

作用:map()是 Python 内置的高阶函数,它接收一个函数 function 和一个 iterable,并通过把函数 function依次作用在 iterable的每个元素上,并返回一个新的迭代器;map()函数也可以接收多个iterable

# 传入一个序列的例子
def f(x):
    return x * x
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

for i in map(f, list1):
    print(i)

# 传入多个序列的例子
def f(x, y):
    return x * y
list1 = [1, 2, 3]
list2 = [4, 5, 6]

for i in map(f, list1, list2):
    print(i, end="\t")

注意:map()函数不改变原有的迭代器,而是返回一个新的迭代器。

11.2 reduce()函数

**reduce()**函数语法:reduce(function, iterable[, initializer])

  • 参数:
    • function:函数
    • iterable:可迭代对象
    • initializer:可选,初始参数
  • 返回值:返回函数计算结果

作用:reduce()函数也是Python内置的一个高阶函数,reduce()函数接收的参数和 map()类似,一个函数 function,一个iterable,但行为和 map()不同,reduce()函数的作用是用传给 reduce 中的function函数先对iterable中的第 1、2 个元素进行运算,得到的结果再与第3个数据用 function 函数运算,最后返回运算的结果。

Python3.x reduce() 已经被移到 functools 模块里,如果我们要使用,需要引入 functools 模块来调用 reduce() 函数:

from functools import reduce

举例:

def add(x, y):
    print("x=%d,y=%d" % (x, y))
    return x + y

sum1 = reduce(add, [1,2,3,4,5])	# 相当于计算1+2+3+4+5,得到15
print(sum1)

# reduce()还可以接收第3个可选参数,作为计算的初始值。如果把初始值设为1000
sum2 = reduce(add, [1,2,3,4,5], 1000) 	# 相当于计算1000+1+2+3+4+5,得到1015
print(sum2)

11.3 filter()函数

filter()函数语法:filter(function, iterable)

  • 参数:
    • function:判断函数
    • iterable:可迭代对象
  • 返回值:迭代器

作用:filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。该函数接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新的迭代器对象中。

举例:

# 判断n是不是偶数
def is_odd(n):
    return n%2 == 0
    
odd = filter(is_odd, [1,2,3,4,5,100])

print(list(odd))

11.4 sorted()函数

sorted函数语法:sorted(iterable, reverse=False)

  • 参数说明:

    • iterable – 可迭代对象。
    • reverse – 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
  • 返回值:返回重新排序的列表。

作用:sorted() 函数对所有可迭代的对象进行排序操作。

面试常问题:sorted()函数与sort()方法的区别?

  1. sort()方法是应用在list对象上的方法,sorted()函数可以对所有可迭代的对象进行排序操作;
  2. list对象的sort()方法是对已经存在的列表进行操作,没有返回值,而sorted()函数返回的是一个新的list对象,而不是在原来的基础上进行的操作。

举例:

list1 = [5, 7, 6, 3, 4, 1, 2]
list2 = sorted(list1)
print(list1)		# 原来的列表顺序不变
print(list2)		# 新生成了一个列表

12. 面向对象

12.1 面向对象的基本概念

  • 面向对象:是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

  • 怎么理解面向对象?

    面向对象是一种编程思想,就是把要解决的问题抽象成一个一个的类,在类里面定义属性和方法(这个过程叫封装),在使用的时候创建类的对象来调用这些属性和方法,解决具体的问题

  • 类class和对象object:

    类class:类是抽象的,类具有事物的描述(属性,静态的特征)和操作(方法,动态的特征);比如学生类,它具备的属性有姓名、年龄、身高、体重等,它具备的方法有学习、吃饭、睡觉、打闹等等。

    对象object:对象是具体的事物,它具有特定事物的描述和操作,类相当于一个模板,对象就是根据这个模板创建出来的具体个体。

    分析以下描述中的类和对象?

    我有一部手机,品牌是华为,型号是Mate50 Pro保时捷,颜色是墨蓝瓷,容量是512G,买它花了我15000块钱,这部手机可以打电话,刷抖音,还能吃鸡。

我还有部手机,品牌是小米,型号是xxx

我:对象;

手机:类;

这一部手机:对象;

  • 属性

    • 品牌:属性;华为:属性的参数(品牌=华为);

    • 型号:属性;Mate50 Pro保时捷:属性的参数;

    • 颜色:属性;墨蓝瓷是属性对应的值

      • 容量:属性;512G是属性对应的值
    • 方法

      • 买:方法;

      • 打电话:方法;

      • 刷抖音:方法;

      • 吃鸡:方法

    类:手机类 ----------> 创建出来的对象是:这是一部手机

    • 属性:品牌、型号、颜色、容量-------------->属性用变量来定义

    • 方法:买东西、打电话、刷抖音、吃鸡----------->方法用函数来定义

    **怎么理解类和对象?**类是一类事物的统称,相当于一个模板;对象是类的一个个体,通过类来创建对象,一个类可以创建多个对象

    根据类创建出来的对象,也具备类一样的属性和方法,可以根据类去创建多个对象。

    如图举例:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

12.2 类的声明与实例化

类是对客观世界中事物得抽象描述,而对象是类实例化后的实体。

12.2.1 类的声明

  • 使用class关键字定义一个类,类名一般用大驼峰法表示,做到“见名思意”。比如DemoClass

  • 类的声明语法:

    class 类名称:
    	类的成员		#包括属性和方法
    

    举例:

    # 定义一个类
    class People:
        #类的属性:用变量来表示
        type="高等生物"
        #类的方法:用函数来表示,但注意方法和函数两个概念有区别
        def eat(self):
            return "用筷子吃饭!"
    

12.2.2 属性和方法

  • 属性和方法统称类的成员。

    1. 属性:类的静态特征叫属性,比如学生类的姓名、性别、年龄等特征就是静态特征,用变量来表示;
    2. 方法:类的动态特征叫方法,比如学生类的吃饭、睡觉、吃鸡等特征就是动态特征,用函数来表示;
  • 举例:

    # 定义一个类
    class Students():
        #类的属性:用变量来表示
        type="学生"
        #类的方法:用函数来表示,但注意方法和函数两个概念有区别
        def study(self):
            return "学习软件测试技术!"
    

12.2.3 类的实例化(创建对象)

  • 根据类创建对象的过程,就是类的实例化;一个类可以创建多个对象;创建的对象带有类的属性和方法。

  • 变量 = 类()

    # 定义一个类
    class Students:
        #类的属性:用变量来表示
        type="学生"
        #类的方法:用函数来表示,但注意方法和函数两个概念有区别
        def study(self):
            return "学习软件测试技术!"
    #实例化对象
    Students()
    print(id(Students()))
    
    #实例化一个对象,并赋值给变量stu1,那么stu1就相当于对象的引用
    stu1=Students()
    print(id(stu1))
    print(id(stu1))
    #实例化另一个对象
    stu2=Students()
    print(id(stu2))
    

12.2.4 属性和方法的调用

  • 因为类的对象有类的所有属性和方法,所以可以通过对象来调用类的属性和方法,调用的语法是:
    • 对象.属性
    • 对象.方法
# 定义一个类
class Students():
    #类的属性:用变量来表示
    type="学生"
    #类的方法:用函数来表示,但注意方法和函数两个概念有区别
    def study(self):
        return "学习软件测试技术!"
#实例化一个对象
stu1=Students()
#调用属性
print(stu1.type)
#调用方法
print(stu1.study())

12.2.5 对self的理解

方法中至少有1个参数self,并且self形参必须在第一个位置,但调用方法时又不需要给self参数传参。self表示对象本身,哪个对象调用方法,self就表示哪个对象。

# 定义一个类
class Students():
    # 类的方法:用函数来表示,但注意方法和函数两个概念有区别
    def change_type(self,type):
        # 如下表示调用对象的type属性进行操作,通过self.type实现调用
        self.type=type
        # 输出self
        print("self:",self)
        return self.type
# 实例化一个对象
stu1=Students()
# 调用change_type()方法,此时change_type()方法在执行的时候self对应的就是stu1这个对象
print(stu1.change_type("小学生"))
# 实例化一个对象
stu2=Students()
# 调用change_type()方法,此时change_type()方法在执行的时候self对应的就是stu2这个对象
print(stu2.change_type("中学生"))
# 输出stu2对象,此时可以看到对应的内存地址与self的值一样
print(stu2)

12.2.6 构造方法和析构方法

魔法方法:在Python中,所有以__双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』,例如类的初始化方法 __init__ 。这里介绍两个魔法方法:__init__()__del__()

构造方法:__init__()

  • __init__是构造方法,在声明类的时候构造方法是可选的(也叫魔法方法——python自带的方法)
  • __init__在类中的作用是:初始化对象
  • 当类中没有显式地声明构造方法的时候,系统会使用一个默认的不带参的构造方法来实现对象的创建,当我们需要对对象初始化的时候,就可以显式地声明一个带参(也可以不带参)的构造方法。

析构方法:__del__()

  • __del__()是析构方法,作用是在一个对象调用完成后,会将对象释放掉,不再使用
# 定义一个类
class Students():
    #类的属性:用变量来表示
    type="学生"
    def __init__(self,name,age):
        #实例的属性
        self.Name=name
        self.Age=age
    
    #类的方法:用函数来表示,但注意方法和函数两个概念有区别
    def study(self):
        return "学习软件测试技术!"
    
    def get_name(self):
        return self.Name

#实例化一个对象
stu1=Students("张三",20)
#调用属性
print(stu1.type)
print(stu1.Name)
print(stu1.Age)
#调用方法
print(stu1.study())
print(stu1.get_name())

12.2.7 函数与方法的区别

函数和方法的区别:

  1. 函数定义在类外面,方法定义在类里面;

  2. 函数通过函数名(实参)来调用,方法一般通过对象.方法名(实参)来执行;

  3. 方法中必须有一个参数(通常命名为self)且这个参数一定在第一个位置,self表示对象本身,方法中的self参数在调用方法时不需要传实参;如果在函数声明时写一个叫self的形参,表示一个叫self的位置参数,在调用函数时必须对它传值。

12.3 类的封装

封装:类是封装属性和方法的载体。类封装的目的是实现访问控制(该暴露的就暴露,不该暴露的就隐藏)。在控制权限上,属性和方法分为public(公共的)和private(私有的)两种,默认是公共的。

简单地讲,封装就是把属性和方法都放在一个类里面,而且还可以通过访问类的权限将属性给区分开,更加安全,不想要释放的功能,直接搞成私有机制。

12.3.1 属性的不同类型

12.3.1.1 类属性

类的属性可以通过类调用,也可以通过对象来调用,对于类的所有对象共同的属性,可以声明成类的属性。

类.属性

对象.属性

# 定义一个类
class Students():
    #类的属性:用变量来表示
    type="学生"
    
    #类的方法:用函数来表示,但注意方法和函数两个概念有区别
    def study(self):
        return "学习软件测试技术!"
    
#实例化一个对象
stu1=Students()

#通过类调用类的属性
print(Students.type)
#通过对象调用类的属性
print(stu1.type)
12.3.1.2 实例属性(普通属性)

实例属性属于每个对象,每个对象的属性值可以不一样,实例属性通过对象来调用,实例属性是我们写得最多的属性。(前面在类中定义的属性(与方法同级的)是类属性,可以通过对象或类进行访问;在构造方法中定义的属性是实例属性,只能通过对象进行访问)

对象.属性

# 定义一个类
class Students():
    #类的属性:用变量来表示
    type="学生"
    #构造方法
    def __init__(self,name,age):
        #实例的属性
        self.Name=name
        self.Age=age
    
#实例化一个对象
stu1=Students("张三",20)

#不能通过类调用实例的属性,因为这个属性属于对象而不属于类
print(Students.Name)
#通过对象调用实例的属性是Ok的
print(stu1.Name)
12.3.1.3 私有属性

属性和方法分为public(公共的)和private(私有的),默认是公共的。如果要声明私有的属性或方法,要在属性名或方法名前加__(两个下划线),私有的方法不能通过类或者对象直接调用。

私有的属性或方法的调用方式:

  1. 通过定义公有的方法来间接地调用私有的属性或者方法;
  2. 通过_类名__属性/方法来调用,在python中没有绝对的私有,所谓的私有实际上是在名称前加了_类名而已。
# 定义一个类
class Students():
    # 类的属性:用变量来表示
    type = "学生"
    def __init__(self, name, age,weight):
        # 实例的属性
        self.Name = name
        self.Age = age
        #私有的属性
        self.__Weight=weight
    #声明一个共有的方法来间接地调用私有方法
    def get_weight(self):
        return self.__Weight
# 实例化一个对象
stu1 = Students("张三", 20, 80)
# 通过声明的共有方法来间接地实现私有属性的调用
print(stu1.get_weight())
# 通过在属性前加_类名来实现对私有方法的调用
print(stu1._Students__Weight)

在实际编程中,虽然Python无法实现绝对的私有,但我们可以通过声明私有属性或方法来告诉别人这个属性或方法是私有的,别人看到后就不会强制去调用,以免出现意料之外的结果。

12.3.1.4 实例属性与类属性重名

当实例属性和类属性重名时,通过类调用的默认是类属性,通过对象调用的默认是实例属性,相当于在对象中实例属性会覆盖类的属性。

# 定义一个类
class Students():
    # 类的属性:用变量来表示
    type = "学生"

    def __init__(self,name,age,weight,type):
        # 实例的属性
        self.Na me = name
        self.Age = age
        #私有的属性
        self.__Weight=weight
        self.type=type

# 实例化一个对象
stu1 = Students("张三", 20, 80,"小学生")

# 重新传值覆盖类的属性的值
print(stu1.type)
print(Students.type)

#例如:
class Test(object):
  val = 100
  def __init__(self):
  self.val = 200

test = Test()
test.val #输出200
Test.val #输出100

12.3.2 方法的不同类型

12.3.2.1 类方法(classmethod装饰器)

使用classmethod装饰器来装饰一个方法,可以把这个方法变成类的方法。类的方法的特点是:

  1. 类方法的第一个参数是类对象参数clscls代表类本身),在调用的时候不需要对cls传实参;
  2. 如果一个方法被标示为类方法,则该方法可被类调用,也可以被类的对象调用。
# 定义一个类
class Students():
    # 类的属性:用变量来表示
    type = "学生"
    #声明一个类方法
    @classmethod
    def get_age(cls):
        return cls.type
# 实例化一个对象
stu1 = Students()

#通过类调用类的方法
print(Students.get_age())
#通过对象调用类的方法
print(stu1.get_age())
12.3.2.2 实例方法(普通方法)

实例的方法只能通过对象调用,这是我们写得最多的方法。

# 定义一个类
class Students():
    # 类的属性:用变量来表示
    type = "学生"

    def __init__(self,name,age,weight,type):
        # 实例的属性
        self.Name = name
        self.Age = age
        #私有的属性
        self.__Weight=weight
        self.type=type
    # 方法:用函数来表示,但注意方法和函数两个概念有区别
    def study(self):
        return "学习软件测试技术!"

    def get_name(self):
        return self.Name

# 实例化一个对象
stu1 = Students("张三", 20, 80,"小学生")

#通过对象调用实例方法
print(stu1.get_name())
12.3.2.3 静态方法(staticmethod装饰器)

使用 staticmethod装饰器来装饰一个方法,可以把它变成一个静态方法,静态方法不需要传形参selfcls(就像一个在类里面的普通函数)。静态方法可以用类来调用,也可以用对象来调用。

# 定义一个类
class Students():
    # 类的属性:用变量来表示
    type = "学生"

    #声明一个静态方法
    @staticmethod
    def get_type():
        return Students.type

# 实例化一个对象
stu1 = Students()

#通过类调用静态方法
print(Students.get_type())
#通过对象调用静态方法
print(stu1.get_type())

几种方法的使用场景:

  • 如果某一个方法需要访问到对象的属性,可以把这个方法封装成一个实例方法;

  • 那如果某一个方法不需要访问对象的属性,但是需要访问到类的属性,这个时候就可以考虑把这个方法封装成一个类方法;

  • 如果要封装的某一个方法,既不需要访问到对象的属性,也不需要访问类的属性,这个时候就可以考虑把这个方法封装成一个静态方法。

12.3.2.4 私有方法(方法前面加两个下划线__

属性和方法分为public(公共的)和private(私有的),默认是公共的。如果要声明私有的属性或方法,要在属性名或方法名前加__(两个下划线),私有的方法不能通过类或者对象直接调用。

私有的属性或方法的调用方式:

  1. 通过定义公有的方法来间接地调用私有的属性或者方法;
  2. 通过_类名__属性/方法来调用,在python中没有绝对的私有,所谓的私有实际上是在名称前加了_类名而已。
# 定义一个类
class Students():
    # 类的属性:用变量来表示
    type = "学生"

    #声明一个私有方法
    def __get_type_2(self):
        return self.type

    #声明一个公有方法来间接地调用私有方法__get_type_2
    def get_type_2(self):
        return self.__get_type_2()

# 实例化一个对象
stu1 = Students()
# 通过声明的公有方法来间接地实现私有方法的调用
print(stu1.get_type_2())
# 通过在属性前加_类名来实现对私有方法的调用
print(stu1._Students__get_type_2())

12.4 类的继承

12.4.1 继承的概念

继承是类与类之间的一种关系,子类继承父类。通过继承可以使得子类能够拥有父类的属性和方法,以达到复用的目的。

  • 子类:需要继承的类
  • 父类:被继承的类

父类与子类示意图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

理解:

  1. 动物类作为狗的父类,所以狗继承了动物类的属性和方法,比如动物具有吃、跑、睡等特征,狗也同样具备这些特征;

  2. 父类和子类的关系是相对的,子类又可以是它的子类的父类,比如:动物类是狗、猫、牛的父类,狗、猫、牛是子类;但狗又是牧羊犬、中华田园犬的父类,牧羊犬、中华田园犬是子类。

继承的特点:

  1. 父类拥有的属性和方法,则子类一定有(私有的属性和方法可以通过子类对象._父类__方法间接调用),子类的对象可以直接调用父类的属性和方法;
  2. 父类拥有的属性和方法,子类可以修改,这就是重写父类的方法;
  3. 父类没有的属性和方法,子类可以新增。

12.4.2 继承的写法

在声明一个类的时候,如果这个类继承了父类,在类名后加括号来指定父类的名称。

# 定义一个父类
class People:
    type="高等生物"
    def get_type(self):
        print("父类的self:", self)
        return self.type
    # 私有方法
    def __study(self):
        return "学习软件测试"
    
#定义一个子类,继承父类People
class Students(People):
    def study(self):
        print("子类的self:", self)
        return "在蓉华学习IT技术"

# 创建子类对象,调用父类的属性和方法
s1=Students()
print(s1.type)
print(s1.get_type())
print(s1._People__study())

# 通过子类对象s1调用子类的study()方法,可以看出打印的self和父类方法中打印的self是同一个对象,也就是s1这个对象
print(s1.study())

12.4.3 子类继承父类的属性和方法

# 定义一个父类
class People:
    type="高等生物"

    def get_type(self):
        return self.type
#定义一个子类,集成父类People
class Students(People):
    pass

#创建Students类的对象
stu1=Students()
#子类对象可以调用父类的属性和方法,因为继承过来了
print(stu1.type)
print(stu1.get_type())

12.4.4 子类新增父类没有的属性和方法

# 定义一个父类
class People:
    type="高等生物"

    def get_type(self):
        return self.type
#定义一个子类,集成父类People
class Students(People):
    #子类的属性
    type2 ="小学生"   
    #子类的方法
    def get_type2(self):
        return self.type2

#创建Students类的对象
stu1=Students()
#子类对象可以调用父类的属性和方法,因为继承过来了
print(stu1.type)
print(stu1.get_type())

12.4.5 子类重写父类的属性和方法

重写父类中的方法的原因:父类中的方法不能满足子类的需要,但是子类又想保留这个方法名。

#把适用于自己的父类方法写成自己的方法
# 定义一个父类
class People:
   type="高等生物"

   def get_type(self):
       return self.type
#定义一个子类,继承父类People
class Students(People):
   type ="小学生"
   def get_type(self):
       return self.type

#创建Students类的对象
stu1=Students()
#子类对象可以调用父类的属性和方法,因为继承过来了
print(stu1.type)
print(stu1.get_type())

12.4.3 super超类的使用

在类的继承中,如果重新定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 超类来实现,比如:

class Animal(object):
    def __init__(self, name):
        self.name = name

    def greet(self):
        print('Hello, I am %s.' % self.name)


class Dog(Animal):
    def greet(self):
    	# 在Python3中如下两种方式二选一,都可以实现调用父类的方法
        super(Dog, self).greet()
        # super().greet()
        print("汪汪汪...")

Dog("小灰").greet()
# 输出:
Hello, I am 小灰.
汪汪汪...

super超类的作用是调用父类的方法,当在子类中调用父类的同名的方法时,需要通过super超类来调用。不同名的方法可以直接通过self来调用。

python3.x中通过super超类调用父类方法有两种写法:

  1. super().父类的方法(参数)
  2. super(子类名称,self).父类的方法(参数)

super超类的一个最常见用法就是在子类中调用父类的构造方法,Python继承情况下写构造方法的3种典型场景:

  1. 如果子类没有显式声明__init__()方法,不管父类有没有显式地声明__init__()方法,都不需要在子类中手动地调用父类的__init__()方法,系统会实现自动调用;
  2. 如果子类显式声明了__init__()方法,但父类没有显式声明__init__()方法,同样不需要在子类中手动地调用父类的__init__()方法,系统会实现自动调用;
  3. 如果子类和父类都显式声明__init__()方法,则必须在子类的__init__()方法中用super手动地调用父类的__init__()方法(调用父类的__init__()方法时不需要传入self参数)。

举例:

#例子1:父类显式地声明了__init__方法,而子类没有显式地声明了__init__方法
#定义一个父类
class People:
    type="高等生物"
    #构造方法
    def __init__(self, name, age):
        # self表示对象本身
        # 定义属性
        self.name = name
        self.age = age
    def get_type(self):
        return self.type
#定义一个子类,集成父类People
class Students(People):
    pass

#创建Students类的对象,需要传入父类构造方法需要的参数,当子类没有显式地声明__init__方法的时候,在创建子类对象之前,系统会自动调用父类的__init__方法来完成父类对象的创建
stu1=Students("张三",30)
print(stu1.name)
print(stu1.age)

#例子2:父类没有显式地声明__init__方法,而子类显式地声明了__init__方法
# 定义一个父类
class People:
    type="高等生物"
    #构造方法
    # def __init__(self, name, age):
    #     # self表示对象本身
    #     # 定义属性
    #     self.name = name
    #     self.age = age
    def get_type(self):
        return self.type
#定义一个子类,集成父类People
class Students(People):
    def __init__(self, sex, school):
        self.sex=sex
        self.school=school

#创建Students类的对象,因为父类没有显式地声明__init__方法,当子类显式地声明__init__方法的时候,在创建子类对象之前,系统会自动调用父类的默认的不带参的__init__方法来完成父类对象的创建
stu1=Students("男","成都职业技术学院")
print(stu1.sex)
print(stu1.school)

#例子3:父类和子类都显式地声明了__init__方法
# 定义一个父类
class People:
    type="高等生物"
    #构造方法
    def __init__(self, name, age):
        # self表示对象本身
        # 定义属性
        self.name = name
        self.age = age
    def get_type(self):
        return self.type
#定义一个子类,集成父类People
class Students(People):
    def __init__(self, sex, school, name, age):
        #当父类和子类中都显式地声明了__init__方法时,需要在子类中用super对象来调用父类的__init__			方法,以完成父类对象的创建,这样才能实现子类继承父类。此时子类的__init__方法的形参包括父类		 __init__方法的形参
        super().__init__(name, age)
        self.sex=sex
        self.school=school
        
#例子4:
#子类重写了父类的方法,还需要调用父类的方法
#定义一个父类
class People:
    type="高等生物"

    def get_type(self,):
        return self.type
#定义一个子类,集成父类People
class Students(People):
    type1 ="小学生"
    def get_type(self):
        return self.type1
    def super_get_type(self): # 重新父类的方法后用作调用父类的方法
        return super().get_type()
#创建Students类的对象
stu1=Students()
#子类重新父类的方法后再调用父类的方法
print(stu1.super_get_type())

12.4.4 多继承

多继承:子类可以拥有多个父类,并且具有所有父类的属性和方法。例如:孩子会继承自己父亲和母亲的特性。

多继承示意图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

语法:

class 子类(父类1,父类2...):
	pass

举例:

class A:
    def __init__(self):
        print("初始化A类对象")

class B(A):
    def __init__(self):
        # super().__init__()
        super(B, self).__init__()
        print("初始化B类对象")

class C(A):
    def __init__(self):
        super(C, self).__init__()
        print("初始化C类对象")

class D(B, C):
    def __init__(self):
        super(D, self).__init__()
        print("初始化D类对象")

b = B()
# 输出:
初始化A类对象
初始化B类对象

d = D()
# 输出:
初始化A类对象
初始化C类对象
初始化B类对象
初始化D类对象

在创建D类的对象时,按正常的理解,程序调用顺序应该是:实例化D以后,通过super调用类B的__init__,然后B再通过super调用类A的__init__,因此程序输出结果应该是a b d,可实际上程序输出结果却是a c b d,这究其原因,就是因为Python中的MRO的问题了。

MRO:就是方法解析顺序(Method Resolution Order),可以通过类调用__mro__方法来查看MRO

print(D.__mro__)
# 输出:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

从以上输出,可以看出,MRO的顺序是:D-->B-->C-->A-->object(object类是所有类的基类,Python解释器在解释执行代码的时候会自动继承object类),所以执行时输出的顺序就是:A C B D。

举例:思考如下代码的输出是多少?

class A:
    def __init__(self):
        self.n = 2

    def add(self, m):
        print("A 类中self为:", self)
        self.n += m

class B(A):
    def __init__(self):
        super().__init__()
        # super(B, self).__init__()
        self.n = 3

    def add(self, m):
        print("B 类中self为:", self)
        super().add(m)
        self.n += 3

class C(A):
    def __init__(self):
        super().__init__()
        # super(C, self).__init__()
        self.n = 4

    def add(self, m):
        print("C 类中self为:", self)
        super().add(m)
        self.n += 4

class D(B, C):
    def __init__(self):
        super().__init__()
        # super(D, self).__init__()
        self.n = 5

    def add(self, m):
        print("D 类中self为:", self)
        super().add(m)
        self.n += 5

d = D()
d.add(2)
print(d.n)
  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Philo Lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值