├── LICENSE ├── Python基础连载 ├── Python网络通信.md ├── Python语言相关.md ├── 函数模块.md ├── 分支结构.md ├── 字符串和常见数据类型.md ├── 循环结构.md ├── 文件和异常.md ├── 综合练习.md ├── 认识Python.md ├── 面向对象基础.md └── 面向对象进阶.md ├── README.md ├── code └── snake.py ├── docs ├── MySQL高性能优化规范建议.md ├── python_ Interview.md ├── 一千行MySQL命令.md ├── 一文学会Python十大排序算法.md └── 获取计算机网络pdf.md └── python100题 └── 获取python100题pdf.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 hellgoddess 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Python基础连载/Python网络通信.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:Python网络通信 2 | 3 | ## Python网络通信-socket编程 4 | 5 | - Python网络编程 - 套接字的概念 / socket模块 / socket函数 / 创建TCP服务器 / 创建TCP客户端 / 创建UDP服务器 / 创建UDP客户端 / SocketServer模块 6 | 7 | ### 套接字 8 | 9 | 套接字这个词对很多不了解网络编程的人来说显得非常晦涩和陌生,其实说得通俗点,套接字就是一套用[C语言](https://zh.wikipedia.org/wiki/C语言)写成的应用程序开发库,主要用于实现进程间通信和网络编程,在网络应用开发中被广泛使用。在Python中也可以基于套接字来使用传输层提供的传输服务,并基于此开发自己的网络应用。实际开发中使用的套接字可以分为三类:流套接字(TCP套接字)、数据报套接字和原始套接字。 10 | 11 | Python的Socket逻辑如下: 12 | 13 | ![image-20210401170658823](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401170658823.png) 14 | 15 | 这张逻辑图,是整个socket编程中的重点的重点,你必须将它理解、吃透,然后刻在脑海里,真正成为自己记忆的一部分!很多人说怎么都学不会socket编程,归根到底的原因就是没有“死记硬背”知识点。 16 | 17 | 在Python中,`import socket`后,用`socket.socket()`方法来创建套接字,语法格式如下: 18 | 19 | ``` 20 | sk = socket.socket([family[, type[, proto]]]) 21 | ``` 22 | 23 | 参数说明: 24 | 25 | - family: 套接字家族,可以使`AF_UNIX`或者`AF_INET`。 26 | - type: 套接字类型,根据是面向连接的还是非连接分为`SOCK_STREAM`或`SOCK_DGRAM`,也就是TCP和UDP的区别。 27 | - protocol: 一般不填默认为0。 28 | 29 | 直接socket.socket(),则全部使用默认值。 30 | 31 | 下面是具体的参数定义: 32 | 33 | | socket类型 | 描述 | 34 | | --------------------- | ------------------------------------------------------------ | 35 | | socket.AF_UNIX | 只能够用于单一的Unix系统进程间通信 | 36 | | socket.AF_INET | IPv4 | 37 | | socket.AF_INET6 | IPv6 | 38 | | socket.SOCK_STREAM | 流式socket , for TCP | 39 | | socket.SOCK_DGRAM | 数据报式socket , for UDP | 40 | | socket.SOCK_RAW | 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 | 41 | | socket.SOCK_SEQPACKET | 可靠的连续数据包服务 | 42 | | 创建TCP Socket: | s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) | 43 | | 创建UDP Socket: | s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) | 44 | 45 | 通过`s = socket.socket()`方法,我们可以获得一个socket对象s,也就是通常说的获取了一个“套接字”,该对象具有一下方法: 46 | 47 | | 方法 | 描述 | 48 | | ------------------------------------ | ------------------------------------------------------------ | 49 | | **服务器端方法** | | 50 | | **s.bind()** | 绑定地址(host,port)到套接字,在AF_INET下,以元组(host,port)的形式表示地址。 | 51 | | **s.listen(backlog)** | 开始监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 | 52 | | **s.accept()** | 被动接受客户端连接,(阻塞式)等待连接的到来,并返回(conn,address)二元元组,其中conn是一个通信对象,可以用来接收和发送数据。address是连接客户端的地址。 | 53 | | **客户端方法** | | 54 | | **s.connect(address)** | 客户端向服务端发起连接。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 | 55 | | s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 | 56 | | **公共方法** | | 57 | | **s.recv(bufsize)** | 接收数据,数据以bytes类型返回,bufsize指定要接收的最大数据量。 | 58 | | **s.send()** | 发送数据。返回值是要发送的字节数量。 | 59 | | **s.sendall()** | 完整发送数据。将数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 | 60 | | s.recvform() | 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收的数据,address是发送数据的套接字地址。 | 61 | | s.sendto(data,address) | 发送UDP数据,将数据data发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 | 62 | | **s.close()** | 关闭套接字,必须执行。 | 63 | | s.getpeername() | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 | 64 | | s.getsockname() | 返回套接字自己的地址。通常是一个元组(ipaddr,port) | 65 | | s.setsockopt(level,optname,value) | 设置给定套接字选项的值。 | 66 | | s.getsockopt(level,optname[.buflen]) | 返回套接字选项的值。 | 67 | | s.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect()) | 68 | | s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 | 69 | | s.fileno() | 返回套接字的文件描述符。 | 70 | | s.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 | 71 | | s.makefile() | 创建一个与该套接字相关连的文件 | 72 | 73 | **注意事项:** 74 | 75 | 1. Python3以后,socket传递的都是**bytes类型**的数据,字符串需要先转换一下,`string.encode()`即可;另一端接收到的bytes数据想转换成字符串,只要`bytes.decode()`一下就可以。 76 | 2. 在正常通信时,`accept()`和`recv()`方法都是阻塞的。所谓的阻塞,指的是程序会暂停在那,一直等到有数据过来。 77 | 78 | 下面我们来通过代码来详细的理解一下: 79 | 80 | 下面是TCP套接字编程: 81 | 82 | 服务端: 83 | 84 | ```python 85 | #!/usr/bin/env python 86 | # -*- coding:utf-8 -*- 87 | 88 | import socket 89 | 90 | ip_port = ('127.0.0.1', 8888) 91 | 92 | sk = socket.socket() # 创建套接字 93 | sk.bind(ip_port) # 绑定服务地址 94 | sk.listen(5) # 监听连接请求 95 | print('启动socket服务,等待客户端连接...') 96 | conn, address = sk.accept() # 等待连接,此处自动阻塞 97 | while True: # 一个死循环,直到客户端发送‘exit’的信号,才关闭连接 98 | client_data = conn.recv(1024).decode() # 接收信息 99 | if client_data == "exit": # 判断是否退出连接 100 | exit("通信结束") 101 | print("来自%s的客户端向你发来信息:%s" % (address, client_data)) 102 | conn.sendall('服务器已经收到你的信息'.encode()) # 回馈信息给客户端 103 | conn.close() # 关闭连接 104 | ``` 105 | 106 | 客户端 107 | 108 | ``` 109 | #!/usr/bin/env python 110 | # -*- coding:utf-8 -*- 111 | 112 | import socket 113 | 114 | ip_port = ('127.0.0.1', 9999) 115 | 116 | s = socket.socket() # 创建套接字 117 | 118 | s.connect(ip_port) # 连接服务器 119 | 120 | while True: # 通过一个死循环不断接收用户输入,并发送给服务器 121 | inp = input("请输入要发送的信息: ").strip() 122 | if not inp: # 防止输入空信息,导致异常退出 123 | continue 124 | s.sendall(inp.encode()) 125 | 126 | if inp == "exit": # 如果输入的是‘exit’,表示断开连接 127 | print("结束通信!") 128 | break 129 | 130 | server_reply = s.recv(1024).decode() 131 | print(server_reply) 132 | 133 | s.close() # 关闭连接 134 | ``` 135 | 136 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 137 | -------------------------------------------------------------------------------- /Python基础连载/Python语言相关.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:Python语言相关 2 | 3 | # Python语言相关 4 | 5 | - 程序和进制 6 | - 变量 - 变量的命名 / 变量的使用 / input函数 / 检查变量类型 / 类型转换 7 | - 数字和字符串 - 整数 / 浮点数 / 复数 / 字符串 / 字符串基本操作 / 字符编码 8 | - 运算符 - 数学运算符 / 赋值运算符 / 比较运算符 / 逻辑运算符 / 身份运算符 / 运算符的优先级 9 | 10 | ### 程序和进制 11 | 12 | 计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。今天我们大多数时候使用的计算机,虽然它们的元器件做工越来越精密,处理能力越来越强大,但究其本质来说仍然属于[“冯·诺依曼结构”](https://zh.wikipedia.org/wiki/冯·诺伊曼结构)的计算机。“冯·诺依曼结构”有两个关键点,一是指出要将存储设备与中央处理器分开,二是提出了将数据以二进制方式编码。二进制是一种“逢二进一”的计数法,跟我们人类使用的“逢十进一”的计数法没有实质性的区别,人类因为有十根手指所以使用了十进制(因为在数数时十根手指用完之后就只能进位了,当然凡事都有例外,玛雅人可能是因为长年光着脚的原因把脚趾头也算上了,于是他们使用了二十进制的计数法,在这种计数法的指导下玛雅人的历法就与我们平常使用的历法不一样,而按照玛雅人的历法,2012年是上一个所谓的“太阳纪”的最后一年,而2013年则是新的“太阳纪”的开始,后来这件事情被以讹传讹的方式误传为”2012年是玛雅人预言的世界末日“这种荒诞的说法,今天我们可以大胆的猜测,玛雅文明之所以发展缓慢估计也与使用了二十进制有关)。对于计算机来说,二进制在物理器件上来说是最容易实现的(高电压表示1,低电压表示0),于是在“冯·诺依曼结构”的计算机都使用了二进制。虽然我们并不需要每个程序员都能够使用二进制的思维方式来工作,但是了解二进制以及它与我们生活中的十进制之间的转换关系,以及二进制与八进制和十六进制的转换关系还是有必要的。如果你对这一点不熟悉,可以自行使用[维基百科](https://zh.wikipedia.org/wiki/二进制)或者[百度百科](https://baike.baidu.com/)科普一下。 13 | 14 | ### 变量与类型 15 | 16 | 在程序设计中,变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间,变量的值可以被读取和修改,这是所有计算和控制的基础。计算机能处理的数据有很多种类型,除了数值之外还可以处理文本、图形、音频、视频等各种各样的数据,那么不同的数据就需要定义不同的存储类型。 17 | 18 | > **程序就是用来处理数据的,而变量就是用来存储数据的** 19 | 20 | ## 变量定义 21 | 22 | - 在 Python 中,每个变量 **在使用前都必须赋值**,变量 **赋值以后** 该变量 **才会被创建** 23 | - 等号(=)用来给变量赋值 24 | - `=` 左边是一个变量名 25 | - `=` 右边是存储在变量中的值 26 | 27 | ``` 28 | 变量名 = 值 29 | ``` 30 | 31 | > 变量定义之后,后续就可以直接使用了 32 | 33 | ### 1) 变量演练1-- IDLE —— Python 34 | 35 | ``` 36 | # 定义 qq_number 的变量用来保存 qq 号码 37 | In [1]: qq_number = "1234567" 38 | 39 | # 输出 qq_number 中保存的内容 40 | In [2]: qq_number 41 | Out[2]: '1234567' 42 | 43 | # 定义 qq_password 的变量用来保存 qq 密码 44 | In [3]: qq_password = "123" 45 | 46 | # 输出 qq_password 中保存的内容 47 | In [4]: qq_password 48 | Out[4]: '123' 49 | ``` 50 | 51 | > 使用交互式方式,如果要查看变量内容,直接输入变量名即可,不需要使用 `print` 函数 52 | 53 | ### 2) 变量演练 2 —— PyCharm 54 | 55 | ``` 56 | # 定义 qq 号码变量 57 | qq_number = "1234567" 58 | 59 | # 定义 qq 密码变量 60 | qq_password = "123" 61 | 62 | # 在程序中,如果要输出变量的内容,需要使用 print 函数 63 | print(qq_number) 64 | print(qq_password) 65 | ``` 66 | 67 | > 使用解释器执行,如果要输出变量的内容,必须要要使用 `print` 函数 68 | 69 | ### 3) 变量演练 3 —— 超市买苹果 70 | 71 | > - 可以用 **其他变量的计算结果** 来定义变量 72 | > - 变量定义之后,后续就可以直接使用了 73 | 74 | **需求** 75 | 76 | - 苹果的价格是 **8.5 元/斤** 77 | - 买了 **7.5 斤** 苹果 78 | - 计算付款金额 79 | 80 | ``` 81 | # 定义苹果价格变量 82 | price = 8.5 83 | 84 | # 定义购买重量 85 | weight = 7.5 86 | 87 | # 计算金额 88 | money = price * weight 89 | 90 | print(money) 91 | ``` 92 | 93 | #### 思考题 94 | 95 | - 如果 **只要买苹果,就返 5 块钱** 96 | - 请重新计算购买金额 97 | 98 | ``` 99 | # 定义苹果价格变量 100 | price = 8.5 101 | 102 | # 定义购买重量 103 | weight = 7.5 104 | 105 | # 计算金额 106 | money = price * weight 107 | 108 | # 只要买苹果就返 5 元 109 | money = money - 5 110 | print(money) 111 | ``` 112 | 113 | **提问** 114 | 115 | - 上述代码中,一共定义有几个变量? 116 | 117 | - 三个:`price`/`weight`/`money` 118 | 119 | - ``` 120 | money = money - 5 121 | ``` 122 | 123 | 124 | 125 | 是在定义新的变量还是在使用变量? 126 | 127 | - 直接使用之前已经定义的变量 128 | - 变量名 只有在 **第一次出现** 才是 **定义变量** 129 | - 变量名 再次出现,不是定义变量,而是直接使用之前定义过的变量 130 | 131 | - 在程序开发中,可以修改之前定义变量中保存的值吗? 132 | 133 | - 可以 134 | - 变量中存储的值,就是可以 **变** 的 135 | 136 | ## 02. 变量的类型 137 | 138 | - 在内存中创建一个变量,会包括: 139 | 1. 变量的名称 140 | 2. 变量保存的数据 141 | 3. 变量存储数据的类型 142 | 4. 变量的地址(标示) 143 | 144 | ### 2.1 变量类型的演练 —— 个人信息 145 | 146 | **需求** 147 | 148 | - 定义变量保存小明的个人信息 149 | - 姓名:**小明** 150 | - 年龄:**18** 岁 151 | - 性别:**是**男生 152 | - 身高:**1.75** 米 153 | - 体重:**75.0** 公斤 154 | 155 | > 利用 **单步调试** 确认变量中保存数据的类型 156 | 157 | **提问** 158 | 159 | 1. 在演练中,一共有几种数据类型? 160 | 161 | - 4 种 162 | - `str` —— 字符串 163 | - `bool` —— 布尔(真假) 164 | - `int` —— 整数 165 | - `float` —— 浮点数(小数) 166 | 167 | 2. 在 168 | 169 | 170 | 171 | ``` 172 | Python 173 | ``` 174 | 175 | 176 | 177 | 中定义变量时需要指定类型吗? 178 | 179 | - 不需要 180 | - `Python` 可以根据 `=` 等号右侧的值,自动推导出变量中存储数据的类型 181 | 182 | ### 2.2 变量的类型 183 | 184 | - 在 `Python` 中定义变量是 **不需要指定类型**(在其他很多高级语言中都需要) 185 | 186 | - 数据类型可以分为 **数字型** 和 **非数字型** 187 | 188 | - 数字型 189 | 190 | - 整型 (`int`) 191 | 192 | - 浮点型(`float`) 193 | 194 | - 布尔型( 195 | 196 | ``` 197 | bool 198 | ``` 199 | 200 | ) 201 | 202 | - 真 `True` `非 0 数` —— **非零即真** 203 | - 假 `False` `0` 204 | 205 | - 复数型 ( 206 | 207 | ``` 208 | complex 209 | ``` 210 | 211 | ) 212 | 213 | - 主要用于科学计算,例如:平面场问题、波动问题、电感电容等问题 214 | 215 | - 非数字型 216 | 217 | - 字符串 218 | - 列表 219 | - 元组 220 | - 字典 221 | 222 | > 提示:在 Python 2.x 中,**整数** 根据保存数值的长度还分为: 223 | > 224 | > - `int`(整数) 225 | > - `long`(长整数) 226 | 227 | - 使用 `type` 函数可以查看一个变量的类型 228 | 229 | ``` 230 | In [1]: type(name) 231 | ``` 232 | 233 | ### 2.3 不同类型变量之间的计算 234 | 235 | #### 1) **数字型变量** 之间可以直接计算 236 | 237 | - 在 Python 中,两个数字型变量是可以直接进行 算数运算的 238 | 239 | - 如果变量是 240 | 241 | 242 | 243 | ``` 244 | bool 245 | ``` 246 | 247 | 248 | 249 | 型,在计算时 250 | 251 | - `True` 对应的数字是 `1` 252 | - `False` 对应的数字是 `0` 253 | 254 | **演练步骤** 255 | 256 | 1. 定义整数 `i = 10` 257 | 2. 定义浮点数 `f = 10.5` 258 | 3. 定义布尔型 `b = True` 259 | 260 | - 在 iPython 中,使用上述三个变量相互进行算术运算 261 | 262 | #### 2) **字符串变量** 之间使用 `+` 拼接字符串 263 | 264 | - 在 Python 中,字符串之间可以使用 `+` 拼接生成新的字符串 265 | 266 | ``` 267 | In [1]: first_name = "三" 268 | 269 | In [2]: last_name = "张" 270 | 271 | In [3]: first_name + last_name 272 | Out[3]: '三张' 273 | ``` 274 | 275 | #### 3) **字符串变量** 可以和 **整数** 使用 `*` 重复拼接相同的字符串 276 | 277 | ``` 278 | In [1]: "-" * 50 279 | Out[1]: '--------------------------------------------------' 280 | ``` 281 | 282 | #### 4) **数字型变量** 和 **字符串** 之间 **不能进行其他计算** 283 | 284 | ``` 285 | In [1]: first_name = "zhang" 286 | 287 | In [2]: x = 10 288 | 289 | In [3]: x + first_name 290 | --------------------------------------------------------------------------- 291 | TypeError: unsupported operand type(s) for +: 'int' and 'str' 292 | 类型错误:`+` 不支持的操作类型:`int` 和 `str` 293 | ``` 294 | 295 | ### 2.4 变量的输入 296 | 297 | - 所谓 **输入**,就是 **用代码** **获取** 用户通过 **键盘** 输入的信息 298 | - 例如:去银行取钱,在 ATM 上输入密码 299 | - 在 Python 中,如果要获取用户在 **键盘** 上的输入信息,需要使用到 `input` 函数 300 | 301 | #### 1) 关于函数 302 | 303 | - 一个 **提前准备好的功能**(别人或者自己写的代码),**可以直接使用**,而 **不用关心内部的细节** 304 | - 目前已经学习过的函数 305 | 306 | | 函数 | 说明 | 307 | | -------- | ----------------- | 308 | | print(x) | 将 x 输出到控制台 | 309 | | type(x) | 查看 x 的变量类型 | 310 | 311 | #### 2) input 函数实现键盘输入 312 | 313 | - 在 Python 中可以使用 `input` 函数从键盘等待用户的输入 314 | - 用户输入的 **任何内容** Python 都认为是一个 **字符串** 315 | - 语法如下: 316 | 317 | ``` 318 | 字符串变量 = input("提示信息:") 319 | ``` 320 | 321 | #### 3) 类型转换函数 322 | 323 | | 函数 | 说明 | 324 | | -------- | --------------------- | 325 | | int(x) | 将 x 转换为一个整数 | 326 | | float(x) | 将 x 转换到一个浮点数 | 327 | 328 | #### 4) 变量输入演练 —— 超市买苹果增强版 329 | 330 | **需求** 331 | 332 | - **收银员输入** 苹果的价格,单位:**元/斤** 333 | - **收银员输入** 用户购买苹果的重量,单位:**斤** 334 | - 计算并且 **输出** 付款金额 335 | 336 | ##### 演练方式 1 337 | 338 | ``` 339 | # 1. 输入苹果单价 340 | price_str = input("请输入苹果价格:") 341 | 342 | # 2. 要求苹果重量 343 | weight_str = input("请输入苹果重量:") 344 | 345 | # 3. 计算金额 346 | # 1> 将苹果单价转换成小数 347 | price = float(price_str) 348 | 349 | # 2> 将苹果重量转换成小数 350 | weight = float(weight_str) 351 | 352 | # 3> 计算付款金额 353 | money = price * weight 354 | 355 | print(money) 356 | ``` 357 | 358 | **提问** 359 | 360 | 1. 演练中,针对价格 361 | 362 | 定义了几个变量? 363 | 364 | - **两个** 365 | - `price_str` 记录用户输入的价格字符串 366 | - `price` 记录转换后的价格数值 367 | 368 | 2. **思考** —— 如果开发中,需要用户通过控制台 输入 **很多个 数字**,针对每一个数字都要定义两个变量,**方便吗**? 369 | 370 | ##### 演练方式 2 —— 买苹果改进版 371 | 372 | 1. **定义** 一个 **浮点变量** 接收用户输入的同时,就使用 `float` 函数进行转换 373 | 374 | ``` 375 | price = float(input("请输入价格:")) 376 | ``` 377 | 378 | - 改进后的好处: 379 | 380 | 1. 节约空间,只需要为一个变量分配空间 381 | 2. 起名字方便,不需要为中间变量起名字 382 | 383 | - 改进后的“缺点”: 384 | 385 | 1. 初学者需要知道,两个函数能够嵌套使用,稍微有一些难度 386 | 387 | **提示** 388 | 389 | - 如果输入的不是一个数字,程序执行时会出错,有关数据转换的高级话题,后续会讲! 390 | 391 | ### 2.5 变量的格式化输出 392 | 393 | > 苹果单价 `9.00` 元/斤,购买了 `5.00` 斤,需要支付 `45.00` 元 394 | 395 | - 在 Python 中可以使用 `print` 函数将信息输出到控制台 396 | 397 | - 如果希望输出文字信息的同时,**一起输出** **数据**,就需要使用到 **格式化操作符** 398 | 399 | - ``` 400 | % 401 | ``` 402 | 403 | 被称为格式化操作符,专门用于处理字符串中的格式 404 | 405 | - 包含 `%` 的字符串,被称为 **格式化字符串** 406 | - `%` 和不同的 **字符** 连用,**不同类型的数据** 需要使用 **不同的格式化字符** 407 | 408 | | 格式化字符 | 含义 | 409 | | ---------- | ------------------------------------------------------------ | 410 | | %s | 字符串 | 411 | | %d | 有符号十进制整数,`%06d` 表示输出的整数显示位数,不足的地方使用 `0` 补全 | 412 | | %f | 浮点数,`%.2f` 表示小数点后只显示两位 | 413 | | %% | 输出 `%` | 414 | 415 | - 语法格式如下: 416 | 417 | ``` 418 | print("格式化字符串" % 变量1) 419 | 420 | print("格式化字符串" % (变量1, 变量2...)) 421 | ``` 422 | 423 | **我们在这里将之前学到的东西再综合实战一下** 424 | 425 | 下面通过几个例子来说明变量的类型和变量使用。 426 | 427 | ``` 428 | """ 429 | 使用变量保存数据并进行加减乘除运算 430 | 431 | Author: 海森堡 432 | """ 433 | a = 321 434 | b = 12 435 | print(a + b) # 333 436 | print(a - b) # 309 437 | print(a * b) # 3852 438 | print(a / b) # 26.75 439 | ``` 440 | 441 | 在Python中可以使用`type`函数对变量的类型进行检查。程序设计中函数的概念跟数学上函数的概念是一致的,数学上的函数相信大家并不陌生,它包括了函数名、自变量和因变量。如果暂时不理解这个概念也不要紧,我们会在后续的章节中专门讲解函数的定义和使用。 442 | 443 | ``` 444 | """ 445 | 使用type()检查变量的类型 446 | 447 | Author: 海森堡 448 | """ 449 | a = 100 450 | b = 12.345 451 | c = 1 + 5j 452 | d = 'hello, world' 453 | e = True 454 | print(type(a)) # 455 | print(type(b)) # 456 | print(type(c)) # 457 | print(type(d)) # 458 | print(type(e)) # 459 | ``` 460 | 461 | 可以使用Python中内置的函数对变量类型进行转换。 462 | 463 | - `int()`:将一个数值或字符串转换成整数,可以指定进制。 464 | - `float()`:将一个字符串转换成浮点数。 465 | - `str()`:将指定的对象转换成字符串形式,可以指定编码。 466 | - `chr()`:将整数转换成该编码对应的字符串(一个字符)。 467 | - `ord()`:将字符串(一个字符)转换成对应的编码(整数)。 468 | 469 | 下面的代码通过键盘输入两个整数来实现对两个整数的算术运算。 470 | 471 | ``` 472 | """ 473 | 使用input()函数获取键盘输入(字符串) 474 | 使用int()函数将输入的字符串转换成整数 475 | 使用print()函数输出带占位符的字符串 476 | 477 | Author: 海森堡 478 | """ 479 | a = int(input('a = ')) 480 | b = int(input('b = ')) 481 | print('%d + %d = %d' % (a, b, a + b)) 482 | print('%d - %d = %d' % (a, b, a - b)) 483 | print('%d * %d = %d' % (a, b, a * b)) 484 | print('%d / %d = %f' % (a, b, a / b)) 485 | print('%d // %d = %d' % (a, b, a // b)) 486 | print('%d %% %d = %d' % (a, b, a % b)) 487 | print('%d ** %d = %d' % (a, b, a ** b)) 488 | ``` 489 | 490 | > **说明**:上面的print函数中输出的字符串使用了占位符语法,其中`%d`是整数的占位符,`%f`是小数的占位符,`%%`表示百分号(因为百分号代表了占位符,所以带占位符的字符串中要表示百分号必须写成`%%`),字符串之后的`%`后面跟的变量值会替换掉占位符然后输出到终端中,运行上面的程序,看看程序执行结果就明白啦。 491 | 492 | > 变量的命名规则 493 | 494 | > **命名规则** 可以被视为一种 **惯例**,并无绝对与强制 目的是为了 **增加代码的识别和可读性** 495 | 496 | #### 标识符和关键字 497 | 498 | > 标示符就是程序员定义的 **变量名**、**函数名** 499 | 500 | - 标示符可以由 **字母**、**下划线** 和 **数字** 组成 501 | - **不能以数字开头** 502 | - **不能与关键字重名** 503 | 504 | ### 关键字 505 | 506 | - **关键字** 就是在 `Python` 内部已经使用的标识符 507 | - **关键字** 具有特殊的功能和含义 508 | - 开发者 **不允许定义和关键字相同的名字的标示符** 509 | 510 | ## 变量的命名规则 511 | 512 | > **命名规则** 可以被视为一种 **惯例**,并无绝对与强制 目的是为了 **增加代码的识别和可读性** 513 | 514 | **注意** `Python` 中的 **标识符** 是 **区分大小写的** 515 | 516 | [![002_标识符区分大小写](https://github.com/hyh1750522171/bigData/raw/master/He%20Yihao/python%E5%9F%BA%E7%A1%80/images/009/002_%E6%A0%87%E8%AF%86%E7%AC%A6%E5%8C%BA%E5%88%86%E5%A4%A7%E5%B0%8F%E5%86%99.jpg)](https://github.com/hyh1750522171/bigData/blob/master/He Yihao/python基础/images/009/002_标识符区分大小写.jpg) 517 | 518 | 1. 在定义变量时,为了保证代码格式,`=` 的左右应该各保留一个空格 519 | 520 | 2. Python中,如果变量名需要由 二个 或 多个单词 组成时,可以按照以下方式命名 521 | 522 | 1. 每个单词都使用小写字母 523 | 2. 单词与单词之间使用 **`_`下划线** 连接 524 | 525 | - 例如:`first_name`、`last_name`、`qq_number`、`qq_password` 526 | 527 | ### 驼峰命名法 528 | 529 | - 当 **变量名** 是由二个或多个单词组成时,还可以利用驼峰命名法来命名 530 | - 小驼峰式命名法 531 | - 第一个单词以小写字母开始,后续单词的首字母大写 532 | - 例如:`firstName`、`lastName` 533 | - 大驼峰式命名法 534 | - 每一个单词的首字母都采用大写字母 535 | - 例如:`FirstName`、`LastName`、`CamelCase` 536 | 537 | 在Python中使用变量时,需要遵守一些规则和指南。违反这些规则将引发错误,而指南旨在让你编写的代码更容易阅读和理解。请务必牢记下述有关变量的规则。 538 | 539 | + 变量名只能包含字母、数字和下划线。变量名可以字母或下划线打头,但不能以数字打头,例如,可将变量命名为message_1,但不能将其命名为1_message。 540 | 541 | + 变量名不能包含空格,但可使用下划线来分隔其中的单词。例如,变量名greeting_message可行,但变量名greetingmessage会引发错误。 542 | 543 | + 不要将Python关键字和函数名用作变量名,即不要使用Python保留用于特殊用途的单词,如print (请参见附录A.4)。 544 | 545 | + 变量名应既简短又具有描述性。例如,name比n好,student_name比s_n好,name_length比length_of_persons_name好。 546 | 547 | + 慎用小写字母l和大写字母O,因为它们可能被人错看成数字1和0。 548 | 549 | 要创建良好的变量名,需要经过一定的实践,在程序复杂而有趣时尤其如此。随着你编写的程序越来越多,并开始阅读别人编写的代码,将越来越善于创建有意义的变量名。 550 | 551 | ### 运算符 552 | 553 | Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符,运算符的优先级指的是多个运算符同时出现时,先做什么运算然后再做什么运算。除了我们之前已经用过的赋值运算符和算术运算符,我们稍后会陆续讲到其他运算符的使用。 554 | 555 | - 算数运算符 556 | - 比较(关系)运算符 557 | - 逻辑运算符 558 | - 赋值运算符 559 | - 运算符的优先级 560 | 561 | ## 算数运算符 562 | 563 | - 是完成基本的算术运算使用的符号,用来处理四则运算 564 | 565 | | 运算符 | 描述 | 实例 | 566 | | ------ | ------ | ------------------------------------------ | 567 | | + | 加 | 10 + 20 = 30 | 568 | | - | 减 | 10 - 20 = -10 | 569 | | * | 乘 | 10 * 20 = 200 | 570 | | / | 除 | 10 / 20 = 0.5 | 571 | | // | 取整除 | 返回除法的整数部分(商) 9 // 2 输出结果 4 | 572 | | % | 取余数 | 返回除法的余数 9 % 2 = 1 | 573 | | ** | 幂 | 又称次方、乘方,2 ** 3 = 8 | 574 | 575 | - 在 Python 中 `*` 运算符还可以用于字符串,计算结果就是字符串重复指定次数的结果 576 | 577 | ``` 578 | In [1]: "-" * 50 579 | Out[1]: '----------------------------------------' 580 | ``` 581 | 582 | ## 比较(关系)运算符 583 | 584 | | 运算符 | 描述 | 585 | | ------ | ------------------------------------------------------------ | 586 | | == | 检查两个操作数的值是否 **相等**,如果是,则条件成立,返回 True | 587 | | != | 检查两个操作数的值是否 **不相等**,如果是,则条件成立,返回 True | 588 | | > | 检查左操作数的值是否 **大于** 右操作数的值,如果是,则条件成立,返回 True | 589 | | < | 检查左操作数的值是否 **小于** 右操作数的值,如果是,则条件成立,返回 True | 590 | | >= | 检查左操作数的值是否 **大于或等于** 右操作数的值,如果是,则条件成立,返回 True | 591 | | <= | 检查左操作数的值是否 **小于或等于** 右操作数的值,如果是,则条件成立,返回 True | 592 | 593 | > Python 2.x 中判断 **不等于** 还可以使用 `<>` 运算符 594 | > 595 | > `!=` 在 Python 2.x 中同样可以用来判断 **不等于** 596 | 597 | ## 逻辑运算符 598 | 599 | | 运算符 | 逻辑表达式 | 描述 | 600 | | ------ | ---------- | ------------------------------------------------------------ | 601 | | and | x and y | 只有 x 和 y 的值都为 True,才会返回 True 否则只要 x 或者 y 有一个值为 False,就返回 False | 602 | | or | x or y | 只要 x 或者 y 有一个值为 True,就返回 True 只有 x 和 y 的值都为 False,才会返回 False | 603 | | not | not x | 如果 x 为 True,返回 False 如果 x 为 False,返回 True | 604 | 605 | ## 赋值运算符 606 | 607 | - 在 Python 中,使用 `=` 可以给变量赋值 608 | - 在算术运算时,为了简化代码的编写,`Python` 还提供了一系列的 与 **算术运算符** 对应的 **赋值运算符** 609 | - 注意:**赋值运算符中间不能使用空格** 610 | 611 | | 运算符 | 描述 | 实例 | 612 | | ------ | -------------------------- | ------------------------------------- | 613 | | = | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c | 614 | | += | 加法赋值运算符 | c += a 等效于 c = c + a | 615 | | -= | 减法赋值运算符 | c -= a 等效于 c = c - a | 616 | | *= | 乘法赋值运算符 | c *= a 等效于 c = c * a | 617 | | /= | 除法赋值运算符 | c /= a 等效于 c = c / a | 618 | | //= | 取整除赋值运算符 | c //= a 等效于 c = c // a | 619 | | %= | 取 **模** (余数)赋值运算符 | c %= a 等效于 c = c % a | 620 | | **= | 幂赋值运算符 | c **= a 等效于 c = c ** a | 621 | 622 | ## 运算符的优先级 623 | 624 | - 以下表格的算数优先级由高到最低顺序排列 625 | 626 | | 运算符 | 描述 | 627 | | ------------------------ | ---------------------- | 628 | | ** | 幂 (最高优先级) | 629 | | * / % // | 乘、除、取余数、取整除 | 630 | | + - | 加法、减法 | 631 | | <= < > >= | 比较运算符 | 632 | | == != | 等于运算符 | 633 | | = %= /= //= -= += *= **= | 赋值运算符 | 634 | | not or and | 逻辑运算符 | 635 | 636 | 637 | 638 | ### 实战 639 | 640 | 641 | 642 | ```python 643 | """ 644 | 比较运算符和逻辑运算符的使用 645 | 646 | Author: 海森堡 647 | """ 648 | flag0 = 1 == 1 649 | flag1 = 10 > 2 650 | flag2 = 20 < 1 651 | flag3 = flag1 and flag2 652 | flag4 = flag1 or flag2 653 | flag5 = not (1 != 2) 654 | print('flag0 =', flag0) # flag0 = True 655 | print('flag1 =', flag1) # flag1 = True 656 | print('flag2 =', flag2) # flag2 = False 657 | print('flag3 =', flag3) # flag3 = False 658 | print('flag4 =', flag4) # flag4 = True 659 | print('flag5 =', flag5) # flag5 = False 660 | ``` 661 | 662 | ![image-20210331182413839](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331182413839.png) 663 | 664 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 665 | 666 | -------------------------------------------------------------------------------- /Python基础连载/函数模块.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:函数模块 2 | 3 | - 函数的快速体验 4 | - 定义函数 - def语句 / 函数名 / 参数列表 / return语句 / 调用自定义函数 5 | - 调用函数 - Python内置函数 / 导入模块和函数 6 | - 函数的参数 - 默认参数 / 可变参数 / 关键字参数 / 命名关键字参数 7 | - 函数的返回值 - 没有返回值 / 返回单个值 / 返回多个值 8 | - 用模块管理函数 - 模块的概念 / 用自定义模块管理函数 / 命名冲突的时候会怎样(同一个模块和不同的模块) 9 | 10 | ## 函数的快速体验 11 | 12 | ### 快速体验 13 | 14 | - 所谓**函数**,就是把 **具有独立功能的代码块** 组织为一个小模块,在需要的时候 **调用** 15 | - 函数的使用包含两个步骤: 16 | 1. 定义函数 —— **封装** 独立的功能 17 | 2. 调用函数 —— 享受 **封装** 的成果 18 | - **函数的作用**,在开发程序时,使用函数可以提高编写的效率以及代码的 **重用** 19 | 20 | **演练步骤** 21 | 22 | 1. 新建 `04_函数` 项目 23 | 2. 复制之前完成的 **乘法表** 文件 24 | 3. 修改文件,增加函数定义 `multiple_table():` 25 | 4. 新建另外一个文件,使用 `import` 导入并且调用函数 26 | 27 | ## 函数基本使用 28 | 29 | ### 函数的定义 30 | 31 | 定义函数的格式如下: 32 | 33 | ``` 34 | def 函数名(): 35 | 36 | 函数封装的代码 37 | …… 38 | ``` 39 | 40 | 1. `def` 是英文 `define` 的缩写 41 | 2. **函数名称** 应该能够表达 **函数封装代码** 的功能,方便后续的调用 42 | 3. **函数名称** 的命名应该 **符合** **标识符的命名规则** 43 | - 可以由 **字母**、**下划线** 和 **数字** 组成 44 | - **不能以数字开头** 45 | - **不能与关键字重名** 46 | 47 | ### 函数调用 48 | 49 | 调用函数很简单的,通过 `函数名()` 即可完成对函数的调用 50 | 51 | ### 第一个函数演练 52 | 53 | **需求** 54 | 55 | - 1. 编写一个打招呼 `say_hello` 的函数,封装三行打招呼的代码 56 | - 1. 在函数下方调用打招呼的代码 57 | 58 | ``` 59 | name = "小明" 60 | 61 | 62 | # 解释器知道这里定义了一个函数 63 | def say_hello(): 64 | print("hello 1") 65 | print("hello 2") 66 | print("hello 3") 67 | 68 | print(name) 69 | # 只有在调用函数时,之前定义的函数才会被执行 70 | # 函数执行完成之后,会重新回到之前的程序中,继续执行后续的代码 71 | say_hello() 72 | 73 | print(name) 74 | ``` 75 | 76 | > 用 **单步执行 F8 和 F7** 观察以下代码的执行过程 77 | 78 | - 定义好函数之后,只表示这个函数封装了一段代码而已 79 | - 如果不主动调用函数,函数是不会主动执行的 80 | 81 | #### 思考 82 | 83 | - 能否将 **函数调用** 放在 **函数定义** 的上方? 84 | - 不能! 85 | - 因为在 **使用函数名** 调用函数之前,必须要保证 `Python` 已经知道函数的存在 86 | - 否则控制台会提示 `NameError: name 'say_hello' is not defined` (**名称错误:say_hello 这个名字没有被定义**) 87 | 88 | ### PyCharm 的调试工具 89 | 90 | - **F8 Step Over** 可以单步执行代码,会把函数调用看作是一行代码直接执行 91 | - **F7 Step Into** 可以单步执行代码,如果是函数,会进入函数内部 92 | 93 | ### 函数的文档注释 94 | 95 | - 在开发中,如果希望给函数添加注释,应该在 **定义函数** 的下方,使用 **连续的三对引号** 96 | - 在 **连续的三对引号** 之间编写对函数的说明文字 97 | - 在 **函数调用** 位置,使用快捷键 `CTRL + Q` 可以查看函数的说明信息 98 | 99 | > 注意:因为 **函数体相对比较独立**,**函数定义的上方**,应该和其他代码(包括注释)保留 **两个空行** 100 | 101 | ## 函数的参数 102 | 103 | **演练需求** 104 | 105 | 1. 开发一个 `sum_2_num` 的函数 106 | 2. 函数能够实现 **两个数字的求和** 功能 107 | 108 | 演练代码如下: 109 | 110 | ``` 111 | def sum_2_num(): 112 | 113 | num1 = 10 114 | num2 = 20 115 | result = num1 + num2 116 | 117 | print("%d + %d = %d" % (num1, num2, result)) 118 | 119 | sum_2_num() 120 | ``` 121 | 122 | **思考一下存在什么问题** 123 | 124 | > 函数只能处理 **固定数值** 的相加 125 | 126 | **如何解决?** 127 | 128 | - 如果能够把需要计算的数字,在调用函数时,传递到函数内部就好了! 129 | 130 | ### 函数参数的使用 131 | 132 | - 在函数名的后面的小括号内部填写 **参数** 133 | - 多个参数之间使用 `,` 分隔 134 | 135 | ``` 136 | def sum_2_num(num1, num2): 137 | 138 | result = num1 + num2 139 | 140 | print("%d + %d = %d" % (num1, num2, result)) 141 | 142 | sum_2_num(50, 20) 143 | ``` 144 | 145 | ### 参数的作用 146 | 147 | - **函数**,把 **具有独立功能的代码块** 组织为一个小模块,在需要的时候 **调用** 148 | - **函数的参数**,增加函数的 **通用性**,针对 **相同的数据处理逻辑**,能够 **适应更多的数据** 149 | 1. 在函数 **内部**,把参数当做 **变量** 使用,进行需要的数据处理 150 | 2. 函数调用时,按照函数定义的**参数顺序**,把 **希望在函数内部处理的数据**,**通过参数** 传递 151 | 152 | ### 形参和实参 153 | 154 | - **形参**:**定义** 函数时,小括号中的参数,是用来接收参数用的,在函数内部 **作为变量使用** 155 | - **实参**:**调用** 函数时,小括号中的参数,是用来把数据传递到 **函数内部** 用的 156 | 157 | ## 函数的返回值 158 | 159 | - 在程序开发中,有时候,会希望 **一个函数执行结束后,告诉调用者一个结果**,以便调用者针对具体的结果做后续的处理 160 | - **返回值** 是函数 **完成工作**后,**最后** 给调用者的 **一个结果** 161 | - 在函数中使用 `return` 关键字可以返回结果 162 | - 调用函数一方,可以 **使用变量** 来 **接收** 函数的返回结果 163 | 164 | > 注意:`return` 表示返回,后续的代码都不会被执行 165 | 166 | ``` 167 | def sum_2_num(num1, num2): 168 | """对两个数字的求和""" 169 | 170 | return num1 + num2 171 | 172 | # 调用函数,并使用 result 变量接收计算结果 173 | result = sum_2_num(10, 20) 174 | 175 | print("计算结果是 %d" % result) 176 | ``` 177 | 178 | ## 函数的嵌套调用 179 | 180 | - 一个函数里面 **又调用** 了 **另外一个函数**,这就是 **函数嵌套调用** 181 | 182 | + 如果函数 `test2` 中,调用了另外一个函数 `test1` 183 | + 那么执行到调用 `test1` 函数时,会先把函数 `test1` 中的任务都执行完 184 | + 才会回到 `test2` 中调用函数 `test1` 的位置,继续执行后续的代码 185 | 186 | ``` 187 | def test1(): 188 | 189 | print("*" * 50) 190 | print("test 1") 191 | print("*" * 50) 192 | 193 | 194 | def test2(): 195 | 196 | print("-" * 50) 197 | print("test 2") 198 | 199 | test1() 200 | 201 | print("-" * 50) 202 | 203 | test2() 204 | ``` 205 | 206 | ### 函数嵌套的演练 —— 打印分隔线 207 | 208 | > 体会一下工作中 **需求是多变** 的 209 | 210 | **需求 1** 211 | 212 | - 定义一个 `print_line` 函数能够打印 `*` 组成的 **一条分隔线** 213 | 214 | ``` 215 | def print_line(char): 216 | 217 | print("*" * 50) 218 | ``` 219 | 220 | **需求 2** 221 | 222 | - 定义一个函数能够打印 **由任意字符组成** 的分隔线 223 | 224 | ``` 225 | def print_line(char): 226 | 227 | print(char * 50) 228 | 229 | ``` 230 | 231 | **需求 3** 232 | 233 | - 定义一个函数能够打印 **任意重复次数** 的分隔线 234 | 235 | ``` 236 | def print_line(char, times): 237 | 238 | print(char * times) 239 | ``` 240 | 241 | **需求 4** 242 | 243 | - 定义一个函数能够打印 **5 行** 的分隔线,分隔线要求符合**需求 3** 244 | 245 | > 提示:工作中针对需求的变化,应该冷静思考,**不要轻易修改之前已经完成的,能够正常执行的函数**! 246 | 247 | ``` 248 | def print_line(char, times): 249 | 250 | print(char * times) 251 | 252 | 253 | def print_lines(char, times): 254 | 255 | row = 0 256 | 257 | while row < 5: 258 | print_line(char, times) 259 | 260 | row += 1 261 | ``` 262 | 263 | 264 | 265 | ### 用模块管理函数 266 | 267 | 对于任何一种编程语言来说,给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。最简单的场景就是在同一个.py文件中定义了两个同名函数,由于Python没有函数重载的概念,那么后面的定义会覆盖之前的定义,也就意味着两个函数同名函数实际上只有一个是存在的。 268 | 269 | ``` 270 | def foo(): 271 | print('hello, world!') 272 | 273 | 274 | def foo(): 275 | print('goodbye, world!') 276 | 277 | 278 | # 下面的代码会输出什么呢? 279 | foo() 280 | ``` 281 | 282 | 当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。 283 | 284 | ``` 285 | module1.py 286 | def foo(): 287 | print('hello, world!') 288 | module2.py 289 | def foo(): 290 | print('goodbye, world!') 291 | test.py 292 | from module1 import foo 293 | 294 | # 输出hello, world! 295 | foo() 296 | 297 | from module2 import foo 298 | 299 | # 输出goodbye, world! 300 | foo() 301 | ``` 302 | 303 | 也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。 304 | 305 | ``` 306 | test.py 307 | import module1 as m1 308 | import module2 as m2 309 | 310 | m1.foo() 311 | m2.foo() 312 | ``` 313 | 314 | 但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个`foo`,因为后导入的foo覆盖了之前导入的`foo`。 315 | 316 | ``` 317 | test.py 318 | from module1 import foo 319 | from module2 import foo 320 | 321 | # 输出goodbye, world! 322 | foo() 323 | test.py 324 | from module2 import foo 325 | from module1 import foo 326 | 327 | # 输出hello, world! 328 | foo() 329 | ``` 330 | 331 | 需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"__main__"。 332 | 333 | ``` 334 | module3.py 335 | def foo(): 336 | pass 337 | 338 | 339 | def bar(): 340 | pass 341 | 342 | 343 | # __name__是Python中一个隐含的变量它代表了模块的名字 344 | # 只有被Python解释器直接执行的模块的名字才是__main__ 345 | if __name__ == '__main__': 346 | print('call foo()') 347 | foo() 348 | print('call bar()') 349 | bar() 350 | test.py 351 | import module3 352 | 353 | # 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__ 354 | ``` 355 | 356 | 357 | 358 | ### 练习 359 | 360 | #### 练习1:实现判断一个数是不是回文数的函数。 361 | 362 | 参考答案: 363 | 364 | ``` 365 | def is_palindrome(num): 366 | """判断一个数是不是回文数""" 367 | temp = num 368 | total = 0 369 | while temp > 0: 370 | total = total * 10 + temp % 10 371 | temp //= 10 372 | return total == num 373 | ``` 374 | 375 | #### 练习2:实现判断一个数是不是素数的函数。 376 | 377 | 参考答案: 378 | 379 | ``` 380 | def is_prime(num): 381 | """判断一个数是不是素数""" 382 | for factor in range(2, int(num ** 0.5) + 1): 383 | if num % factor == 0: 384 | return False 385 | return True if num != 1 else False 386 | ``` 387 | 388 | ### 拓展:变量的作用域 389 | 390 | 最后,我们来讨论一下Python中有关变量作用域的问题。 391 | 392 | ``` 393 | def foo(): 394 | b = 'hello' 395 | 396 | # Python中可以在函数内部再定义函数 397 | def bar(): 398 | c = True 399 | print(a) 400 | print(b) 401 | print(c) 402 | 403 | bar() 404 | # print(c) # NameError: name 'c' is not defined 405 | 406 | 407 | if __name__ == '__main__': 408 | a = 100 409 | # print(b) # NameError: name 'b' is not defined 410 | foo() 411 | ``` 412 | 413 | 上面的代码能够顺利的执行并且打印出100、hello和True,但我们注意到了,在`bar`函数的内部并没有定义`a`和`b`两个变量,那么`a`和`b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些标识符,我们之前用过的`input`、`print`、`int`等都属于内置作用域。 414 | 415 | 再看看下面这段代码,我们希望通过函数调用修改全局变量`a`的值,但实际上下面的代码是做不到的。 416 | 417 | ``` 418 | def foo(): 419 | a = 200 420 | print(a) # 200 421 | 422 | 423 | if __name__ == '__main__': 424 | a = 100 425 | foo() 426 | print(a) # 100 427 | ``` 428 | 429 | 在调用`foo`函数后,我们发现`a`的值仍然是100,这是因为当我们在函数`foo`中写`a = 200`的时候,是重新定义了一个名字为`a`的局部变量,它跟全局作用域的`a`并不是同一个变量,因为局部作用域中有了自己的变量`a`,因此`foo`函数不再搜索全局作用域中的`a`。如果我们希望在`foo`函数中修改全局作用域中的`a`,代码如下所示。 430 | 431 | ``` 432 | def foo(): 433 | global a 434 | a = 200 435 | print(a) # 200 436 | 437 | 438 | if __name__ == '__main__': 439 | a = 100 440 | foo() 441 | print(a) # 200 442 | ``` 443 | 444 | 我们可以使用`global`关键字来指示`foo`函数中的变量`a`来自于全局作用域,如果全局作用域中没有`a`,那么下面一行的代码就会定义变量`a`并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用`nonlocal`关键字来指示变量来自于嵌套作用域,请大家自行试验。 445 | 446 | 在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/垃圾回收_(計算機科學))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/得墨忒耳定律)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/闭包_(计算机科学)),这个我们在后续的内容中进行讲解。 447 | 448 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 449 | -------------------------------------------------------------------------------- /Python基础连载/分支结构.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:Python分支结构 2 | 3 | ## Python分支结构 4 | 5 | - 分支结构的应用场景 - 条件 / 缩进 / 代码块 / 流程图 6 | - if语句 - 简单的if / if-else结构 / if-elif-else结构 / 嵌套的if 7 | - 应用案例 8 | 9 | ### 开发的应用场景 10 | 11 | 迄今为止,我们写的Python代码都是一条一条语句顺序执行,这种代码结构通常称之为顺序结构。然而仅有顺序结构并不能解决所有的问题,比如我们设计一个游戏,游戏第一关的通关条件是玩家获得1000分,那么在完成本局游戏后,我们要根据玩家得到分数来决定究竟是进入第二关,还是告诉玩家“Game Over”,这里就会产生两个分支,而且这两个分支只有一个会被执行。类似的场景还有很多,我们将这种结构称之为“分支结构”或“选择结构”。 12 | 13 | ### 程序中的判断 14 | 15 | 来一波中文型Python 16 | 17 | ![image-20210331220051664](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331220051664.png) 18 | 19 | ```python 20 | if 今天发工资: 21 | 22 | 先还信用卡的钱 23 | 24 | if 有剩余: 25 | 26 | 又可以happy了,O(∩_∩)O哈哈~ 27 | 28 | else: 29 | 30 | 噢,no。。。还的等30天 31 | else: 32 | 33 | 盼着发工资 34 | ``` 35 | 36 | ### 判断的定义 37 | 38 | - 如果 **条件满足**,才能做某件事情, 39 | - 如果 **条件不满足**,就做另外一件事情,或者什么也不做 40 | 41 | > 正是因为有了判断,才使得程序世界丰富多彩,充满变化! 42 | > 43 | > **判断语句** 又被称为 “分支语句”,正是因为有了判断,才让程序有了很多的分支 44 | 45 | ### if语句 46 | 47 | ### if 判断语句基本语法 48 | 49 | 在 `Python` 中,**if 语句** 就是用来进行判断的,格式如下: 50 | 51 | ``` 52 | if 要判断的条件: 53 | 条件成立时,要做的事情 54 | …… 55 | ``` 56 | 57 | > 注意:代码的缩进为一个 `tab` 键,或者 **4** 个空格 —— **建议使用空格** 58 | > 59 | > - 在 Python 开发中,Tab 和空格不要混用! 60 | 61 | **我们可以把整个 if 语句看成一个完整的代码块** 62 | 63 | ![image-20210331220355001](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331220355001.png) 64 | 65 | 我们通过例子来学习if语句 66 | 67 | ### 判断语句演练 —— 判断年龄 68 | 69 | **需求** 70 | 71 | 1. 定义一个整数变量记录年龄 72 | 2. 判断是否满 18 岁 (**>=**) 73 | 3. 如果满 18 岁,允许进网吧嗨皮 74 | 75 | ``` 76 | """ 77 | Author: 海森堡 78 | """ 79 | # 1. 定义年龄变量 80 | age = int(input("请输入年龄:")) 81 | 82 | # 2. 判断是否满 18 岁 83 | # if 语句以及缩进部分的代码是一个完整的代码块 84 | if age >= 18: 85 | print("可以进网吧嗨皮……") 86 | 87 | # 3. 思考!- 无论条件是否满足都会执行 88 | print("这句代码什么时候执行?") 89 | ``` 90 | 91 | **注意**: 92 | 93 | - `if` 语句以及缩进部分是一个 **完整的代码块** 94 | 95 | ### else 处理条件不满足的情况 96 | 97 | **思考** 98 | 99 | 在使用 `if` 判断时,只能做到满足条件时要做的事情。那如果需要在 **不满足条件的时候**,做某些事情,该如何做呢? 100 | 101 | **答案** 102 | 103 | `else`,格式如下: 104 | 105 | ``` 106 | if 要判断的条件: 107 | 条件成立时,要做的事情 108 | …… 109 | else: 110 | 条件不成立时,要做的事情 111 | …… 112 | ``` 113 | 114 | **注意**: 115 | 116 | - `if` 和 `else` 语句以及各自的缩进部分共同是一个 **完整的代码块** 117 | 118 | ### 判断语句演练 —— 判断年龄改进 119 | 120 | **需求** 121 | 122 | 1. 输入用户年龄 123 | 2. 判断是否满 18 岁 (**>=**) 124 | 3. 如果满 18 岁,允许进网吧嗨皮 125 | 4. 如果未满 18 岁,提示回家写作业 126 | 127 | ```python 128 | # 1. 输入用户年龄 129 | age = int(input("今年多大了?")) 130 | 131 | # 2. 判断是否满 18 岁 132 | # if 语句以及缩进部分的代码是一个完整的语法块 133 | if age >= 18: 134 | print("可以进网吧嗨皮……") 135 | else: 136 | print("你还没长大,应该回家写作业!") 137 | 138 | # 3. 思考!- 无论条件是否满足都会执行 139 | print("这句代码什么时候执行?") 140 | ``` 141 | 142 | ![image-20210331221113623](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331221113623.png) 143 | 144 | ## 逻辑运算 145 | 146 | - 在程序开发中,通常 **在判断条件时**,会需要同时判断多个条件 147 | - 只有多个条件都满足,才能够执行后续代码,这个时候需要使用到 **逻辑运算符** 148 | - **逻辑运算符** 可以把 **多个条件** 按照 **逻辑** 进行 **连接**,变成 **更复杂的条件** 149 | - Python 中的 **逻辑运算符** 包括:**与 and**/**或 or**/**非 not** 三种 150 | 151 | ### `and` 152 | 153 | ``` 154 | 条件1 and 条件2 155 | ``` 156 | 157 | - **与**/**并且** 158 | - 两个条件同时满足,返回 `True` 159 | - 只要有一个不满足,就返回 `False` 160 | 161 | | 条件 1 | 条件 2 | 结果 | 162 | | ------ | ------ | ------ | 163 | | 成立 | 成立 | 成立 | 164 | | 成立 | 不成立 | 不成立 | 165 | | 不成立 | 成立 | 不成立 | 166 | | 不成立 | 不成立 | 不成立 | 167 | 168 | ### `or` 169 | 170 | ``` 171 | 条件1 or 条件2 172 | ``` 173 | 174 | - **或**/**或者** 175 | - 两个条件只要有一个满足,返回 `True` 176 | - 两个条件都不满足,返回 `False` 177 | 178 | | 条件 1 | 条件 2 | 结果 | 179 | | ------ | ------ | ------ | 180 | | 成立 | 成立 | 成立 | 181 | | 成立 | 不成立 | 成立 | 182 | | 不成立 | 成立 | 成立 | 183 | | 不成立 | 不成立 | 不成立 | 184 | 185 | ### `not` 186 | 187 | ``` 188 | not 条件 189 | ``` 190 | 191 | - **非**/**不是** 192 | 193 | | 条件 | 结果 | 194 | | ------ | ------ | 195 | | 成立 | 不成立 | 196 | | 不成立 | 成立 | 197 | 198 | #### 逻辑运算演练 199 | 200 | 练习1: 定义一个整数变量 `age`,编写代码判断年龄是否正确 201 | 202 | - 要求人的年龄在 0-120 之间 203 | 204 | 练习2: 定义两个整数变量 `python_score`、`c_score`,编写代码判断成绩 205 | 206 | - 要求只要有一门成绩 > 60 分就算合格 207 | 208 | 练习3: 定义一个布尔型变量 `is_employee`,编写代码判断是否是本公司员工 209 | 210 | - 如果不是提示不允许入内 211 | 212 | 1. 练习1: 定义一个整数变量 213 | 214 | ``` 215 | # 练习1: 定义一个整数变量 age,编写代码判断年龄是否正确 216 | age = 100 217 | 218 | # 要求人的年龄在 0-120 之间 219 | if age >= 0 and age <= 120: 220 | print("年龄正确") 221 | else: 222 | print("年龄不正确") 223 | ``` 224 | 225 | 2. 练习2: 定义两个整数变量 226 | 227 | ``` 228 | # 练习2: 定义两个整数变量 python_score、c_score,编写代码判断成绩 229 | python_score = 50 230 | c_score = 50 231 | 232 | # 要求只要有一门成绩 > 60 分就算合格 233 | if python_score > 60 or c_score > 60: 234 | print("考试通过") 235 | else: 236 | print("再接再厉!") 237 | ``` 238 | 239 | 3. 练习3: 定义一个布尔型变量 240 | 241 | ``` 242 | # 练习3: 定义一个布尔型变量 `is_employee`,编写代码判断是否是本公司员工 243 | is_employee = True 244 | 245 | # 如果不是提示不允许入内 246 | if not is_employee: 247 | print("非公勿内") 248 | ``` 249 | 250 | 答案 1: 251 | 252 | ``` 253 | # 练习1: 定义一个整数变量 age,编写代码判断年龄是否正确 254 | age = 100 255 | 256 | # 要求人的年龄在 0-120 之间 257 | if age >= 0 and age <= 120: 258 | print("年龄正确") 259 | else: 260 | print("年龄不正确") 261 | ``` 262 | 263 | 答案 2: 264 | 265 | ``` 266 | # 练习2: 定义两个整数变量 python_score、c_score,编写代码判断成绩 267 | python_score = 50 268 | c_score = 50 269 | 270 | # 要求只要有一门成绩 > 60 分就算合格 271 | if python_score > 60 or c_score > 60: 272 | print("考试通过") 273 | else: 274 | print("再接再厉!") 275 | ``` 276 | 277 | 答案 3: 278 | 279 | ``` 280 | # 练习3: 定义一个布尔型变量 `is_employee`,编写代码判断是否是本公司员工 281 | is_employee = True 282 | 283 | # 如果不是提示不允许入内 284 | if not is_employee: 285 | print("非公勿内") 286 | ``` 287 | 288 | ## if 语句进阶 289 | 290 | ### 4.1 `elif` 291 | 292 | - 在开发中,使用 `if` 可以 **判断条件** 293 | - 使用 `else` 可以处理 **条件不成立** 的情况 294 | - 但是,如果希望 **再增加一些条件**,**条件不同,需要执行的代码也不同** 时,就可以使用 `elif` 295 | - 语法格式如下: 296 | 297 | ``` 298 | if 条件1: 299 | 条件1满足执行的代码 300 | …… 301 | elif 条件2: 302 | 条件2满足时,执行的代码 303 | …… 304 | elif 条件3: 305 | 条件3满足时,执行的代码 306 | …… 307 | else: 308 | 以上条件都不满足时,执行的代码 309 | …… 310 | ``` 311 | 312 | - 对比逻辑运算符的代码 313 | 314 | ``` 315 | if 条件1 and 条件2: 316 | 条件1满足 并且 条件2满足 执行的代码 317 | …… 318 | ``` 319 | 320 | **注意** 321 | 322 | 1. `elif` 和 `else` 都必须和 `if` 联合使用,而不能单独使用 323 | 2. 可以将 `if`、`elif` 和 `else` 以及各自缩进的代码,看成一个 **完整的代码块** 324 | 325 | #### elif 演练 —— 女友的节日 326 | 327 | **需求** 328 | 329 | 1. 定义 `holiday_name` 字符串变量记录节日名称 330 | 2. 如果是 **情人节** 应该 **买玫瑰**/**看电影** 331 | 3. 如果是 **平安夜** 应该 **买苹果**/**吃大餐** 332 | 4. 如果是 **生日** 应该 **买蛋糕** 333 | 5. 其他的日子每天都是节日啊…… 334 | 335 | ``` 336 | holiday_name = "平安夜" 337 | 338 | if holiday_name == "情人节": 339 | print("买玫瑰") 340 | print("看电影") 341 | elif holiday_name == "平安夜": 342 | print("买苹果") 343 | print("吃大餐") 344 | elif holiday_name == "生日": 345 | print("买蛋糕") 346 | else: 347 | print("每天都是节日啊……") 348 | ``` 349 | 350 | ### `if` 的嵌套 351 | 352 | **elif** 的应用场景是:**同时** 判断 **多个条件**,所有的条件是 **平级** 的 353 | 354 | - 在开发中,使用 `if` 进行条件判断,如果希望 **在条件成立的执行语句中** 再 **增加条件判断**,就可以使用 **if 的嵌套** 355 | - **if 的嵌套** 的应用场景就是:**在之前条件满足的前提下,再增加额外的判断** 356 | - **if 的嵌套** 的语法格式,**除了缩进之外** 和之前的没有区别 357 | 358 | - 语法格式如下: 359 | 360 | ``` 361 | if 条件 1: 362 | 条件 1 满足执行的代码 363 | …… 364 | 365 | if 条件 1 基础上的条件 2: 366 | 条件 2 满足时,执行的代码 367 | …… 368 | 369 | # 条件 2 不满足的处理 370 | else: 371 | 条件 2 不满足时,执行的代码 372 | 373 | # 条件 1 不满足的处理 374 | else: 375 | 条件1 不满足时,执行的代码 376 | …… 377 | ``` 378 | 379 | #### if 的嵌套 演练 —— 火车站安检 380 | 381 | **需求** 382 | 383 | 1. 定义布尔型变量 `has_ticket` 表示是否有车票 384 | 2. 定义整型变量 `knife_length` 表示刀的长度,单位:厘米 385 | 3. 首先检查是否有车票,如果有,才允许进行 **安检** 386 | 4. 安检时,需要检查刀的长度,判断是否超过 20 厘米 387 | - 如果超过 20 厘米,提示刀的长度,不允许上车 388 | - 如果不超过 20 厘米,安检通过 389 | 5. 如果没有车票,不允许进门 390 | 391 | ```Python 392 | # 定义布尔型变量 has_ticket 表示是否有车票 393 | has_ticket = True 394 | 395 | # 定义整数型变量 knife_length 表示刀的长度,单位:厘米 396 | knife_length = 20 397 | 398 | # 首先检查是否有车票,如果有,才允许进行 安检 399 | if has_ticket: 400 | print("有车票,可以开始安检...") 401 | 402 | # 安检时,需要检查刀的长度,判断是否超过 20 厘米 403 | # 如果超过 20 厘米,提示刀的长度,不允许上车 404 | if knife_length >= 20: 405 | print("不允许携带 %d 厘米长的刀上车" % knife_length) 406 | # 如果不超过 20 厘米,安检通过 407 | else: 408 | print("安检通过,祝您旅途愉快……") 409 | 410 | # 如果没有车票,不允许进门 411 | else: 412 | print("大哥,您要先买票啊") 413 | ``` 414 | 415 | ## 综合应用 —— 石头剪刀布 416 | 417 | **目标** 418 | 419 | 1. 强化 **多个条件** 的 **逻辑运算** 420 | 2. 体会 `import` 导入模块(“工具包”)的使用 421 | 422 | **需求** 423 | 424 | 1. 从控制台输入要出的拳 —— 石头(1)/剪刀(2)/布(3) 425 | 2. 电脑 **随机** 出拳 —— 先假定电脑只会出石头,完成整体代码功能 426 | 3. 比较胜负 427 | 428 | | 序号 | 规则 | 429 | | ---- | ------------ | 430 | | 1 | 石头 胜 剪刀 | 431 | | 2 | 剪刀 胜 布 | 432 | | 3 | 布 胜 石头 | 433 | 434 | ### 5.1 基础代码实现 435 | 436 | - 先 **假定电脑就只会出石头**,完成整体代码功能 437 | 438 | ``` 439 | # 从控制台输入要出的拳 —— 石头(1)/剪刀(2)/布(3) 440 | player = int(input("请出拳 石头(1)/剪刀(2)/布(3):")) 441 | 442 | # 电脑 随机 出拳 - 假定电脑永远出石头 443 | computer = 1 444 | 445 | # 比较胜负 446 | # 如果条件判断的内容太长,可以在最外侧的条件增加一对大括号 447 | # 再在每一个条件之间,使用回车,PyCharm 可以自动增加 8 个空格 448 | if ((player == 1 and computer == 2) or 449 | (player == 2 and computer == 3) or 450 | (player == 3 and computer == 1)): 451 | 452 | print("噢耶!!!电脑弱爆了!!!") 453 | elif player == computer: 454 | print("心有灵犀,再来一盘!") 455 | else: 456 | print("不行,我要和你决战到天亮!") 457 | ``` 458 | 459 | ##### 练习:百分制成绩转换为等级制成绩。 460 | 461 | **要求**:如果输入的成绩在90分以上(含90分)输出A;80分-90分(不含90分)输出B;70分-80分(不含80分)输出C;60分-70分(不含70分)输出D;60分以下输出E。 462 | 463 | ```python 464 | """ 465 | 百分制成绩转换为等级制成绩 466 | """ 467 | score = float(input('请输入成绩: ')) 468 | if score >= 90: 469 | grade = 'A' 470 | elif score >= 80: 471 | grade = 'B' 472 | elif score >= 70: 473 | grade = 'C' 474 | elif score >= 60: 475 | grade = 'D' 476 | else: 477 | grade = 'E' 478 | print('对应的等级是:', grade) 479 | ``` 480 | 481 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 482 | 483 | -------------------------------------------------------------------------------- /Python基础连载/字符串和常见数据类型.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:字符串和常见数据类型 2 | 3 | ## 字符串和常见数据类型 4 | 5 | - 列表基本用法 - 定义列表 / 用下表访问元素 / 下标越界 / 添加元素 / 删除元素 / 修改元素 / 切片 / 循环遍历 6 | - 列表常用操作 - 连接 / 复制(复制元素和复制数组) / 长度 / 排序 / 倒转 / 查找 7 | - 生成列表 - 使用range创建数字列表 / 生成表达式 / 生成器 8 | - 元组的使用 - 定义元组 / 使用元组中的值 / 修改元组变量 / 元组和列表转换 9 | - 集合基本用法 - 集合和列表的区别 / 创建集合 / 添加元素 / 删除元素 / 清空 10 | - 集合常用操作 - 交集 / 并集 / 差集 / 对称差 / 子集 / 超集 11 | - 字典的基本用法 - 字典的特点 / 创建字典 / 添加元素 / 删除元素 / 取值 / 清空 12 | - 字典常用操作 - keys()方法 / values()方法 / items()方法 / setdefault()方法 13 | - 字符串的使用 - 计算长度 / 下标运算 / 切片 / 常用方法 14 | - 实战练习 15 | 16 | ### 知识点回顾 17 | 18 | - Python 中数据类型可以分为 **数字型** 和 **非数字型** 19 | - 数字型 20 | - 整型 (`int`) 21 | - 浮点型(`float`) 22 | - 布尔型(`bool`) 23 | - 真 `True` `非 0 数` —— **非零即真** 24 | - 假 `False` `0` 25 | - 复数型 (`complex`) 26 | + 主要用于科学计算,例如:平面场问题、波动问题、电感电容等问题 27 | - 非数字型 28 | - 字符串 29 | - 列表 30 | - 元组 31 | - 字典 32 | - 在 `Python` 中,所有 **非数字型变量** 都支持以下特点: 33 | 1. 都是一个 **序列** `sequence`,也可以理解为 **容器** 34 | 2. **取值** `[]` 35 | 3. **遍历** `for in` 36 | 4. **计算长度**、**最大/最小值**、**比较**、**删除** 37 | 5. **链接** `+` 和 **重复** `*` 38 | 6. **切片** 39 | 40 | ## 01. 列表 41 | 42 | ### 1.1 列表的定义 43 | 44 | - `List`(列表) 是 `Python` 中使用 **最频繁** 的数据类型,在其他语言中通常叫做 **数组** 45 | - 专门用于存储 **一串 信息** 46 | - 列表用 `[]` 定义,**数据** 之间使用 `,` 分隔 47 | - 列表的 **索引** 从 `0` 开始 48 | - **索引** 就是数据在 **列表** 中的位置编号,**索引** 又可以被称为 **下标** 49 | 50 | > 注意:从列表中取值时,如果 **超出索引范围**,程序会报错 51 | 52 | ``` 53 | name_list = ["zhangsan", "lisi", "wangwu"] 54 | ``` 55 | 56 | ![image-20210331230553109](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331230553109.png) 57 | 58 | ### 列表常用操作 59 | 60 | - 在 `ipython3` 中定义一个 **列表**,例如:`name_list = []` 61 | - 输入 `name_list.` 按下 `TAB` 键,`ipython` 会提示 **列表** 能够使用的 **方法** 如下: 62 | 63 | ``` 64 | In [1]: name_list. 65 | name_list.append name_list.count name_list.insert name_list.reverse 66 | name_list.clear name_list.extend name_list.pop name_list.sort 67 | name_list.copy name_list.index name_list.remove 68 | ``` 69 | 70 | | 序号 | 分类 | 关键字 / 函数 / 方法 | 说明 | 71 | | ---- | ---- | ----------------------- | ------------------------ | 72 | | 1 | 增加 | 列表.insert(索引, 数据) | 在指定位置插入数据 | 73 | | | | 列表.append(数据) | 在末尾追加数据 | 74 | | | | 列表.extend(列表2) | 将列表2 的数据追加到列表 | 75 | | 2 | 修改 | 列表[索引] = 数据 | 修改指定索引的数据 | 76 | | 3 | 删除 | del 列表[索引] | 删除指定索引的数据 | 77 | | | | 列表.remove[数据] | 删除第一个出现的指定数据 | 78 | | | | 列表.pop | 删除末尾数据 | 79 | | | | 列表.pop(索引) | 删除指定索引数据 | 80 | | | | 列表.clear | 清空列表 | 81 | | 4 | 统计 | len(列表) | 列表长度 | 82 | | | | 列表.count(数据) | 数据在列表中出现的次数 | 83 | | 5 | 排序 | 列表.sort() | 升序排序 | 84 | | | | 列表.sort(reverse=True) | 降序排序 | 85 | | | | 列表.reverse() | 逆序、反转 | 86 | 87 | #### del 关键字(科普) 88 | 89 | - 使用 `del` 关键字(`delete`) 同样可以删除列表中元素 90 | - `del` 关键字本质上是用来 **将一个变量从内存中删除的** 91 | - 如果使用 `del` 关键字将变量从内存中删除,后续的代码就不能再使用这个变量了 92 | 93 | ``` 94 | del name_list[1] 95 | ``` 96 | 97 | > 在日常开发中,要从列表删除数据,建议 **使用列表提供的方法** 98 | 99 | #### 关键字、函数和方法(科普) 100 | 101 | - **关键字** 是 Python 内置的、具有特殊意义的标识符 102 | 103 | ``` 104 | In [1]: import keyword 105 | In [2]: print(keyword.kwlist) 106 | In [3]: print(len(keyword.kwlist)) 107 | ``` 108 | 109 | > 关键字后面不需要使用括号 110 | 111 | - **函数** 封装了独立功能,可以直接调用 112 | 113 | ``` 114 | 函数名(参数) 115 | ``` 116 | 117 | > 函数需要死记硬背 118 | 119 | - **方法** 和函数类似,同样是封装了独立的功能 120 | - **方法** 需要通过 **对象** 来调用,表示针对这个 **对象** 要做的操作 121 | 122 | ``` 123 | 对象.方法名(参数) 124 | ``` 125 | 126 | > 在变量后面输入 `.`,然后选择针对这个变量要执行的操作,记忆起来比函数要简单很多 127 | 128 | ### 循环遍历 129 | 130 | - **遍历** 就是 **从头到尾** **依次** 从 **列表** 中获取数据 131 | - 在 **循环体内部** 针对 **每一个元素**,执行相同的操作 132 | - 在 `Python` 中为了提高列表的遍历效率,专门提供的 **迭代 iteration 遍历** 133 | - 使用 `for` 就能够实现迭代遍历 134 | 135 | ``` 136 | # for 循环内部使用的变量 in 列表 137 | for name in name_list: 138 | 139 | 循环内部针对列表元素进行操作 140 | print(name) 141 | ``` 142 | 143 | ![image-20210331230625086](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331230625086.png) 144 | 145 | ### **应用场景** 146 | 147 | - 尽管 `Python` 的 **列表** 中可以 **存储不同类型的数据** 148 | - 但是在开发中,更多的应用场景是 149 | 1. **列表** 存储相同类型的数据 150 | 2. 通过 **迭代遍历**,在循环体内部,针对列表中的每一项元素,执行相同的操作 151 | 152 | ## 元组 153 | 154 | ### 元组的定义 155 | 156 | - `Tuple`(元组)与列表类似,不同之处在于元组的 **元素不能修改** 157 | - **元组** 表示多个元素组成的序列 158 | - **元组** 在 `Python` 开发中,有特定的应用场景 159 | - 用于存储 **一串 信息**,**数据** 之间使用 `,` 分隔 160 | - 元组用 `()` 定义 161 | - 元组的 **索引** 从 `0` 开始 162 | - **索引** 就是数据在 **元组** 中的位置编号 163 | 164 | ``` 165 | info_tuple = ("zhangsan", 18, 1.75) 166 | ``` 167 | 168 | #### 创建空元组 169 | 170 | ``` 171 | info_tuple = () 172 | ``` 173 | 174 | #### 元组中 **只包含一个元素** 时,需要 **在元素后面添加逗号** 175 | 176 | ``` 177 | info_tuple = (50, ) 178 | ``` 179 | 180 | ![image-20210331230653448](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331230653448.png) 181 | 182 | ### 元组常用操作 183 | 184 | - 在 `ipython3` 中定义一个 **元组**,例如:`info = ()` 185 | - 输入 `info.` 按下 `TAB` 键,`ipython` 会提示 **元组** 能够使用的函数如下: 186 | 187 | ``` 188 | info.count info.index 189 | ``` 190 | 191 | > 有关 **元组** 的 **常用操作** 可以参照上图练习 192 | 193 | ### 循环遍历 194 | 195 | - **取值** 就是从 **元组** 中获取存储在指定位置的数据 196 | - **遍历** 就是 **从头到尾** **依次** 从 **元组** 中获取数据 197 | 198 | ``` 199 | # for 循环内部使用的变量 in 元组 200 | for item in info: 201 | 202 | 循环内部针对元组元素进行操作 203 | print(item) 204 | ``` 205 | 206 | > - 在 `Python` 中,可以使用 `for` 循环遍历所有非数字型类型的变量:**列表**、**元组**、**字典** 以及 **字符串** 207 | > - 提示:在实际开发中,除非 **能够确认元组中的数据类型**,否则针对元组的循环遍历需求并不是很多 208 | 209 | ### 应用场景 210 | 211 | - 尽管可以使用 `for in` 遍历 **元组** 212 | 213 | - 但是在开发中,更多的应用场景是: 214 | 215 | - 函数的 参数 和 返回值 216 | 217 | ,一个函数可以接收 218 | 219 | 220 | 221 | 任意多个参数 222 | 223 | ,或者 224 | 225 | 226 | 227 | 一次返回多个数据 228 | 229 | - 有关 **函数的参数 和 返回值**,在后续 **函数高级** 给大家介绍 230 | 231 | - **格式字符串**,格式化字符串后面的 `()` 本质上就是一个元组 232 | 233 | - **让列表不可以被修改**,以保护数据安全 234 | 235 | ``` 236 | info = ("zhangsan", 18) 237 | 238 | print("%s 的年龄是 %d" % info) 239 | ``` 240 | 241 | #### 元组和列表之间的转换 242 | 243 | - 使用 `list` 函数可以把元组转换成列表 244 | 245 | ``` 246 | list(元组) 247 | ``` 248 | 249 | - 使用 `tuple` 函数可以把列表转换成元组 250 | 251 | ``` 252 | tuple(列表) 253 | ``` 254 | 255 | ## 字典 256 | 257 | ### 字典的定义 258 | 259 | - `dictionary`(字典) 是 **除列表以外** `Python` 之中 **最灵活** 的数据类型 260 | 261 | - 字典同样可以用来 262 | 263 | 264 | 265 | 存储多个数据 266 | 267 | - 通常用于存储 **描述一个 `物体` 的相关信息** 268 | 269 | - 和列表的区别 270 | 271 | - **列表** 是 **有序** 的对象集合 272 | - **字典** 是 **无序** 的对象集合 273 | 274 | - 字典用 `{}` 定义 275 | 276 | - 字典使用 277 | 278 | 279 | 280 | 键值对 281 | 282 | 283 | 284 | 存储数据,键值对之间使用 285 | 286 | 287 | 288 | ``` 289 | , 290 | ``` 291 | 292 | 293 | 294 | 分隔 295 | 296 | - **键** `key` 是索引 297 | - **值** `value` 是数据 298 | - **键** 和 **值** 之间使用 `:` 分隔 299 | - **键必须是唯一的** 300 | - **值** 可以取任何数据类型,但 **键** 只能使用 **字符串**、**数字**或 **元组** 301 | 302 | ``` 303 | xiaoming = {"name": "小明", 304 | "age": 18, 305 | "gender": True, 306 | "height": 1.75} 307 | ``` 308 | 309 | ![image-20210331230720665](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331230720665.png) 310 | 311 | ### 字典常用操作 312 | 313 | - 在 `ipython3` 中定义一个 **字典**,例如:`xiaoming = {}` 314 | - 输入 `xiaoming.` 按下 `TAB` 键,`ipython` 会提示 **字典** 能够使用的函数如下: 315 | 316 | ``` 317 | In [1]: xiaoming. 318 | xiaoming.clear xiaoming.items xiaoming.setdefault 319 | xiaoming.copy xiaoming.keys xiaoming.update 320 | xiaoming.fromkeys xiaoming.pop xiaoming.values 321 | xiaoming.get xiaoming.popitem 322 | ``` 323 | 324 | > 有关 **字典** 的 **常用操作** 可以参照上图练习 325 | 326 | ### 循环遍历 327 | 328 | - **遍历** 就是 **依次** 从 **字典** 中获取所有键值对 329 | 330 | ``` 331 | # for 循环内部使用的 `key 的变量` in 字典 332 | for k in xiaoming: 333 | 334 | print("%s: %s" % (k, xiaoming[k])) 335 | ``` 336 | 337 | > 提示:在实际开发中,由于字典中每一个键值对保存数据的类型是不同的,所以针对字典的循环遍历需求并不是很多 338 | 339 | ### 3.4 **应用场景** 340 | 341 | - 尽管可以使用 `for in` 遍历 **字典** 342 | - 但是在开发中,更多的应用场景是: 343 | - 使用 **多个键值对**,存储 **描述一个 `物体` 的相关信息** —— 描述更复杂的数据信息 344 | - 将 **多个字典** 放在 **一个列表** 中,再进行遍历,在循环体内部针对每一个字典进行 **相同的处理** 345 | 346 | ``` 347 | card_list = [{"name": "张三", 348 | "qq": "12345", 349 | "phone": "110"}, 350 | {"name": "李四", 351 | "qq": "54321", 352 | "phone": "10086"} 353 | ] 354 | ``` 355 | 356 | ## 字符串 357 | 358 | ### 字符串的定义 359 | 360 | - **字符串** 就是 **一串字符**,是编程语言中表示文本的数据类型 361 | 362 | - 在 Python 中可以使用 363 | 364 | 365 | 366 | 一对双引号 367 | 368 | 369 | 370 | ``` 371 | " 372 | ``` 373 | 374 | 375 | 376 | 或者 377 | 378 | 379 | 380 | 一对单引号 381 | 382 | 383 | 384 | ``` 385 | ' 386 | ``` 387 | 388 | 389 | 390 | 定义一个字符串 391 | 392 | - 虽然可以使用 393 | 394 | 395 | 396 | ``` 397 | \" 398 | ``` 399 | 400 | 401 | 402 | 或者 403 | 404 | 405 | 406 | ``` 407 | \' 408 | ``` 409 | 410 | 411 | 412 | 做字符串的转义,但是在实际开发中: 413 | 414 | - 如果字符串内部需要使用 `"`,可以使用 `'` 定义字符串 415 | - 如果字符串内部需要使用 `'`,可以使用 `"` 定义字符串 416 | 417 | - 可以使用 **索引** 获取一个字符串中 **指定位置的字符**,索引计数从 **0** 开始 418 | 419 | - 也可以使用 `for` **循环遍历** 字符串中每一个字符 420 | 421 | > 大多数编程语言都是用 `"` 来定义字符串 422 | 423 | ``` 424 | string = "Hello Python" 425 | 426 | for c in string: 427 | print(c) 428 | ``` 429 | 430 | ![image-20210331230738570](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331230738570.png) 431 | 432 | ### 字符串的常用操作 433 | 434 | - 在 `ipython3` 中定义一个 **字符串**,例如:`hello_str = ""` 435 | - 输入 `hello_str.` 按下 `TAB` 键,`ipython` 会提示 **字符串** 能够使用的 **方法** 如下: 436 | 437 | ``` 438 | In [1]: hello_str. 439 | hello_str.capitalize hello_str.isidentifier hello_str.rindex 440 | hello_str.casefold hello_str.islower hello_str.rjust 441 | hello_str.center hello_str.isnumeric hello_str.rpartition 442 | hello_str.count hello_str.isprintable hello_str.rsplit 443 | hello_str.encode hello_str.isspace hello_str.rstrip 444 | hello_str.endswith hello_str.istitle hello_str.split 445 | hello_str.expandtabs hello_str.isupper hello_str.splitlines 446 | hello_str.find hello_str.join hello_str.startswith 447 | hello_str.format hello_str.ljust hello_str.strip 448 | hello_str.format_map hello_str.lower hello_str.swapcase 449 | hello_str.index hello_str.lstrip hello_str.title 450 | hello_str.isalnum hello_str.maketrans hello_str.translate 451 | hello_str.isalpha hello_str.partition hello_str.upper 452 | hello_str.isdecimal hello_str.replace hello_str.zfill 453 | hello_str.isdigit hello_str.rfind 454 | ``` 455 | 456 | > 提示:正是因为 python 内置提供的方法足够多,才使得在开发时,能够针对字符串进行更加灵活的操作!应对更多的开发需求! 457 | 458 | #### 1) 判断类型 - 9 459 | 460 | | 方法 | 说明 | 461 | | ------------------ | ------------------------------------------------------------ | 462 | | string.isspace() | 如果 string 中只包含空格,则返回 True | 463 | | string.isalnum() | 如果 string 至少有一个字符并且所有字符都是字母或数字则返回 True | 464 | | string.isalpha() | 如果 string 至少有一个字符并且所有字符都是字母则返回 True | 465 | | string.isdecimal() | 如果 string 只包含数字则返回 True,`全角数字` | 466 | | string.isdigit() | 如果 string 只包含数字则返回 True,`全角数字`、`⑴`、`\u00b2` | 467 | | string.isnumeric() | 如果 string 只包含数字则返回 True,`全角数字`,`汉字数字` | 468 | | string.istitle() | 如果 string 是标题化的(每个单词的首字母大写)则返回 True | 469 | | string.islower() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True | 470 | | string.isupper() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True | 471 | 472 | #### 2) 查找和替换 - 7 473 | 474 | | 方法 | 说明 | 475 | | ------------------------------------------------------- | ------------------------------------------------------------ | 476 | | string.startswith(str) | 检查字符串是否是以 str 开头,是则返回 True | 477 | | string.endswith(str) | 检查字符串是否是以 str 结束,是则返回 True | 478 | | string.find(str, start=0, end=len(string)) | 检测 str 是否包含在 string 中,如果 start 和 end 指定范围,则检查是否包含在指定范围内,如果是返回开始的索引值,否则返回 `-1` | 479 | | string.rfind(str, start=0, end=len(string)) | 类似于 find(),不过是从右边开始查找 | 480 | | string.index(str, start=0, end=len(string)) | 跟 find() 方法类似,不过如果 str 不在 string 会报错 | 481 | | string.rindex(str, start=0, end=len(string)) | 类似于 index(),不过是从右边开始 | 482 | | string.replace(old_str, new_str, num=string.count(old)) | 把 string 中的 old_str 替换成 new_str,如果 num 指定,则替换不超过 num 次 | 483 | 484 | #### 3) 大小写转换 - 5 485 | 486 | | 方法 | 说明 | 487 | | ------------------- | -------------------------------- | 488 | | string.capitalize() | 把字符串的第一个字符大写 | 489 | | string.title() | 把字符串的每个单词首字母大写 | 490 | | string.lower() | 转换 string 中所有大写字符为小写 | 491 | | string.upper() | 转换 string 中的小写字母为大写 | 492 | | string.swapcase() | 翻转 string 中的大小写 | 493 | 494 | #### 4) 文本对齐 - 3 495 | 496 | | 方法 | 说明 | 497 | | -------------------- | ------------------------------------------------------------ | 498 | | string.ljust(width) | 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串 | 499 | | string.rjust(width) | 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串 | 500 | | string.center(width) | 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串 | 501 | 502 | #### 5) 去除空白字符 - 3 503 | 504 | | 方法 | 说明 | 505 | | --------------- | ---------------------------------- | 506 | | string.lstrip() | 截掉 string 左边(开始)的空白字符 | 507 | | string.rstrip() | 截掉 string 右边(末尾)的空白字符 | 508 | | string.strip() | 截掉 string 左右两边的空白字符 | 509 | 510 | #### 6) 拆分和连接 - 5 511 | 512 | | 方法 | 说明 | 513 | | ------------------------- | ------------------------------------------------------------ | 514 | | string.partition(str) | 把字符串 string 分成一个 3 元素的元组 (str前面, str, str后面) | 515 | | string.rpartition(str) | 类似于 partition() 方法,不过是从右边开始查找 | 516 | | string.split(str="", num) | 以 str 为分隔符拆分 string,如果 num 有指定值,则仅分隔 num + 1 个子字符串,str 默认包含 '\r', '\t', '\n' 和空格 | 517 | | string.splitlines() | 按照行('\r', '\n', '\r\n')分隔,返回一个包含各行作为元素的列表 | 518 | | string.join(seq) | 以 string 作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串 | 519 | 520 | ### 字符串的切片 521 | 522 | - **切片** 方法适用于 **字符串**、**列表**、**元组** 523 | - **切片** 使用 **索引值** 来限定范围,从一个大的 **字符串** 中 **切出** 小的 **字符串** 524 | - **列表** 和 **元组** 都是 **有序** 的集合,都能够 **通过索引值** 获取到对应的数据 525 | - **字典** 是一个 **无序** 的集合,是使用 **键值对** 保存数据 526 | 527 | ![image-20210331230818696](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331230818696.png) 528 | 529 | ``` 530 | 字符串[开始索引:结束索引:步长] 531 | ``` 532 | 533 | **注意**: 534 | 535 | 1. 指定的区间属于 **左闭右开** 型 `[开始索引, 结束索引)` => `开始索引 >= 范围 < 结束索引` 536 | - 从 `起始` 位开始,到 **`结束`位的前一位** 结束(**不包含结束位本身**) 537 | 2. 从头开始,**开始索引** **数字可以省略,冒号不能省略** 538 | 3. 到末尾结束,**结束索引** **数字可以省略,冒号不能省略** 539 | 4. 步长默认为 `1`,如果连续切片,**数字和冒号都可以省略** 540 | 541 | #### 索引的顺序和倒序 542 | 543 | - 在 Python 中不仅支持 **顺序索引**,同时还支持 **倒序索引** 544 | - 所谓倒序索引就是从右向左计算索引 545 | - 最右边的索引值是 **-1**,依次递减 546 | 547 | **演练需求** 548 | 549 | - 1. 截取从 2 ~ 5 位置 的字符串 550 | - 1. 截取从 2 ~ `末尾` 的字符串 551 | - 1. 截取从 `开始` ~ 5 位置 的字符串 552 | - 1. 截取完整的字符串 553 | - 1. 从开始位置,每隔一个字符截取字符串 554 | - 1. 从索引 1 开始,每隔一个取一个 555 | - 1. 截取从 2 ~ `末尾 - 1` 的字符串 556 | - 1. 截取字符串末尾两个字符 557 | - 1. 字符串的逆序(面试题) 558 | 559 | **答案** 560 | 561 | ``` 562 | num_str = "0123456789" 563 | 564 | # 1. 截取从 2 ~ 5 位置 的字符串 565 | print(num_str[2:6]) 566 | 567 | # 2. 截取从 2 ~ `末尾` 的字符串 568 | print(num_str[2:]) 569 | 570 | # 3. 截取从 `开始` ~ 5 位置 的字符串 571 | print(num_str[:6]) 572 | 573 | # 4. 截取完整的字符串 574 | print(num_str[:]) 575 | 576 | # 5. 从开始位置,每隔一个字符截取字符串 577 | print(num_str[::2]) 578 | 579 | # 6. 从索引 1 开始,每隔一个取一个 580 | print(num_str[1::2]) 581 | 582 | # 倒序切片 583 | # -1 表示倒数第一个字符 584 | print(num_str[-1]) 585 | 586 | # 7. 截取从 2 ~ `末尾 - 1` 的字符串 587 | print(num_str[2:-1]) 588 | 589 | # 8. 截取字符串末尾两个字符 590 | print(num_str[-2:]) 591 | 592 | # 9. 字符串的逆序(面试题) 593 | print(num_str[::-1]) 594 | ``` 595 | 596 | ## 公共方法 597 | 598 | ### Python 内置函数 599 | 600 | Python 包含了以下内置函数: 601 | 602 | | 函数 | 描述 | 备注 | 603 | | ----------------- | --------------------------------- | --------------------------- | 604 | | len(item) | 计算容器中元素个数 | | 605 | | del(item) | 删除变量 | del 有两种方式 | 606 | | max(item) | 返回容器中元素最大值 | 如果是字典,只针对 key 比较 | 607 | | min(item) | 返回容器中元素最小值 | 如果是字典,只针对 key 比较 | 608 | | cmp(item1, item2) | 比较两个值,-1 小于/0 相等/1 大于 | Python 3.x 取消了 cmp 函数 | 609 | 610 | **注意** 611 | 612 | - **字符串** 比较符合以下规则: "0" < "A" < "a" 613 | 614 | ### 切片 615 | 616 | | 描述 | Python 表达式 | 结果 | 支持的数据类型 | | :---: | --- | --- | --- | --- | | 切片 | "0123456789"[::-2] | "97531" | 字符串、列表、元组 | 617 | 618 | - **切片** 使用 **索引值** 来限定范围,从一个大的 **字符串** 中 **切出** 小的 **字符串** 619 | - **列表** 和 **元组** 都是 **有序** 的集合,都能够 **通过索引值** 获取到对应的数据 620 | - **字典** 是一个 **无序** 的集合,是使用 **键值对** 保存数据 621 | 622 | ### 运算符 623 | 624 | | 运算符 | Python 表达式 | 结果 | 描述 | 支持的数据类型 | 625 | | ------------ | --------------------- | ---------------------------- | -------------- | ------------------------ | 626 | | + | [1, 2] + [3, 4] | [1, 2, 3, 4] | 合并 | 字符串、列表、元组 | 627 | | * | ["Hi!"] * 4 | ['Hi!', 'Hi!', 'Hi!', 'Hi!'] | 重复 | 字符串、列表、元组 | 628 | | in | 3 in (1, 2, 3) | True | 元素是否存在 | 字符串、列表、元组、字典 | 629 | | not in | 4 not in (1, 2, 3) | True | 元素是否不存在 | 字符串、列表、元组、字典 | 630 | | > >= == < <= | (1, 2, 3) < (2, 2, 3) | True | 元素比较 | 字符串、列表、元组 | 631 | 632 | **注意** 633 | 634 | - `in` 在对 **字典** 操作时,判断的是 **字典的键** 635 | - `in` 和 `not in` 被称为 **成员运算符** 636 | 637 | #### 成员运算符 638 | 639 | 成员运算符用于 **测试** 序列中是否包含指定的 **成员** 640 | 641 | | 运算符 | 描述 | 实例 | 642 | | ------ | ----------------------------------------------------- | --------------------------------- | 643 | | in | 如果在指定的序列中找到值返回 True,否则返回 False | `3 in (1, 2, 3)` 返回 `True` | 644 | | not in | 如果在指定的序列中没有找到值返回 True,否则返回 False | `3 not in (1, 2, 3)` 返回 `False` | 645 | 646 | 注意:在对 **字典** 操作时,判断的是 **字典的键** 647 | 648 | ### 完整的 for 循环语法 649 | 650 | - 在 `Python` 中完整的 `for 循环` 的语法如下: 651 | 652 | ``` 653 | for 变量 in 集合: 654 | 655 | 循环体代码 656 | else: 657 | 没有通过 break 退出循环,循环结束后,会执行的代码 658 | ``` 659 | 660 | #### 应用场景 661 | 662 | - 在 **迭代遍历** 嵌套的数据类型时,例如 **一个列表包含了多个字典** 663 | - 需求:要判断 某一个字典中 是否存在 指定的 值 664 | - 如果 **存在**,提示并且退出循环 665 | - 如果 **不存在**,在 **循环整体结束** 后,希望 **得到一个统一的提示** 666 | 667 | ``` 668 | students = [ 669 | {"name": "阿土", 670 | "age": 20, 671 | "gender": True, 672 | "height": 1.7, 673 | "weight": 75.0}, 674 | {"name": "小美", 675 | "age": 19, 676 | "gender": False, 677 | "height": 1.6, 678 | "weight": 45.0}, 679 | ] 680 | 681 | find_name = "阿土" 682 | 683 | for stu_dict in students: 684 | 685 | print(stu_dict) 686 | 687 | # 判断当前遍历的字典中姓名是否为find_name 688 | if stu_dict["name"] == find_name: 689 | print("找到了") 690 | 691 | # 如果已经找到,直接退出循环,就不需要再对后续的数据进行比较 692 | break 693 | 694 | else: 695 | print("没有找到") 696 | 697 | print("循环结束") 698 | ``` 699 | 700 | ### 练习 701 | 702 | #### 练习1:在屏幕上显示跑马灯文字。 703 | 704 | 参考答案: 705 | 706 | ``` 707 | import os 708 | import time 709 | 710 | 711 | def main(): 712 | content = '北京欢迎你为你开天辟地…………' 713 | while True: 714 | # 清理屏幕上的输出 715 | os.system('cls') # os.system('clear') 716 | print(content) 717 | # 休眠200毫秒 718 | time.sleep(0.2) 719 | content = content[1:] + content[0] 720 | 721 | 722 | if __name__ == '__main__': 723 | main() 724 | ``` 725 | 726 | #### 练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。 727 | 728 | 参考答案: 729 | 730 | ``` 731 | import random 732 | 733 | 734 | def generate_code(code_len=4): 735 | """ 736 | 生成指定长度的验证码 737 | 738 | :param code_len: 验证码的长度(默认4个字符) 739 | 740 | :return: 由大小写英文字母和数字构成的随机验证码 741 | """ 742 | all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 743 | last_pos = len(all_chars) - 1 744 | code = '' 745 | for _ in range(code_len): 746 | index = random.randint(0, last_pos) 747 | code += all_chars[index] 748 | return code 749 | ``` 750 | 751 | #### 练习3:设计一个函数返回给定文件名的后缀名。 752 | 753 | 参考答案: 754 | 755 | ``` 756 | def get_suffix(filename, has_dot=False): 757 | """ 758 | 获取文件名的后缀名 759 | 760 | :param filename: 文件名 761 | :param has_dot: 返回的后缀名是否需要带点 762 | :return: 文件的后缀名 763 | """ 764 | pos = filename.rfind('.') 765 | if 0 < pos < len(filename) - 1: 766 | index = pos if has_dot else pos + 1 767 | return filename[index:] 768 | else: 769 | return '' 770 | ``` 771 | 772 | #### 练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。 773 | 774 | 参考答案: 775 | 776 | ``` 777 | def max2(x): 778 | m1, m2 = (x[0], x[1]) if x[0] > x[1] else (x[1], x[0]) 779 | for index in range(2, len(x)): 780 | if x[index] > m1: 781 | m2 = m1 782 | m1 = x[index] 783 | elif x[index] > m2: 784 | m2 = x[index] 785 | return m1, m2 786 | ``` 787 | 788 | #### 练习5:计算指定的年月日是这一年的第几天。 789 | 790 | 参考答案: 791 | 792 | ``` 793 | def is_leap_year(year): 794 | """ 795 | 判断指定的年份是不是闰年 796 | 797 | :param year: 年份 798 | :return: 闰年返回True平年返回False 799 | """ 800 | return year % 4 == 0 and year % 100 != 0 or year % 400 == 0 801 | 802 | 803 | def which_day(year, month, date): 804 | """ 805 | 计算传入的日期是这一年的第几天 806 | 807 | :param year: 年 808 | :param month: 月 809 | :param date: 日 810 | :return: 第几天 811 | """ 812 | days_of_month = [ 813 | [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], 814 | [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 815 | ][is_leap_year(year)] 816 | total = 0 817 | for index in range(month - 1): 818 | total += days_of_month[index] 819 | return total + date 820 | 821 | 822 | def main(): 823 | print(which_day(1980, 11, 28)) 824 | print(which_day(1981, 12, 31)) 825 | print(which_day(2018, 1, 1)) 826 | print(which_day(2016, 3, 1)) 827 | 828 | 829 | if __name__ == '__main__': 830 | main() 831 | ``` 832 | 833 | #### 练习6:打印[杨辉三角](https://zh.wikipedia.org/wiki/杨辉三角形)。 834 | 835 | 参考答案: 836 | 837 | ``` 838 | def main(): 839 | num = int(input('Number of rows: ')) 840 | yh = [[]] * num 841 | for row in range(len(yh)): 842 | yh[row] = [None] * (row + 1) 843 | for col in range(len(yh[row])): 844 | if col == 0 or col == row: 845 | yh[row][col] = 1 846 | else: 847 | yh[row][col] = yh[row - 1][col] + yh[row - 1][col - 1] 848 | print(yh[row][col], end='\t') 849 | print() 850 | 851 | 852 | if __name__ == '__main__': 853 | main() 854 | ``` 855 | 856 | #### 案例1:双色球选号。 857 | 858 | ``` 859 | from random import randrange, randint, sample 860 | 861 | 862 | def display(balls): 863 | """ 864 | 输出列表中的双色球号码 865 | """ 866 | for index, ball in enumerate(balls): 867 | if index == len(balls) - 1: 868 | print('|', end=' ') 869 | print('%02d' % ball, end=' ') 870 | print() 871 | 872 | 873 | def random_select(): 874 | """ 875 | 随机选择一组号码 876 | """ 877 | red_balls = [x for x in range(1, 34)] 878 | selected_balls = [] 879 | selected_balls = sample(red_balls, 6) 880 | selected_balls.sort() 881 | selected_balls.append(randint(1, 16)) 882 | return selected_balls 883 | 884 | 885 | def main(): 886 | n = int(input('机选几注: ')) 887 | for _ in range(n): 888 | display(random_select()) 889 | 890 | 891 | if __name__ == '__main__': 892 | main() 893 | ``` 894 | 895 | > **说明:** 上面使用random模块的sample函数来实现从列表中选择不重复的n个元素。 896 | 897 | #### 综合案例2:[约瑟夫环问题](https://zh.wikipedia.org/wiki/约瑟夫斯问题)。 898 | 899 | ``` 900 | """ 901 | 《幸运的基督徒》 902 | 有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。 903 | """ 904 | 905 | 906 | def main(): 907 | persons = [True] * 30 908 | counter, index, number = 0, 0, 0 909 | while counter < 15: 910 | if persons[index]: 911 | number += 1 912 | if number == 9: 913 | persons[index] = False 914 | counter += 1 915 | number = 0 916 | index += 1 917 | index %= 30 918 | for person in persons: 919 | print('基' if person else '非', end='') 920 | 921 | 922 | if __name__ == '__main__': 923 | main() 924 | ``` 925 | 926 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 927 | -------------------------------------------------------------------------------- /Python基础连载/循环结构.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:Python循环结构 2 | 3 | ## Python循环结构 4 | 5 | ### 应用场景 6 | 7 | 我们在写程序的时候,一定会遇到需要重复执行某条或某些指令的场景。例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向移动的指令。在这个场景中,让机器人向球门方向移动就是一个需要重复的动作,当然这里还会用到上一课讲的分支结构来判断机器人是否持球以及是否进入射门范围。再举一个简单的例子,如果要实现每隔1秒中在屏幕上打印一次“hello, world”并持续打印一个小时,我们肯定不能够直接把`print('hello, world')`这句代码写3600遍,这里同样需要循环结构。 8 | 9 | 循环结构就是程序中控制某条或某些指令重复执行的结构。在Python中构造循环结构有两种做法,一种是`for-in`循环,一种是`while`循环。 10 | 11 | - 程序的三大流程 12 | - while 循环基本使用 13 | - break 和 continue 14 | - while 循环嵌套 15 | - for-in 16 | 17 | ## 程序的三大流程 18 | 19 | - 在程序开发中,一共有三种流程方式: 20 | 21 | - **顺序** —— **从上向下**,顺序执行代码 22 | - **分支** —— 根据条件判断,决定执行代码的 **分支** 23 | - **循环** —— 让 **特定代码 重复** 执行 24 | 25 | 26 | 27 | ![image-20210331223555736](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331223555736.png) 28 | 29 | ## `while` 循环基本使用 30 | 31 | - 循环的作用就是让 **指定的代码** 重复的执行 32 | - `while` 循环最常用的应用场景就是 **让执行的代码** 按照 **指定的次数** **重复** 执行 33 | - 需求 —— 打印 5 遍 `Hello Python` 34 | - 思考 —— 如果要求打印 100 遍怎么办? 35 | 36 | ### `while` 语句基本语法 37 | 38 | ``` 39 | 初始条件设置 —— 通常是重复执行的 计数器 40 | 41 | while 条件(判断 计数器 是否达到 目标次数): 42 | 条件满足时,做的事情1 43 | 条件满足时,做的事情2 44 | 条件满足时,做的事情3 45 | ...(省略)... 46 | 47 | 处理条件(计数器 + 1) 48 | ``` 49 | 50 | **注意**: 51 | 52 | - `while` 语句以及缩进部分是一个 **完整的代码块** 53 | 54 | #### 第一个 while 循环 55 | 56 | **需求** 57 | 58 | - 打印 5 遍 Hello Python 59 | 60 | ``` 61 | # 1. 定义重复次数计数器 62 | i = 1 63 | 64 | # 2. 使用 while 判断条件 65 | while i <= 5: 66 | # 要重复执行的代码 67 | print("Hello Python") 68 | 69 | # 处理计数器 i 70 | i = i + 1 71 | 72 | print("循环结束后的 i = %d" % i) 73 | ``` 74 | 75 | > 注意:循环结束后,之前定义的计数器条件的数值是依旧存在的 76 | 77 | #### 死循环 78 | 79 | > 由于程序员的原因,**忘记** 在循环内部 **修改循环的判断条件**,导致循环持续执行,程序无法终止! 80 | 81 | ### 赋值运算符 82 | 83 | - 在 Python 中,使用 `=` 可以给变量赋值 84 | - 在算术运算时,为了简化代码的编写,`Python` 还提供了一系列的 与 **算术运算符** 对应的 **赋值运算符** 85 | - 注意:**赋值运算符中间不能使用空格** 86 | 87 | | 运算符 | 描述 | 实例 | 88 | | ------ | -------------------------- | ------------------------------------- | 89 | | = | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c | 90 | | += | 加法赋值运算符 | c += a 等效于 c = c + a | 91 | | -= | 减法赋值运算符 | c -= a 等效于 c = c - a | 92 | | *= | 乘法赋值运算符 | c *= a 等效于 c = c * a | 93 | | /= | 除法赋值运算符 | c /= a 等效于 c = c / a | 94 | | //= | 取整除赋值运算符 | c //= a 等效于 c = c // a | 95 | | %= | 取 **模** (余数)赋值运算符 | c %= a 等效于 c = c % a | 96 | | **= | 幂赋值运算符 | c **= a 等效于 c = c ** a | 97 | 98 | ### Python 中的计数方法 99 | 100 | 常见的计数方法有两种,可以分别称为: 101 | 102 | - **自然计数法**(从 `1` 开始)—— 更符合人类的习惯 103 | - **程序计数法**(从 `0` 开始)—— 几乎所有的程序语言都选择从 0 开始计数 104 | 105 | 因此,大家在编写程序时,应该尽量养成习惯:**除非需求的特殊要求,否则 循环 的计数都从 0 开始** 106 | 107 | ### 循环计算 108 | 109 | > 在程序开发中,通常会遇到 **利用循环** **重复计算** 的需求 110 | 111 | 遇到这种需求,可以: 112 | 113 | 1. 在 `while` 上方定义一个变量,用于 **存放最终计算结果** 114 | 2. 在循环体内部,每次循环都用 **最新的计算结果**,**更新** 之前定义的变量 115 | 116 | **需求** 117 | 118 | - 计算 0 ~ 100 之间所有数字的累计求和结果 119 | 120 | ``` 121 | # 计算 0 ~ 100 之间所有数字的累计求和结果 122 | # 0. 定义最终结果的变量 123 | result = 0 124 | 125 | # 1. 定义一个整数的变量记录循环的次数 126 | i = 0 127 | 128 | # 2. 开始循环 129 | while i <= 100: 130 | print(i) 131 | 132 | # 每一次循环,都让 result 这个变量和 i 这个计数器相加 133 | result += i 134 | 135 | # 处理计数器 136 | i += 1 137 | 138 | print("0~100之间的数字求和结果 = %d" % result) 139 | ``` 140 | 141 | #### 需求进阶 142 | 143 | - 计算 0 ~ 100 之间 所有 **偶数** 的累计求和结果 144 | 145 | 开发步骤 146 | 147 | 1. 编写循环 **确认** **要计算的数字** 148 | 2. 添加 **结果** 变量,在循环内部 **处理计算结果** 149 | 150 | ``` 151 | # 0. 最终结果 152 | result = 0 153 | 154 | # 1. 计数器 155 | i = 0 156 | 157 | # 2. 开始循环 158 | while i <= 100: 159 | 160 | # 判断偶数 161 | if i % 2 == 0: 162 | print(i) 163 | result += i 164 | 165 | # 处理计数器 166 | i += 1 167 | 168 | print("0~100之间偶数求和结果 = %d" % result) 169 | ``` 170 | 171 | ## break 和 continue 172 | 173 | > `break` 和 `continue` 是专门在循环中使用的关键字 174 | 175 | - `break` **某一条件满足时**,退出循环,不再执行后续重复的代码 176 | - `continue` **某一条件满足时**,不执行后续重复的代码 177 | 178 | > `break` 和 `continue` 只针对 **当前所在循环** 有效 179 | 180 | ![image-20210331223647110](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331223647110.png) 181 | 182 | ### break 183 | 184 | - **在循环过程中**,如果 **某一个条件满足后**,**不** 再希望 **循环继续执行**,可以使用 `break` 退出循环 185 | 186 | ``` 187 | i = 0 188 | 189 | while i < 10: 190 | 191 | # break 某一条件满足时,退出循环,不再执行后续重复的代码 192 | # i == 3 193 | if i == 3: 194 | break 195 | 196 | print(i) 197 | 198 | i += 1 199 | 200 | print("over") 201 | ``` 202 | 203 | > `break` 只针对当前所在循环有效 204 | 205 | ### 3.2 continue 206 | 207 | - **在循环过程中**,如果 **某一个条件满足后**,**不** 希望 **执行循环代码,但是又不希望退出循环**,可以使用 `continue` 208 | - 也就是:在整个循环中,**只有某些条件**,不需要执行循环代码,而其他条件都需要执行 209 | 210 | ``` 211 | i = 0 212 | 213 | while i < 10: 214 | 215 | # 当 i == 7 时,不希望执行需要重复执行的代码 216 | if i == 7: 217 | # 在使用 continue 之前,同样应该修改计数器 218 | # 否则会出现死循环 219 | i += 1 220 | 221 | continue 222 | 223 | # 重复执行的代码 224 | print(i) 225 | 226 | i += 1 227 | ``` 228 | 229 | - 需要注意:使用 `continue` 时,**条件处理部分的代码,需要特别注意**,不小心会出现 **死循环** 230 | 231 | > `continue` 只针对当前所在循环有效 232 | 233 | ## `while` 循环嵌套 234 | 235 | ### 4.1 循环嵌套 236 | 237 | - `while` 嵌套就是:`while` 里面还有 `while` 238 | 239 | ``` 240 | while 条件 1: 241 | 条件满足时,做的事情1 242 | 条件满足时,做的事情2 243 | 条件满足时,做的事情3 244 | ...(省略)... 245 | 246 | while 条件 2: 247 | 条件满足时,做的事情1 248 | 条件满足时,做的事情2 249 | 条件满足时,做的事情3 250 | ...(省略)... 251 | 252 | 处理条件 2 253 | 254 | 处理条件 1 255 | ``` 256 | 257 | ### 循环嵌套演练 —— 九九乘法表 258 | 259 | #### 第 1 步:用嵌套打印小星星 260 | 261 | **需求** 262 | 263 | - 在控制台连续输出五行 `*`,每一行星号的数量依次递增 264 | 265 | ``` 266 | * 267 | ** 268 | *** 269 | **** 270 | ***** 271 | ``` 272 | 273 | - 使用字符串 * 打印 274 | 275 | ``` 276 | # 1. 定义一个计数器变量,从数字1开始,循环会比较方便 277 | row = 1 278 | 279 | while row <= 5: 280 | 281 | print("*" * row) 282 | 283 | row += 1 284 | ``` 285 | 286 | #### 第 2 步:使用循环嵌套打印小星星 287 | 288 | **知识点** 对 `print` 函数的使用做一个增强 289 | 290 | - 在默认情况下,`print` 函数输出内容之后,会自动在内容末尾增加换行 291 | - 如果不希望末尾增加换行,可以在 `print` 函数输出内容的后面增加 `, end=""` 292 | - 其中 `""` 中间可以指定 `print` 函数输出内容之后,继续希望显示的内容 293 | - 语法格式如下: 294 | 295 | ``` 296 | # 向控制台输出内容结束之后,不会换行 297 | print("*", end="") 298 | 299 | # 单纯的换行 300 | print("") 301 | ``` 302 | 303 | > `end=""` 表示向控制台输出内容结束之后,不会换行 304 | 305 | **假设** `Python` **没有提供** 字符串的 `*` 操作 **拼接字符串** 306 | 307 | **需求** 308 | 309 | - 在控制台连续输出五行 `*`,每一行星号的数量依次递增 310 | 311 | ``` 312 | * 313 | ** 314 | *** 315 | **** 316 | ***** 317 | ``` 318 | 319 | **开发步骤** 320 | 321 | - 1> 完成 5 行内容的简单输出 322 | 323 | - 2> 分析每行内部的 324 | 325 | 326 | 327 | ``` 328 | * 329 | ``` 330 | 331 | 332 | 333 | 应该如何处理? 334 | 335 | - 每行显示的星星和当前所在的行数是一致的 336 | - 嵌套一个小的循环,专门处理每一行中 `列` 的星星显示 337 | 338 | ``` 339 | row = 1 340 | 341 | while row <= 5: 342 | 343 | # 假设 python 没有提供字符串 * 操作 344 | # 在循环内部,再增加一个循环,实现每一行的 星星 打印 345 | col = 1 346 | 347 | while col <= row: 348 | print("*", end="") 349 | 350 | col += 1 351 | 352 | # 每一行星号输出完成后,再增加一个换行 353 | print("") 354 | 355 | row += 1 356 | ``` 357 | 358 | #### 第 3 步: 九九乘法表 359 | 360 | **需求** 输出 九九乘法表,格式如下: 361 | 362 | ``` 363 | 1 * 1 = 1 364 | 1 * 2 = 2 2 * 2 = 4 365 | 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 366 | 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 367 | 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25 368 | 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36 369 | 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49 370 | 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64 371 | 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81 372 | ``` 373 | 374 | **开发步骤** 375 | 376 | - 1. 打印 9 行小星星 377 | 378 | ``` 379 | * 380 | ** 381 | *** 382 | **** 383 | ***** 384 | ****** 385 | ******* 386 | ******** 387 | ********* 388 | ``` 389 | 390 | - 1. 将每一个 `*` 替换成对应的行与列相乘 391 | 392 | ``` 393 | # 定义起始行 394 | row = 1 395 | 396 | # 最大打印 9 行 397 | while row <= 9: 398 | # 定义起始列 399 | col = 1 400 | 401 | # 最大打印 row 列 402 | while col <= row: 403 | 404 | # end = "",表示输出结束后,不换行 405 | # "\t" 可以在控制台输出一个制表符,协助在输出文本时对齐 406 | print("%d * %d = %d" % (col, row, row * col), end="\t") 407 | 408 | # 列数 + 1 409 | col += 1 410 | 411 | # 一行打印完成的换行 412 | print("") 413 | 414 | # 行数 + 1 415 | row += 1 416 | ``` 417 | 418 | **字符串中的转义字符** 419 | 420 | - `\t` 在控制台输出一个 **制表符**,协助在输出文本时 **垂直方向** 保持对齐 421 | - `\n` 在控制台输出一个 **换行符** 422 | 423 | > **制表符** 的功能是在不使用表格的情况下在 **垂直方向** 按列对齐文本 424 | 425 | | 转义字符 | 描述 | 426 | | -------- | ---------- | 427 | | \\ | 反斜杠符号 | 428 | | \' | 单引号 | 429 | | \" | 双引号 | 430 | | \n | 换行 | 431 | | \t | 横向制表符 | 432 | | \r | 回车 | 433 | 434 | ### or-in循环 435 | 436 | 如果明确的知道循环执行的次数或者要对一个容器进行迭代(后面会讲到),那么我们推荐使用`for-in`循环,例如下面代码中计算1~100求和的结果($\displaystyle \sum \limits_{n=1}^{100}n$)。 437 | 438 | ``` 439 | """ 440 | 用for循环实现1~100求和 441 | 442 | """ 443 | 444 | sum = 0 445 | for x in range(101): 446 | sum += x 447 | print(sum) 448 | ``` 449 | 450 | 需要说明的是上面代码中的`range(1, 101)`可以用来构造一个从1到100的范围,当我们把这样一个范围放到`for-in`循环中,就可以通过前面的循环变量`x`依次取出从1到100的整数。当然,`range`的用法非常灵活,下面给出了一个例子: 451 | 452 | - `range(101)`:可以用来产生0到100范围的整数,需要注意的是取不到101。 453 | - `range(1, 101)`:可以用来产生1到100范围的整数,相当于前面是闭区间后面是开区间。 454 | - `range(1, 101, 2)`:可以用来产生1到100的奇数,其中2是步长,即每次数值递增的值。 455 | - `range(100, 0, -2)`:可以用来产生100到1的偶数,其中-2是步长,即每次数字递减的值。 456 | 457 | 知道了这一点,我们可以用下面的代码来实现1~100之间的偶数求和。 458 | 459 | ``` 460 | """ 461 | 用for循环实现1~100之间的偶数求和 462 | 463 | """ 464 | 465 | sum = 0 466 | for x in range(2, 101, 2): 467 | sum += x 468 | print(sum) 469 | ``` 470 | 471 | 当然,也可以通过在循环中使用分支结构的方式来实现相同的功能,代码如下所示。 472 | 473 | ``` 474 | """ 475 | 用for循环实现1~100之间的偶数求和 476 | 477 | """ 478 | 479 | sum = 0 480 | for x in range(1, 101): 481 | if x % 2 == 0: 482 | sum += x 483 | print(sum) 484 | ``` 485 | 486 | > **说明**:相较于上面直接跳过奇数的做法,下面这种做法很明显并不是很好的选择。 487 | 488 | ### 练习 489 | 490 | #### 练习1:输入一个正整数判断是不是素数。 491 | 492 | > **提示**:素数指的是只能被1和自身整除的大于1的整数。 493 | 494 | 参考答案: 495 | 496 | ``` 497 | """ 498 | 输入一个正整数判断它是不是素数 499 | 500 | """ 501 | from math import sqrt 502 | 503 | num = int(input('请输入一个正整数: ')) 504 | end = int(sqrt(num)) 505 | is_prime = True 506 | for x in range(2, end + 1): 507 | if num % x == 0: 508 | is_prime = False 509 | break 510 | if is_prime and num != 1: 511 | print('%d是素数' % num) 512 | else: 513 | print('%d不是素数' % num) 514 | ``` 515 | 516 | #### 练习2:输入两个正整数,计算它们的最大公约数和最小公倍数。 517 | 518 | > **提示**:两个数的最大公约数是两个数的公共因子中最大的那个数;两个数的最小公倍数则是能够同时被两个数整除的最小的那个数。 519 | 520 | 参考答案: 521 | 522 | ``` 523 | """ 524 | 输入两个正整数计算它们的最大公约数和最小公倍数 525 | 526 | """ 527 | 528 | x = int(input('x = ')) 529 | y = int(input('y = ')) 530 | # 如果x大于y就交换x和y的值 531 | if x > y: 532 | # 通过下面的操作将y的值赋给x, 将x的值赋给y 533 | x, y = y, x 534 | # 从两个数中较的数开始做递减的循环 535 | for factor in range(x, 0, -1): 536 | if x % factor == 0 and y % factor == 0: 537 | print('%d和%d的最大公约数是%d' % (x, y, factor)) 538 | print('%d和%d的最小公倍数是%d' % (x, y, x * y // factor)) 539 | break 540 | ``` 541 | 542 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 543 | -------------------------------------------------------------------------------- /Python基础连载/文件和异常.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:文件和异常 2 | 3 | ## 文件的概念 4 | 5 | ### 文件的概念和作用 6 | 7 | - 计算机的 **文件**,就是存储在某种 **长期储存设备** 上的一段 **数据** 8 | - 长期存储设备包括:硬盘、U 盘、移动硬盘、光盘... 9 | 10 | **文件的作用** 11 | 12 | 将数据长期保存下来,在需要的时候使用 13 | 14 | ### 文件的存储方式 15 | 16 | - 在计算机中,文件是以 **二进制** 的方式保存在磁盘上的 17 | 18 | #### 文本文件和二进制文件 19 | 20 | - 文本文件 21 | - 可以使用 **文本编辑软件** 查看 22 | - 本质上还是二进制文件 23 | - 例如:python 的源程序 24 | - 二进制文件 25 | - 保存的内容 不是给人直接阅读的,而是 **提供给其他软件使用的** 26 | - 例如:图片文件、音频文件、视频文件等等 27 | - 二进制文件不能使用 **文本编辑软件** 查看 28 | 29 | ## 文件的基本操作 30 | 31 | ### 2.1 操作文件的套路 32 | 33 | 在 **计算机** 中要操作文件的套路非常固定,一共包含**三个步骤**: 34 | 35 | 1. 打开文件 36 | 2. 读、写文件 37 | - **读** 将文件内容读入内存 38 | - **写** 将内存内容写入文件 39 | 3. 关闭文件 40 | 41 | ### 2.2 操作文件的函数/方法 42 | 43 | - 在 `Python` 中要操作文件需要记住 1 个函数和 3 个方法 44 | 45 | | 序号 | 函数/方法 | 说明 | 46 | | ---- | --------- | ------------------------------ | 47 | | 01 | open | 打开文件,并且返回文件操作对象 | 48 | | 02 | read | 将文件内容读取到内存 | 49 | | 03 | write | 将指定内容写入文件 | 50 | | 04 | close | 关闭文件 | 51 | 52 | - `open` 函数负责打开文件,并且返回文件对象 53 | - `read`/`write`/`close` 三个方法都需要通过 **文件对象** 来调用 54 | 55 | ### 2.3 read 方法 —— 读取文件 56 | 57 | - open函数的第一个参数是要打开的文件名(文件名区分大小写) 58 | - 如果文件 **存在**,返回 **文件操作对象** 59 | - 如果文件 **不存在**,会 **抛出异常** 60 | - `read` 方法可以一次性 **读入** 并 **返回** 文件的 **所有内容** 61 | - close方法负责关闭文件 62 | - 如果 **忘记关闭文件**,**会造成系统资源消耗,而且会影响到后续对文件的访问** 63 | - **注意**:`read` 方法执行后,会把 **文件指针** 移动到 **文件的末尾** 64 | 65 | ``` 66 | # 1. 打开 - 文件名需要注意大小写 67 | file = open("README") 68 | 69 | # 2. 读取 70 | text = file.read() 71 | print(text) 72 | 73 | # 3. 关闭 74 | file.close() 75 | ``` 76 | 77 | **提示** 78 | 79 | - 在开发中,通常会先编写 **打开** 和 **关闭** 的代码,再编写中间针对文件的 **读/写** 操作! 80 | 81 | #### 文件指针(知道) 82 | 83 | - **文件指针** 标记 **从哪个位置开始读取数据** 84 | - **第一次打开** 文件时,通常 **文件指针会指向文件的开始位置** 85 | - 当执行了read方法后,文件指针会移动到读取内容的末尾 86 | - 默认情况下会移动到 **文件末尾** 87 | 88 | **思考** 89 | 90 | - 如果执行了一次 `read` 方法,读取了所有内容,那么再次调用 `read` 方法,还能够获得到内容吗? 91 | 92 | **答案** 93 | 94 | - 不能 95 | - 第一次读取之后,文件指针移动到了文件末尾,再次调用不会读取到任何的内容 96 | 97 | ### 打开文件的方式 98 | 99 | - `open` 函数默认以 **只读方式** 打开文件,并且返回文件对象 100 | 101 | 语法如下: 102 | 103 | ``` 104 | f = open("文件名", "访问方式") 105 | ``` 106 | 107 | | 访问方式 | 说明 | 108 | | -------- | ------------------------------------------------------------ | 109 | | r | 以**只读**方式打开文件。文件的指针将会放在文件的开头,这是**默认模式**。如果文件不存在,抛出异常 | 110 | | w | 以**只写**方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 | 111 | | a | 以**追加**方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 | 112 | | r+ | 以**读写**方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常 | 113 | | w+ | 以**读写**方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 | 114 | | a+ | 以**读写**方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 | 115 | 116 | **提示** 117 | 118 | - 频繁的移动文件指针,**会影响文件的读写效率**,开发中更多的时候会以 **只读**、**只写** 的方式来操作文件 119 | 120 | **写入文件示例** 121 | 122 | ``` 123 | # 打开文件 124 | f = open("README", "w") 125 | 126 | f.write("hello python!\n") 127 | f.write("今天天气真好") 128 | 129 | # 关闭文件 130 | f.close() 131 | ``` 132 | 133 | ### 按行读取文件内容 134 | 135 | - `read` 方法默认会把文件的 **所有内容** **一次性读取到内存** 136 | - 如果文件太大,对内存的占用会非常严重 137 | 138 | #### `readline` 方法 139 | 140 | - `readline` 方法可以一次读取一行内容 141 | - 方法执行后,会把 **文件指针** 移动到下一行,准备再次读取 142 | 143 | **读取大文件的正确姿势** 144 | 145 | ``` 146 | # 打开文件 147 | file = open("README") 148 | 149 | while True: 150 | # 读取一行内容 151 | text = file.readline() 152 | 153 | # 判断是否读到内容 154 | if not text: 155 | break 156 | 157 | # 每读取一行的末尾已经有了一个 `\n` 158 | print(text, end="") 159 | 160 | # 关闭文件 161 | file.close() 162 | ``` 163 | 164 | ### 2.6 文件读写案例 —— 复制文件 165 | 166 | **目标** 167 | 168 | 用代码的方式,来实现文件复制过程 169 | 170 | [![025_复制文件-w441](https://github.com/hyh1750522171/bigData/raw/master/He%20Yihao/python%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/media/15019810951755/025_%E5%A4%8D%E5%88%B6%E6%96%87%E4%BB%B6.png)](https://github.com/hyh1750522171/bigData/blob/master/He Yihao/python面向对象/media/15019810951755/025_复制文件.png) 171 | 172 | #### 小文件复制 173 | 174 | - 打开一个已有文件,读取完整内容,并写入到另外一个文件 175 | 176 | ``` 177 | # 1. 打开文件 178 | file_read = open("README") 179 | file_write = open("README[复件]", "w") 180 | 181 | # 2. 读取并写入文件 182 | text = file_read.read() 183 | file_write.write(text) 184 | 185 | # 3. 关闭文件 186 | file_read.close() 187 | file_write.close() 188 | ``` 189 | 190 | #### 大文件复制 191 | 192 | - 打开一个已有文件,逐行读取内容,并顺序写入到另外一个文件 193 | 194 | ``` 195 | # 1. 打开文件 196 | file_read = open("README") 197 | file_write = open("README[复件]", "w") 198 | 199 | # 2. 读取并写入文件 200 | while True: 201 | # 每次读取一行 202 | text = file_read.readline() 203 | 204 | # 判断是否读取到内容 205 | if not text: 206 | break 207 | 208 | file_write.write(text) 209 | 210 | # 3. 关闭文件 211 | file_read.close() 212 | file_write.close() 213 | ``` 214 | 215 | ## 异常 216 | 217 | ## 异常的概念 218 | 219 | - 程序在运行时,如果 `Python 解释器` **遇到** 到一个错误,**会停止程序的执行,并且提示一些错误信息**,这就是 **异常** 220 | - **程序停止执行并且提示错误信息** 这个动作,我们通常称之为:**抛出(raise)异常** 221 | 222 | ## 捕获异常 223 | 224 | ### 简单的捕获异常语法 225 | 226 | - 在程序开发中,如果 **对某些代码的执行不能确定是否正确**,可以增加 `try(尝试)` 来 **捕获异常** 227 | - 捕获异常最简单的语法格式: 228 | 229 | ``` 230 | try: 231 | 尝试执行的代码 232 | except: 233 | 出现错误的处理 234 | ``` 235 | 236 | - `try` **尝试**,下方编写要尝试代码,不确定是否能够正常执行的代码 237 | - `except` **如果不是**,下方编写尝试失败的代码 238 | 239 | #### 简单异常捕获演练 —— 要求用户输入整数 240 | 241 | ``` 242 | try: 243 | # 提示用户输入一个数字 244 | num = int(input("请输入数字:")) 245 | except: 246 | print("请输入正确的数字") 247 | ``` 248 | 249 | ### 错误类型捕获 250 | 251 | - 在程序执行时,可能会遇到 **不同类型的异常**,并且需要 **针对不同类型的异常,做出不同的响应**,这个时候,就需要捕获错误类型了 252 | - 语法如下: 253 | 254 | ``` 255 | try: 256 | # 尝试执行的代码 257 | pass 258 | except 错误类型1: 259 | # 针对错误类型1,对应的代码处理 260 | pass 261 | except (错误类型2, 错误类型3): 262 | # 针对错误类型2 和 3,对应的代码处理 263 | pass 264 | except Exception as result: 265 | print("未知错误 %s" % result) 266 | ``` 267 | 268 | - 当 `Python` 解释器 **抛出异常** 时,**最后一行错误信息的第一个单词,就是错误类型** 269 | 270 | #### 异常类型捕获演练 —— 要求用户输入整数 271 | 272 | **需求** 273 | 274 | 1. 提示用户输入一个整数 275 | 2. 使用 `8` 除以用户输入的整数并且输出 276 | 277 | ``` 278 | try: 279 | num = int(input("请输入整数:")) 280 | result = 8 / num 281 | print(result) 282 | except ValueError: 283 | print("请输入正确的整数") 284 | except ZeroDivisionError: 285 | print("除 0 错误") 286 | ``` 287 | 288 | #### 捕获未知错误 289 | 290 | - 在开发时,**要预判到所有可能出现的错误**,还是有一定难度的 291 | - 如果希望程序 **无论出现任何错误**,都不会因为 `Python` 解释器 **抛出异常而被终止**,可以再增加一个 `except` 292 | 293 | 语法如下: 294 | 295 | ``` 296 | except Exception as result: 297 | print("未知错误 %s" % result) 298 | ``` 299 | 300 | ### 异常捕获完整语法 301 | 302 | - 在实际开发中,为了能够处理复杂的异常情况,完整的异常语法如下: 303 | 304 | > 提示: 305 | > 306 | > - 有关完整语法的应用场景,在后续学习中,**结合实际的案例**会更好理解 307 | > - 现在先对这个语法结构有个印象即可 308 | 309 | ``` 310 | try: 311 | # 尝试执行的代码 312 | pass 313 | except 错误类型1: 314 | # 针对错误类型1,对应的代码处理 315 | pass 316 | except 错误类型2: 317 | # 针对错误类型2,对应的代码处理 318 | pass 319 | except (错误类型3, 错误类型4): 320 | # 针对错误类型3 和 4,对应的代码处理 321 | pass 322 | except Exception as result: 323 | # 打印错误信息 324 | print(result) 325 | else: 326 | # 没有异常才会执行的代码 327 | pass 328 | finally: 329 | # 无论是否有异常,都会执行的代码 330 | print("无论是否有异常,都会执行的代码") 331 | ``` 332 | 333 | - `else` 只有在没有异常时才会执行的代码 334 | - `finally` 无论是否有异常,都会执行的代码 335 | - 之前一个演练的 **完整捕获异常** 的代码如下: 336 | 337 | ``` 338 | try: 339 | num = int(input("请输入整数:")) 340 | result = 8 / num 341 | print(result) 342 | except ValueError: 343 | print("请输入正确的整数") 344 | except ZeroDivisionError: 345 | print("除 0 错误") 346 | except Exception as result: 347 | print("未知错误 %s" % result) 348 | else: 349 | print("正常执行") 350 | finally: 351 | print("执行完成,但是不保证正确") 352 | ``` 353 | 354 | ## 异常的传递 355 | 356 | - **异常的传递** —— 当 **函数/方法** 执行 **出现异常**,会 **将异常传递** 给 函数/方法 的 **调用一方** 357 | - 如果 **传递到主程序**,仍然 **没有异常处理**,程序才会被终止 358 | 359 | > 提示 360 | 361 | - 在开发中,可以在主函数中增加 **异常捕获** 362 | - 而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的 **异常捕获** 中 363 | - 这样就不需要在代码中,增加大量的 **异常捕获**,能够保证代码的整洁 364 | 365 | **需求** 366 | 367 | 1. 定义函数 `demo1()` **提示用户输入一个整数并且返回** 368 | 2. 定义函数 `demo2()` 调用 `demo1()` 369 | 3. 在主程序中调用 `demo2()` 370 | 371 | ``` 372 | def demo1(): 373 | return int(input("请输入一个整数:")) 374 | 375 | 376 | def demo2(): 377 | return demo1() 378 | 379 | try: 380 | print(demo2()) 381 | except ValueError: 382 | print("请输入正确的整数") 383 | except Exception as result: 384 | print("未知错误 %s" % result) 385 | ``` 386 | 387 | ## 抛出 `raise` 异常 388 | 389 | ### 应用场景 390 | 391 | - 在开发中,除了 **代码执行出错** `Python` 解释器会 **抛出** 异常之外 392 | - 还可以根据 **应用程序** **特有的业务需求** **主动抛出异常** 393 | 394 | **示例** 395 | 396 | - 提示用户 **输入密码**,如果 **长度少于 8**,抛出 **异常** 397 | 398 | ![image-20210401165707052](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401165707052.png) 399 | 400 | **注意** 401 | 402 | - 当前函数 **只负责** 提示用户输入密码,如果 **密码长度不正确,需要其他的函数进行额外处理** 403 | - 因此可以 **抛出异常**,由其他需要处理的函数 **捕获异常** 404 | 405 | ### 抛出异常 406 | 407 | - `Python` 中提供了一个 `Exception` **异常类** 408 | - 在开发时,如果满足特定业务需求时,希望抛出异常,可以: 409 | 1. **创建** 一个 `Exception` 的 **对象** 410 | 2. 使用 `raise` **关键字** 抛出 **异常对象** 411 | 412 | **需求** 413 | 414 | - 定义 `input_password` 函数,提示用户输入密码 415 | - 如果用户输入长度 < 8,抛出异常 416 | - 如果用户输入长度 >=8,返回输入的密码 417 | 418 | ``` 419 | def input_password(): 420 | 421 | # 1. 提示用户输入密码 422 | pwd = input("请输入密码:") 423 | 424 | # 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码 425 | if len(pwd) >= 8: 426 | return pwd 427 | 428 | # 3. 密码长度不够,需要抛出异常 429 | # 1> 创建异常对象 - 使用异常的错误信息字符串作为参数 430 | ex = Exception("密码长度不够") 431 | 432 | # 2> 抛出异常对象 433 | raise ex 434 | 435 | 436 | try: 437 | user_pwd = input_password() 438 | print(user_pwd) 439 | except Exception as result: 440 | print("发现错误:%s" % result) 441 | ``` 442 | 443 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 444 | -------------------------------------------------------------------------------- /Python基础连载/综合练习.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:综合练习 2 | 3 | 学完前面的几个章节后,我觉得有必要在这里带大家做一些练习来巩固之前所学的知识,虽然迄今为止我们学习的内容只是Python的冰山一角,但是这些内容已经足够我们来构建程序中的逻辑。对于编程语言的初学者来说,在学习了Python的核心语言元素(变量、类型、运算符、表达式、分支结构、循环结构等)之后,必须做的一件事情就是尝试用所学知识去解决现实中的问题,换句话说就是锻炼自己把用人类自然语言描述的算法(解决问题的方法和步骤)翻译成Python代码的能力,而这件事情必须通过大量的练习才能达成。 4 | 5 | 我们在本章为大家整理了一些经典的案例和习题,希望通过这些例子,一方面帮助大家巩固之前所学的Python知识,另一方面帮助大家了解如何建立程序中的逻辑以及如何运用一些简单的算法解决现实中的问题。 6 | 7 | ### 经典的例子 8 | 9 | 1. 寻找**水仙花数**。 10 | 11 | > **说明**:水仙花数也被称为超完全数字不变数、自恋数、自幂数、阿姆斯特朗数,它是一个3位数,该数字每个位上数字的立方之和正好等于它本身,例如:$1^3 + 5^3+ 3^3=153$。 12 | 13 | ``` 14 | """ 15 | 找出所有水仙花数 16 | 17 | """ 18 | 19 | for num in range(100, 1000): 20 | low = num % 10 21 | mid = num // 10 % 10 22 | high = num // 100 23 | if num == low ** 3 + mid ** 3 + high ** 3: 24 | print(num) 25 | ``` 26 | 27 | 在上面的代码中,我们通过整除和求模运算分别找出了一个三位数的个位、十位和百位,这种小技巧在实际开发中还是常用的。用类似的方法,我们还可以实现将一个正整数反转,例如:将12345变成54321,代码如下所示。 28 | 29 | ``` 30 | """ 31 | 正整数的反转 32 | """ 33 | 34 | num = int(input('num = ')) 35 | reversed_num = 0 36 | while num > 0: 37 | reversed_num = reversed_num * 10 + num % 10 38 | num //= 10 39 | print(reversed_num) 40 | ``` 41 | 42 | 2. **百钱百鸡**问题。 43 | 44 | > **说明**:百钱百鸡是我国古代数学家[张丘建](https://baike.baidu.com/item/张丘建/10246238)在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?翻译成现代文是:公鸡5元一只,母鸡3元一只,小鸡1元三只,用100块钱买一百只鸡,问公鸡、母鸡、小鸡各有多少只? 45 | 46 | ``` 47 | for x in range(0, 20): 48 | for y in range(0, 33): 49 | z = 100 - x - y 50 | if 5 * x + 3 * y + z / 3 == 100: 51 | print('公鸡: %d只, 母鸡: %d只, 小鸡: %d只' % (x, y, z)) 52 | ``` 53 | 54 | 上面使用的方法叫做**穷举法**,也称为**暴力搜索法**,这种方法通过一项一项的列举备选解决方案中所有可能的候选项并检查每个候选项是否符合问题的描述,最终得到问题的解。这种方法看起来比较笨拙,但对于运算能力非常强大的计算机来说,通常都是一个可行的甚至是不错的选择,而且问题的解如果存在,这种方法一定能够找到它。 55 | 56 | 3. **CRAPS赌博游戏**。 57 | 58 | > **说明**:CRAPS又称花旗骰,是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子,玩家通过摇两粒骰子获得点数进行游戏。简单的规则是:玩家第一次摇骰子如果摇出了7点或11点,玩家胜;玩家第一次如果摇出2点、3点或12点,庄家胜;其他点数玩家继续摇骰子,如果玩家摇出了7点,庄家胜;如果玩家摇出了第一次摇的点数,玩家胜;其他点数,玩家继续要骰子,直到分出胜负。 59 | 60 | ``` 61 | """ 62 | Craps赌博游戏 63 | 我们设定玩家开始游戏时有1000元的赌注 64 | 游戏结束的条件是玩家输光所有的赌注 65 | """ 66 | from random import randint 67 | 68 | money = 1000 69 | while money > 0: 70 | print('你的总资产为:', money) 71 | needs_go_on = False 72 | while True: 73 | debt = int(input('请下注: ')) 74 | if 0 < debt <= money: 75 | break 76 | first = randint(1, 6) + randint(1, 6) 77 | print('玩家摇出了%d点' % first) 78 | if first == 7 or first == 11: 79 | print('玩家胜!') 80 | money += debt 81 | elif first == 2 or first == 3 or first == 12: 82 | print('庄家胜!') 83 | money -= debt 84 | else: 85 | needs_go_on = True 86 | while needs_go_on: 87 | needs_go_on = False 88 | current = randint(1, 6) + randint(1, 6) 89 | print('玩家摇出了%d点' % current) 90 | if current == 7: 91 | print('庄家胜') 92 | money -= debt 93 | elif current == first: 94 | print('玩家胜') 95 | money += debt 96 | else: 97 | needs_go_on = True 98 | print('你破产了, 游戏结束!') 99 | ``` 100 | 101 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 102 | -------------------------------------------------------------------------------- /Python基础连载/认识Python.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:认识Python 2 | 3 | ## 认识Python 4 | 5 | > 本章目录: 6 | 7 | + Python简介-历史/优缺点 8 | + 搭建编程环境 9 | + 第一个Python程序 10 | + 注释 11 | 12 | #### Python起源 13 | 14 | 1. 1989 年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的**解释程序**,作为 ABC 语言的一种继承(**感觉下什么叫牛人**) 15 | 2. ABC 是由吉多参加设计的一种教学语言,就吉多本人看来,ABC 这种语言非常优美和强大,是**专门为非专业程序员设计的**。但是 ABC 语言并没有成功,究其原因,吉多认为是**非开放**造成的。吉多决心在 Python 中避免这一错误,并获取了非常好的效果 16 | 3. 之所以选中 Python(蟒蛇) 作为程序的名字,是因为他是 BBC 电视剧——蒙提·派森的飞行马戏团(Monty Python's Flying Circus)的爱好者 17 | 4. 1991 年,第一个 Python **解释器** 诞生,它是用 C 语言实现的,并能够调用 C 语言的库文件 18 | 19 | > 在这里我们要去了解一下什么是解析器 20 | 21 | ![image-20210331164735838](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331164735838.png) 22 | 23 | - **编译型语言**:程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如 C、C++ 24 | - **解释型语言**:解释型语言编写的程序不进行预先编译,以文本方式存储程序代码,会将代码一句一句直接运行。在发布程序时,看起来省了道编译工序,但是在运行程序的时候,必须先解释再运行 25 | 26 | #### 编译型语言和解释型语言对比 27 | 28 | - **速度** —— 编译型语言比解释型语言执行速度快 29 | - **跨平台性** —— 解释型语言比编译型语言跨平台性好 30 | 31 | #### Python的特点 32 | 33 | - Python 是 34 | 35 | 完全面向对象的语言 36 | 37 | - **函数**、**模块**、**数字**、**字符串**都是对象,**在 Python 中一切皆对象** 38 | - 完全支持继承、重载、多重继承 39 | - 支持重载运算符,也支持泛型设计 40 | 41 | - Python **拥有一个强大的标准库**,Python 语言的核心只包含 **数字**、**字符串**、**列表**、**字典**、**文件** 等常见类型和函数,而由 Python 标准库提供了 **系统管理**、**网络通信**、**文本处理**、**数据库接口**、**图形系统**、**XML 处理** 等额外的功能 42 | 43 | - Python 社区提供了**大量的第三方模块**,使用方式与标准库类似。它们的功能覆盖 **科学计算**、**人工智能**、**机器学习**、**Web 开发**、**数据库接口**、**图形系统** 多个领域 44 | 45 | #### Python的优缺点 46 | 47 | Python的优点很多,简单的可以总结为以下几点。 48 | 49 | 1. 简单明了,学习曲线低,比很多编程语言都容易上手。 50 | 2. 开放源代码,拥有强大的社区和生态圈,尤其是在数据分析和机器学习领域。 51 | 3. 解释型语言,天生具有平台可移植性,代码可以工作于不同的操作系统。 52 | 4. 对两种主流的编程范式(面向对象编程和函数式编程)都提供了支持。 53 | 5. 代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。 54 | 55 | Python的缺点主要集中在以下几点。 56 | 57 | 1. 执行效率稍低,对执行效率要求高的部分可以由其他语言(如:C、C++)编写。 58 | 2. 代码无法加密,但是现在很多公司都不销售卖软件而是销售服务,这个问题会被弱化。 59 | 3. 在开发时可以选择的框架太多(如Web框架就有100多个),有选择的地方就有错误。 60 | 61 | #### Python的应用领域 62 | 63 | Python在Web应用后端开发、云基础设施建设、DevOps、网络数据采集(爬虫)、自动化测试、数据分析、机器学习等领域都有着广泛的应用。 64 | 65 | #### 搭建编译环境 66 | 67 | > 介绍一下Python有什么解释器 68 | 69 | **Python 的解释器** 如今有多个语言的实现,包括: 70 | 71 | - `CPython` —— 官方版本的 C 语言实现 72 | - `Jython` —— 可以运行在 Java 平台 73 | - `IronPython` —— 可以运行在 .NET 和 Mono 平台 74 | - `PyPy` —— Python 实现的,支持 JIT 即时编译 75 | 76 | ### 交互式运行 Python 程序 77 | 78 | - 直接在终端中运行解释器,而不输入要执行的文件名 79 | - 在 Python 的 `Shell` 中直接输入 **Python 的代码**,会立即看到程序执行结果 80 | 81 | #### 1) 交互式运行 Python 的优缺点 82 | 83 | ##### 优点 84 | 85 | - 适合于学习/验证 Python 语法或者局部代码 86 | 87 | ##### 缺点 88 | 89 | - 代码不能保存 90 | - 不适合运行太大的程序 91 | 92 | #### Windows环境 93 | 94 | > 安装 95 | 96 | 97 | 98 | 首先,检查你的系统是否安装了Python。为此,在“开始”菜单中输入command 并按回车以打开一个命令窗口;你也可按住Shift键并右击桌面,再选择“在此处打开命令窗口”。在终 99 | 100 | 端窗口中输入python并按回车;如果出现了Python提示符(>>> ),就说明你的系统安装了Python。然而,你也可能会看到一条错误消息,指出python 是无法识别的命令。 101 | 102 | 如果是这样,就需要下载Windows Python安装程序。为此,请访问http://python.org/downloads/ 。你将看到两个按钮,分别用于下载Python 3和Python 2。单击用于下载Python 3的按 103 | 104 | 钮,这会根据你的系统自动下载正确的安装程序。下载安装程序后,运行它。请务必选中复选框Add Python to PATH(如图1-2所示),这让你能够更轻松地配置系统。 105 | 106 | ![image-20210331174038790](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331174038790.png) 107 | 108 | 109 | 110 | 如果系统显示api-ms-win-crt*.dll文件缺失,可以参照[《api-ms-win-crt*.dll缺失原因分析和解决方法》](https://zhuanlan.zhihu.com/p/32087135)一文讲解的方法进行处理或者直接在[微软官网](https://www.microsoft.com/zh-cn/download/details.aspx?id=48145)下载Visual C++ Redistributable for Visual Studio 2015文件进行修复;如果是因为更新Windows的DirectX之后导致某些动态链接库文件缺失问题,可以下载一个[DirectX修复工具](https://dl.pconline.com.cn/download/360074-1.html)进行修复。 111 | 112 | **2.** 启动Python终端 113 | 114 | 通过配置系统,让其能够在终端会话中运行Python,可简化文本编辑器的配置工作。打开一个命令窗口,并在其中执行命令python 。如果出现了Python提示符(>>> ),就说明 115 | 116 | Windows找到了你刚安装的Python版本。 117 | 118 | #### Linux环境 119 | 120 | Linux环境自带了Python 2.x版本,但是如果要更新到3.x的版本,可以在[Python的官方网站](https://www.python.org/)下载Python的源代码并通过源代码构建安装的方式进行安装,具体的步骤如下所示(以CentOS为例)。 121 | 122 | 1. 安装依赖库(因为没有这些依赖库可能在源代码构件安装时因为缺失底层依赖库而失败)。 123 | 124 | ``` 125 | yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel 126 | ``` 127 | 128 | 1. 下载Python源代码并解压缩到指定目录。 129 | 130 | ``` 131 | wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tar.xz 132 | xz -d Python-3.7.6.tar.xz 133 | tar -xvf Python-3.7.6.tar 134 | ``` 135 | 136 | 1. 切换至Python源代码目录并执行下面的命令进行配置和安装。 137 | 138 | ``` 139 | cd Python-3.7.6 140 | ./configure --prefix=/usr/local/python37 --enable-optimizations 141 | make && make install 142 | ``` 143 | 144 | 1. 修改用户主目录下名为.bash_profile的文件,配置PATH环境变量并使其生效。 145 | 146 | ``` 147 | cd ~ 148 | vim .bash_profile 149 | # ... 此处省略上面的代码 ... 150 | 151 | export PATH=$PATH:/usr/local/python37/bin 152 | 153 | # ... 此处省略下面的代码 ... 154 | ``` 155 | 156 | 1. 激活环境变量。 157 | 158 | ``` 159 | source .bash_profile 160 | ``` 161 | 162 | #### macOS环境 163 | 164 | macOS也自带了Python 2.x版本,可以通过[Python的官方网站](https://www.python.org/)提供的安装文件(pkg文件)安装Python 3.x的版本。默认安装完成后,可以通过在终端执行`python`命令来启动2.x版本的Python解释器,启动3.x版本的Python解释器需要执行`python3`命令。 165 | 166 | ### 运行Python程序 167 | 168 | #### 确认Python的版本 169 | 170 | 可以Windows的命令行提示符中键入下面的命令。 171 | 172 | ``` 173 | python --version 174 | ``` 175 | 176 | ![image-20210331165308176](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331165308176.png) 177 | 178 | 在Linux或macOS系统的终端中键入下面的命令。 179 | 180 | ``` 181 | python3 --version 182 | ``` 183 | 184 | 当然也可以先输入`python`或`python3`进入交互式环境,再执行以下的代码检查Python的版本。 185 | 186 | ``` 187 | import sys 188 | 189 | print(sys.version_info) 190 | print(sys.version) 191 | ``` 192 | 193 | ![image-20210331165439132](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331165439132.png) 194 | 195 | ### 第一个Python程序 196 | 197 | #### 我们可以现在Python自带的idle中写我们的代码 198 | 199 | ![image-20210331165938038](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331165938038.png) 200 | 201 | #### PyCharm - Python开发神器 202 | 203 | ### 1) 集成开发环境(IDE) 204 | 205 | 集成开发环境(`IDE`,Integrated Development Environment)—— **集成了开发软件需要的所有工具**,一般包括以下工具: 206 | 207 | - 图形用户界面 208 | - 代码编辑器(支持 **代码补全**/**自动缩进**) 209 | - 编译器/解释器 210 | - 调试器(**断点**/**单步执行**) 211 | - …… 212 | 213 | ### 2)PyCharm 介绍 214 | 215 | - `PyCharm` 是 Python 的一款非常优秀的集成开发环境 216 | 217 | - `PyCharm` 除了具有一般 IDE 所必备功能外,还可以在 `Windows`、`Linux`、`macOS` 下使用 218 | 219 | - ``` 220 | PyCharm 221 | ``` 222 | 223 | 224 | 225 | 适合开发大型项目 226 | 227 | - 一个项目通常会包含 **很多源文件** 228 | - 每个 **源文件** 的代码行数是有限的,通常在几百行之内 229 | - 每个 **源文件** 各司其职,共同完成复杂的业务功能 230 | 231 | ![image-20210331173353219](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331173353219.png) 232 | 233 | - **文件导航区域** 能够 **浏览**/**定位**/**打开** 项目文件 234 | 235 | - **文件编辑区域** 能够 **编辑** 当前打开的文件 236 | 237 | - 控制台区域 238 | 239 | 240 | 241 | 能够: 242 | 243 | - 输出程序执行内容 244 | - 跟踪调试代码的执行 245 | 246 | - 右上角的 **工具栏** 能够 **执行(SHIFT + F10)** / **调试(SHIFT + F9)** 代码 247 | 248 | ![image-20210331173422242](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331173422242.png) 249 | 250 | - 通过控制台上方的**单步执行按钮(F8)**,可以单步执行代码 251 | 252 | ![image-20210331173444243](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331173444243.png) 253 | 254 | 255 | 256 | ### 注释 257 | 258 | - 注释的作用 259 | - 单行注释(行注释) 260 | - 多行注释(块注释) 261 | 262 | ## 01. 注释的作用 263 | 264 | > 使用用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序的可读性 265 | 266 | ## 单行注释(行注释) 267 | 268 | - 以 `#` 开头,`#` 右边的所有东西都被当做说明文字,而不是真正要执行的程序,只起到辅助说明作用 269 | - 示例代码如下: 270 | 271 | ``` 272 | # 这是第一个单行注释 273 | print("hello python") 274 | ``` 275 | 276 | > 为了保证代码的可读性,`#` 后面建议先添加一个空格,然后再编写相应的说明文字 277 | 278 | ### 在代码后面增加的单行注释 279 | 280 | - 在程序开发时,同样可以使用 `#` 在代码的后面(旁边)增加说明性的文字 281 | - 但是,需要注意的是,**为了保证代码的可读性**,**注释和代码之间** 至少要有 **两个空格** 282 | - 示例代码如下: 283 | 284 | ``` 285 | print("hello python") # 输出 `hello python` 286 | ``` 287 | 288 | ## 多行注释(块注释) 289 | 290 | - 如果希望编写的 **注释信息很多,一行无法显示**,就可以使用多行注释 291 | - 要在 Python 程序中使用多行注释,可以用 **一对 连续的 三个 引号**(单引号和双引号都可以) 292 | - 示例代码如下: 293 | 294 | ``` 295 | """ 296 | 这是一个多行注释 297 | 298 | 在多行注释之间,可以写很多很多的内容…… 299 | """ 300 | print("hello python") 301 | ``` 302 | 303 | ### 什么时候需要使用注释? 304 | 305 | 1. **注释不是越多越好**,对于一目了然的代码,不需要添加注释 306 | 2. 对于 **复杂的操作**,应该在操作开始前写上若干行注释 307 | 3. 对于 **不是一目了然的代码**,应在其行尾添加注释(为了提高可读性,注释应该至少离开代码 2 个空格) 308 | 4. 绝不要描述代码,假设阅读代码的人比你更懂 Python,他只是不知道你的代码要做什么 309 | 310 | > 在一些正规的开发团队,通常会有 **代码审核** 的惯例,就是一个团队中彼此阅读对方的代码 311 | 312 | ### 关于代码规范 313 | 314 | - `Python` 官方提供有一系列 PEP(Python Enhancement Proposals) 文档 315 | - 其中第 8 篇文档专门针对 **Python 的代码格式** 给出了建议,也就是俗称的 **PEP 8** 316 | - 文档地址:https://www.python.org/dev/peps/pep-0008/ 317 | - 谷歌有对应的中文文档:http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/ 318 | 319 | > 任何语言的程序员,编写出符合规范的代码,是开始程序生涯的第一步 320 | 321 | 322 | 323 | 324 | 325 | ### 拓展 -- 认识一下一些错误 326 | 327 | #### 关于错误 328 | 329 | - 编写的程序**不能正常执行**,或者**执行的结果不是我们期望的** 330 | 331 | - 俗称 332 | 333 | 334 | 335 | ``` 336 | BUG 337 | ``` 338 | 339 | ,是程序员在开发时非常常见的,初学者常见错误的原因包括: 340 | 341 | 1. 手误 342 | 2. 对已经学习过的知识理解还存在不足 343 | 3. 对语言还有需要学习和提升的内容 344 | 345 | - 在学习语言时,不仅要**学会语言的语法**,而且还要**学会如何认识错误和解决错误的方法** 346 | 347 | > 每一个程序员都是在不断地修改错误中成长的 348 | 349 | #### 第一个演练中的常见错误 350 | 351 | - 1> **手误**,例如使用 `pirnt("Hello world")` 352 | 353 | ``` 354 | NameError: name 'pirnt' is not defined 355 | 356 | 名称错误:'pirnt' 名字没有定义 357 | ``` 358 | 359 | - 2> 将多条 `print` 写在一行 360 | 361 | ``` 362 | SyntaxError: invalid syntax 363 | 364 | 语法错误:语法无效 365 | ``` 366 | 367 | > 每行代码负责完成一个动作 368 | 369 | - 3> 缩进错误 370 | 371 | ``` 372 | IndentationError: unexpected indent 373 | 374 | 缩进错误:不期望出现的缩进 375 | ``` 376 | 377 | > - Python 是一个格式非常严格的程序设计语言 378 | > - 目前而言,大家记住每行代码前面都不要增加空格 379 | 380 | #### 单词列表 381 | 382 | ``` 383 | * error 错误 384 | * name 名字 385 | * defined 已经定义 386 | * syntax 语法 387 | * invalid 无效 388 | * Indentation 索引 389 | * unexpected 意外的,不期望的 390 | * character 字符 391 | * line 行 392 | * encoding 编码 393 | * declared 声明 394 | * details 细节,详细信息 395 | * ASCII 一种字符编码 396 | ``` 397 | 398 | 399 | 400 | 401 | 402 | ### 练习 403 | 404 | 让我们一起得到Python之禅 405 | 406 | 1.在Python交互式环境中输入下面的代码并查看结果 407 | 408 | ``` 409 | import this 410 | ``` 411 | 412 | ![image-20210331173825828](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210331173825828.png) 413 | 414 | 2. 学习使用turtle在屏幕上绘制图形。 415 | 416 | > **说明**:turtle是Python内置的一个非常有趣的模块,特别适合对计算机程序设计进行初体验的小伙伴,它最早是Logo语言的一部分,Logo语言是Wally Feurzig和Seymour Papert在1966发明的编程语言。 417 | 418 | ``` 419 | import turtle 420 | 421 | turtle.pensize(4) 422 | turtle.pencolor('red') 423 | 424 | 425 | turtle.right(90) 426 | 427 | turtle.right(90) 428 | turtle.forward(100) 429 | turtle.right(90) 430 | turtle.forward(100) 431 | 432 | turtle.mainloop() 433 | ``` 434 | 435 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 436 | -------------------------------------------------------------------------------- /Python基础连载/面向对象基础.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:面向对象基础 2 | 3 | ## 面向对象基础 4 | 5 | - 类和对象 - 什么是类 / 什么是对象 / 面向对象其他相关概念 6 | - 定义类 - 基本结构 / 属性和方法 / 构造器 / 析构器 / __str__方法 7 | - 创建对象 8 | - 面向对象的三大特征 封装 / 继承 / 多态 9 | 10 | **这是很多编程语言的一个基础知识,那什么才是面向对象呢?** 11 | 12 | 要理解面向对象,我们先来看看与面向对象相对应的另外一种程序设计方法:面向过程。 13 | 14 | 面向过程的编程的基本构成便是“过程”,过程实现的方式就是“函数”,我们通过不同函数来实现不同的功能,并按照程序的执行顺序调用相应的函数,组成一个完整的可以运行的应用程序。我们可以通过把不同的功能在不同的函数中实现或者给函数传递不同的参数来实现不同的功能,这是面向过程中模块化设计的原理。 15 | 16 | 但是面向过程有很多问题,当我们总是按照教科书上的小例子来学习程序设计时是永远也体会不到面向过程中存在的这些问题的,反而会觉得面向过程更简单,更容易理解。而事实是当我们设计一些大型的应用的时候你将会发现使用面向过程编程是多么的痛苦和无奈,代码极难维护,我们不得不为相似功能设计不同的函数,天长日久,代码量越来越大,函数越来越多,而重复的代码越来越多,噩梦就此产生。 17 | 18 | 于是乎产生了另外一种设计思想:面向对象,从此程序员发现编程是多么快乐的一件事情。我们可以把现实世界的很多哲学思想或者模型应用于编程,这是计算机的一次伟大的革命。那么究竟何为面向对象?要理解这两个重要的字“对象“,我们首先需要理解一下类和实例: 19 | 20 | 举一个简单的例子,大家都会下五子棋,我们就以开发一个五子棋的游戏来讲解面向过程和面向对象最本质的区别,在早期以面向过程为主要开发方法时,我们是这样来设计这个游戏的: 21 | 22 | 1. 开始游戏; 23 | 2. 黑方出子; 24 | 3. 绘制画面; 25 | 4. 判断胜负; 26 | 5. 白方出子; 27 | 6. 绘制画面; 28 | 7. 判断胜负; 29 | 8. 循环2、3、4、5、6、7步; 30 | 9. 输出结果。 31 | 32 | 最后将每一个步骤作为一个处理函数开发出来,每次运行都调用一遍函数(或者过程)。面向过程最关键的概念就是“过程”,所以程序运行都是一步接一步,从上往下。 33 | 34 | 而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为: 35 | 36 | 1. 黑白双方:负责出子和悔棋; 37 | 2. 棋盘系统:负责绘制画面; 38 | 3. 规则系统:负责判定诸如犯规、输赢等; 39 | 4. 输入输出系统:负责接收黑白子放的位置信息和输出游戏过程中的相关信息。 40 | 41 | 这就是面向对象,更强调将程序模块化,我们甚至可以将该程序抽象出来使其适用于五子棋和围棋(它们除了规则不一样以外没有其它区别,那么我们只需要修改规则系统便可轻易支持围棋)。 42 | 43 | **再初步理解了什么是面向对象的时候,我们接下来就要去理解一下什么是类,什么是对象** 44 | 45 | --- 46 | 47 | **类** 和 **对象** 是 **面向对象编程的 两个 核心概念** 48 | 49 | ### 类 50 | 51 | + **类** 是对一群具有 **相同 特征** 或者 **行为** 的事物的一个统称,是抽象的,**不能直接使用** 52 | + **特征** 被称为 **属性** 53 | + **行为** 被称为 **方法** 54 | + **类** 就相当于制造飞机时的**图纸**,是一个 **模板**,是 **负责创建对象的** 55 | 56 | ![image-20210401142113965](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401142113965.png) 57 | 58 | ### 对象 59 | 60 | + **对象** 是 **由类创建出来的一个具体存在**,可以直接使用 61 | + 由 **哪一个类** 创建出来的 **对象**,就拥有在 **哪一个类** 中定义的: 62 | + 属性 63 | + 方法 64 | + **对象** 就相当于用 **图纸** **制造** 的飞机 65 | 66 | **在程序开发中,应该先有类,再有对象** 67 | 68 | ## 类和对象的关系 69 | 70 | + **类是模板**,**对象** 是根据 **类** 这个模板创建出来的,应该 **先有类,再有对象** 71 | + **类** 只有一个,而 **对象** 可以有很多个 72 | + **不同的对象** 之间 **属性** 可能会各不相同 73 | + **类** 中定义了什么 **属性和方法**,**对象** 中就有什么属性和方法,**不可能多,也不可能少** 74 | 75 | ### 定义类 76 | 77 | 在Python中可以使用`class`关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。 78 | 79 | ```Python 80 | class Student(object): 81 | 82 | # __init__是一个特殊方法用于在创建对象时进行初始化操作 83 | # 通过这个方法我们可以为学生对象绑定name和age两个属性 84 | def __init__(self, name, age): 85 | self.name = name 86 | self.age = age 87 | 88 | def study(self, course_name): 89 | print('%s正在学习%s.' % (self.name, course_name)) 90 | 91 | 92 | # 驼峰命名法(驼峰标识) 93 | def watch_movie(self): 94 | if self.age < 18: 95 | print('%s正在看蜡笔小新.' % self.name) 96 | else: 97 | print('%s正在观看海贼王' % self.name) 98 | ``` 99 | 100 | ### 创建和使用对象 101 | 102 | 当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。 103 | 104 | ``` 105 | def main(): 106 | # 创建学生对象并指定姓名和年龄 107 | stu1 = Student('海森堡', 19) 108 | # 给对象发study消息 109 | stu1.study('大话Python') 110 | # 给对象发watch_av消息 111 | stu1.watch_movie() 112 | 113 | 114 | 115 | if __name__ == '__main__': 116 | main() 117 | ``` 118 | 119 | ## 类的设计 120 | 121 | 在使用面相对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类! 122 | 123 | ![image-20210401143347135](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401143347135.png) 124 | 125 | 在程序开发中,要设计一个类,通常需要满足一下三个要素: 126 | 127 | 1. **类名** 这类事物的名字,**满足大驼峰命名法** 128 | 2. **属性** 这类事物具有什么样的特征 129 | 3. **方法** 这类事物具有什么样的行为 130 | 131 | ### 大驼峰命名法 132 | 133 | ``` 134 | CapWords 135 | ``` 136 | 137 | 1. 每一个单词的首字母大写 138 | 2. 单词与单词之间没有下划线 139 | 140 | ### 面向对象的三大特征 141 | 142 | 面向想有三大特征: 封装,继承,多态,下面我们来了解一下这三大特性。 143 | 144 | **什么是封装呢?** 145 | 146 | 我个人的理解就是,把你的属性隐藏起来,外界不可获取到。我简单的分为四点去理解: 147 | 148 | 1. **封装** 是面向对象编程的一大特点 149 | 2. 面向对象编程的 **第一步** —— 将 **属性** 和 **方法** **封装** 到一个抽象的 **类** 中 150 | 3. **外界** 使用 **类** 创建 **对象**,然后 **让对象调用方法** 151 | 4. **对象方法的细节** 都被 **封装** 在 **类的内部** 152 | 153 | 我们通过一个例子来了解一下什么是封装: 154 | 155 | **需求** 156 | 157 | 1. **小明** **体重** `75.0` 公斤 158 | 2. 小明每次 **跑步** 会减肥 `0.5` 公斤 159 | 3. 小明每次 **吃东西** 体重增加 `1` 公斤 160 | 161 | ![image-20210401144210055](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401144210055.png) 162 | 163 | > 提示:在 **对象的方法内部**,是可以 **直接访问对象的属性** 的! 164 | 165 | + 代码实现 166 | 167 | ``` 168 | class Person: 169 | """人类""" 170 | “”“封装”“” 171 | def __init__(self, name, weight): 172 | 173 | self.name = name 174 | self.weight = weight 175 | 176 | """内置的方法,下一节会讲到""" 177 | def __str__(self): 178 | 179 | return "我的名字叫 %s 体重 %.2f 公斤" % (self.name, self.weight) 180 | 181 | def run(self): 182 | """跑步""" 183 | 184 | print("%s 爱跑步,跑步锻炼身体" % self.name) 185 | self.weight -= 0.5 186 | 187 | def eat(self): 188 | """吃东西""" 189 | 190 | print("%s 是吃货,吃完这顿再减肥" % self.name) 191 | self.weight += 1 192 | 193 | 194 | xiaoming = Person("小明", 75) 195 | 196 | xiaoming.run() 197 | xiaoming.eat() 198 | xiaoming.eat() 199 | 200 | print(xiaoming) 201 | ``` 202 | 203 | #### 定义一个类描述数字时钟。 204 | 205 | ```Python 206 | from time import sleep 207 | 208 | 209 | class Clock(object): 210 | """数字时钟""" 211 | 212 | def __init__(self, hour=0, minute=0, second=0): 213 | """初始化方法 214 | 215 | :param hour: 时 216 | :param minute: 分 217 | :param second: 秒 218 | """ 219 | self._hour = hour 220 | self._minute = minute 221 | self._second = second 222 | 223 | def run(self): 224 | """走字""" 225 | self._second += 1 226 | if self._second == 60: 227 | self._second = 0 228 | self._minute += 1 229 | if self._minute == 60: 230 | self._minute = 0 231 | self._hour += 1 232 | if self._hour == 24: 233 | self._hour = 0 234 | 235 | def show(self): 236 | """显示时间""" 237 | return '%02d:%02d:%02d' % \ 238 | (self._hour, self._minute, self._second) 239 | 240 | 241 | def main(): 242 | clock = Clock(23, 59, 58) 243 | while True: 244 | print(clock.show()) 245 | sleep(1) 246 | clock.run() 247 | 248 | 249 | if __name__ == '__main__': 250 | main() 251 | ``` 252 | 253 | ### 继承 254 | 255 | 继承,从字面意识上理解,就是可以实现从父亲辈继承到一些遗产什么的,而在Python中就是可以继承到一些属性和方法。Python与Java在继承中有一个非常大的区别就是Python支持多继承,而java只支持单继承。 256 | 257 | ## 单继承 258 | 259 | ### 继承的概念、语法和特点 260 | 261 | **继承的概念**:**子类** 拥有 **父类** 的所有 **方法** 和 **属性** 262 | 263 | ![image-20210401144847194](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401144847194.png) 264 | 265 | #### 1) 继承的语法 266 | 267 | ``` 268 | class 类名(父类名): 269 | 270 | pass 271 | ``` 272 | 273 | + `Dog` 类是 `Animal` 类的**子类**,`Animal` 类是 `Dog` 类的**父类**,`Dog` 类从 `Animal` 类**继承** 274 | + `Dog` 类是 `Animal` 类的**派生类**,`Animal` 类是 `Dog` 类的**基类**,`Dog` 类从 `Animal` 类**派生** 275 | 276 | #### 2) 专业术语 277 | 278 | + `C` 类从 `B` 类继承,`B` 类又从 `A` 类继承 279 | + 那么 `C` 类就具有 `B` 类和 `A` 类的所有属性和方法 280 | 281 | ### 方法的重写 282 | 283 | + **子类** 拥有 **父类** 的所有 **方法** 和 **属性** 284 | + **子类** 继承自 **父类**,可以直接 **享受** 父类中已经封装好的方法,不需要再次开发 285 | 286 | **应用场景** 287 | 288 | + 当 **父类** 的方法实现不能满足子类需求时,可以对方法进行 **重写(override)** 289 | 290 | ![image-20210401145055969](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401145055969.png) 291 | 292 | **重写** 父类方法有两种情况: 293 | 294 | 1. **覆盖** 父类的方法 295 | 2. 对父类方法进行 **扩展** 296 | 297 | #### 1) 覆盖父类的方法 298 | 299 | + 如果在开发中,**父类的方法实现** 和 **子类的方法实现**,**完全不同** 300 | + 就可以使用 **覆盖** 的方式,**在子类中** **重新编写** 父类的方法实现 301 | 302 | > 具体的实现方式,就相当于在 **子类中** 定义了一个 **和父类同名的方法并且实现** 303 | 304 | #### 2) 对父类方法进行 **扩展** 305 | 306 | + 如果在开发中,**子类的方法实现** 中 **包含** **父类的方法实现** 307 | + **父类原本封装的方法实现** 是 **子类方法的一部分** 308 | + 就可以使用 **扩展** 的方式 309 | + **在子类中** **重写** 父类的方法 310 | + 在需要的位置使用 `super().父类方法` 来调用父类方法的执行 311 | + 代码其他的位置针对子类的需求,编写 **子类特有的代码实现** 312 | 313 | ##### 关于 `super` 314 | 315 | + 在 `Python` 中 `super` 是一个 **特殊的类** 316 | + `super()` 就是使用 `super` 类创建出来的对象 317 | + **最常** 使用的场景就是在 **重写父类方法时**,调用 **在父类中封装的方法实现** 318 | 319 | ### 父类的 私有属性 和 私有方法 320 | 321 | 1. **子类对象** **不能** 在自己的方法内部,**直接** 访问 父类的 **私有属性** 或 **私有方法** 322 | 2. **子类对象** 可以通过 **父类** 的 **公有方法** **间接** 访问到 **私有属性** 或 **私有方法** 323 | 324 | > - **私有属性、方法** 是对象的隐私,不对外公开,**外界** 以及 **子类** 都不能直接访问 325 | > - **私有属性、方法** 通常用于做一些内部的事情 326 | 327 | ![image-20210401145333011](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401145333011.png) 328 | 329 | - `B` 的对象不能直接访问 `__num2` 属性 330 | - `B` 的对象不能在 `demo` 方法内访问 `__num2` 属性 331 | - `B` 的对象可以在 `demo` 方法内,调用父类的 `test` 方法 332 | - 父类的 `test` 方法内部,能够访问 `__num2` 属性和 `__test` 方法 333 | 334 | ## 多继承 335 | 336 | **概念** 337 | 338 | + **子类** 可以拥有 **多个父类**,并且具有 **所有父类** 的 **属性** 和 **方法** 339 | + 例如:**孩子** 会继承自己 **父亲** 和 **母亲** 的 **特性** 340 | 341 | 342 | 343 | ![image-20210401150811858](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401150811858.png) 344 | 345 | **语法** 346 | 347 | ``` 348 | class 子类名(父类名1, 父类名2...) 349 | pass 350 | ``` 351 | 352 | #### Python 中的 MRO —— 方法搜索顺序(了解) 353 | 354 | - `Python` 中针对 **类** 提供了一个 **内置属性** `__mro__` 可以查看 **方法** 搜索顺序 355 | - MRO 是 `method resolution order`,主要用于 **在多继承时判断 方法、属性 的调用 路径** 356 | 357 | ``` 358 | print(C.__mro__) 359 | ``` 360 | 361 | **输出结果** 362 | 363 | ``` 364 | (, , , ) 365 | ``` 366 | 367 | - 在搜索方法时,是按照 `__mro__` 的输出结果 **从左至右** 的顺序查找的 368 | - 如果在当前类中 **找到方法,就直接执行,不再搜索** 369 | - 如果 **没有找到,就查找下一个类** 中是否有对应的方法,**如果找到,就直接执行,不再搜索** 370 | - 如果找到最后一个类,还没有找到方法,程序报错 371 | 372 | ## 多态 373 | 374 | 重温一下面向对象三大特性: 375 | 376 | 1. **封装** 根据 **职责** 将 **属性** 和 **方法** **封装** 到一个抽象的 **类** 中 377 | - 定义类的准则 378 | 2. **继承** **实现代码的重用**,相同的代码不需要重复的编写 379 | - 设计类的技巧 380 | - 子类针对自己特有的需求,编写特定的代码 381 | 3. **多态** 不同的 **子类对象** 调用相同的 **父类方法**,产生不同的执行结果 382 | - **多态** 可以 **增加代码的灵活度** 383 | - 以 **继承** 和 **重写父类方法** 为前提 384 | - 是调用方法的技巧,**不会影响到类的内部设计** 385 | 386 | ![image-20210401150953469](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401150953469.png) 387 | 388 | ## 多态案例演练 389 | 390 | 1.在 `Dog` 类中封装方法 `game` 391 | 392 | - 普通狗只是简单的玩耍 393 | 394 | 2.定义 `XiaoTianDog` 继承自 `Dog`,并且重写 `game` 方法 395 | 396 | - 哮天犬需要在天上玩耍 397 | 398 | 3.定义 `Person` 类,并且封装一个 **和狗玩** 的方法 399 | 400 | + 在方法内部,直接让 **狗对象** 调用 `game` 方法 401 | 402 | ![image-20210401151131412](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401151131412.png) 403 | 404 | **案例小结** 405 | 406 | - `Person` 类中只需要让 **狗对象** 调用 `game` 方法,而不关心具体是 **什么狗** 407 | - `game` 方法是在 `Dog` 父类中定义的 408 | - 在程序执行时,传入不同的 **狗对象** 实参,就会产生不同的执行效果 409 | 410 | > **多态** 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化! 411 | 412 | ``` 413 | class Dog(object): 414 | 415 | def __init__(self, name): 416 | self.name = name 417 | 418 | def game(self): 419 | print("%s 蹦蹦跳跳的玩耍..." % self.name) 420 | 421 | 422 | class XiaoTianDog(Dog): 423 | 424 | def game(self): 425 | print("%s 飞到天上去玩耍..." % self.name) 426 | 427 | 428 | class Person(object): 429 | 430 | def __init__(self, name): 431 | self.name = name 432 | 433 | def game_with_dog(self, dog): 434 | 435 | print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name)) 436 | 437 | # 让狗玩耍 438 | dog.game() 439 | 440 | 441 | # 1. 创建一个狗对象 442 | # wangcai = Dog("旺财") 443 | wangcai = XiaoTianDog("飞天旺财") 444 | 445 | # 2. 创建一个小明对象 446 | xiaoming = Person("小明") 447 | 448 | # 3. 让小明调用和狗玩的方法 449 | xiaoming.game_with_dog(wangcai) 450 | ``` 451 | 452 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 453 | -------------------------------------------------------------------------------- /Python基础连载/面向对象进阶.md: -------------------------------------------------------------------------------- 1 | # Python连载系列:面向对象进阶 2 | 3 | ## 面向对象进阶 4 | 5 | + 类属性和类方法和静态方法 6 | + 运算符重载 - __add__ / __sub__ / __or__ /__getitem__ / __setitem__ / __len__ / __repr__ / __gt__ / __lt__ / __le__ / __ge__ / __eq__ / __ne__ / __contains__ 7 | 8 | ## 类的结构 9 | 10 | ### 术语 —— 实例 11 | 12 | 1. 使用面相对象开发,**第 1 步** 是设计 **类** 13 | 2. 使用 **类名()** 创建对象,**创建对象** 的动作有两步: 14 | - 1. 在内存中为对象 **分配空间** 15 | - 1. 调用初始化方法 `__init__` 为 **对象初始化** 16 | 3. 对象创建后,**内存** 中就有了一个对象的 **实实在在** 的存在 —— **实例** 17 | 18 | ![image-20210401152042631](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401152042631.png) 19 | 20 | 因此,通常也会把: 21 | 22 | 1. 创建出来的 **对象** 叫做 **类** 的 **实例** 23 | 2. 创建对象的 **动作** 叫做 **实例化** 24 | 3. **对象的属性** 叫做 **实例属性** 25 | 4. **对象调用的方法** 叫做 **实例方法** 26 | 27 | 在程序执行时: 28 | 29 | 1. 对象各自拥有自己的 **实例属性** 30 | 2. 调用对象方法,可以通过 `self.` 31 | - 访问自己的属性 32 | - 调用自己的方法 33 | 3. - 访问自己的属性 34 | - 调用自己的方法 35 | 36 | **结论** 37 | 38 | - **每一个对象** 都有自己 **独立的内存空间**,**保存各自不同的属性** 39 | - **多个对象的方法**,**在内存中只有一份**,在调用方法时,**需要把对象的引用** 传递到方法内部 40 | 41 | ### 类是一个特殊的对象 42 | 43 | > `Python` 中 **一切皆对象**: 44 | > 45 | > - `class AAA:` 定义的类属于 **类对象** 46 | > - `obj1 = AAA()` 属于 **实例对象** 47 | 48 | - 在程序运行时,**类** 同样 **会被加载到内存** 49 | - 在 `Python` 中,**类** 是一个特殊的对象 —— **类对象** 50 | - 在程序运行时,**类对象** 在内存中 **只有一份**,使用 **一个类** 可以创建出 **很多个对象实例** 51 | - 除了封装 **实例** 的 **属性** 和 **方法**外,**类对象** 还可以拥有自己的 **属性** 和 **方法* 52 | 1. **类属性** 53 | 2. **类方法** 54 | - 1. **类属性** 55 | 2. **类方法** 56 | - 通过 **类名.** 的方式可以 **访问类的属性** 或者 **调用类的方法** 57 | 58 | ![image-20210401152222934](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401152222934.png) 59 | 60 | ## 类属性和实例属性 61 | 62 | ### 2.1 概念和使用 63 | 64 | - **类属性** 就是给 **类对象** 中定义的 **属性** 65 | - 通常用来记录 **与这个类相关** 的特征 66 | - **类属性** **不会用于**记录 **具体对象的特征** 67 | 68 | **示例需求** 69 | 70 | - 定义一个 **工具类** 71 | - 每件工具都有自己的 `name` 72 | - **需求** —— 知道使用这个类,创建了多少个工具对象? 73 | 74 | ![image-20210401152255066](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401152255066.png) 75 | 76 | ``` 77 | class Tool(object): 78 | 79 | # 使用赋值语句,定义类属性,记录创建工具对象的总数 80 | count = 0 81 | 82 | def __init__(self, name): 83 | self.name = name 84 | 85 | # 针对类属性做一个计数+1 86 | Tool.count += 1 87 | 88 | 89 | # 创建工具对象 90 | tool1 = Tool("斧头") 91 | tool2 = Tool("榔头") 92 | tool3 = Tool("铁锹") 93 | 94 | # 知道使用 Tool 类到底创建了多少个对象? 95 | print("现在创建了 %d 个工具" % Tool.count) 96 | ``` 97 | 98 | 因此,要访问类属性有两种方式: 99 | 100 | 1. **类名.类属性** 101 | 2. **对象.类属性** (不推荐) 102 | 103 | **注意** 104 | 105 | - 如果使用 `对象.类属性 = 值` 赋值语句,只会 **给对象添加一个属性**,而不会影响到 **类属性的值** 106 | 107 | ## 类方法和静态方法 108 | 109 | ### 类方法 110 | 111 | - **类属性** 就是针对 **类对象** 定义的属性 112 | - 使用 **赋值语句** 在 `class` 关键字下方可以定义 **类属性** 113 | - **类属性** 用于记录 **与这个类相关** 的特征 114 | - **类方法** 就是针对 **类对象** 定义的方法 115 | - 在 **类方法** 内部可以直接访问 **类属性** 或者调用其他的 **类方法** 116 | 117 | **语法如下** 118 | 119 | ``` 120 | @classmethod 121 | def 类方法名(cls): 122 | pass 123 | ``` 124 | 125 | + 类方法需要用 **修饰器** `@classmethod` 来标识,**告诉解释器这是一个类方法** 126 | + 类方法的 **第一个参数** 应该是 `cls` 127 | + 由 **哪一个类** 调用的方法,方法内的 `cls` 就是 **哪一个类的引用** 128 | + 这个参数和 **实例方法** 的第一个参数是 `self` 类似 129 | + **提示** 使用其他名称也可以,不过习惯使用 `cls` 130 | + 通过 **类名.** 调用 **类方法**,**调用方法时**,不需要传递 `cls` 参数 131 | + **在方法内部** 132 | + 可以通过 `cls.` **访问类的属性** 133 | + 也可以通过 `cls.` **调用其他的类方法** 134 | 135 | **示例需求** 136 | 137 | - 定义一个 **工具类** 138 | - 每件工具都有自己的 `name` 139 | - **需求** —— 在 **类** 封装一个 `show_tool_count` 的类方法,输出使用当前这个类,创建的对象个数 140 | 141 | ![image-20210401152602204](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401152602204.png) 142 | 143 | ``` 144 | @classmethod 145 | def show_tool_count(cls): 146 | """显示工具对象的总数""" 147 | print("工具对象的总数 %d" % cls.count) 148 | ``` 149 | 150 | > 在类方法内部,可以直接使用 `cls` 访问 **类属性** 或者 **调用类方法** 151 | 152 | ### 静态方法 153 | 154 | + 在开发时,如果需要在 **类** 中封装一个方法,这个方法: 155 | + 既 **不需要** 访问 **实例属性** 或者调用 **实例方法** 156 | + 也 **不需要** 访问 **类属性** 或者调用 **类方法** 157 | 158 | **语法如下** 159 | 160 | ``` 161 | @staticmethod 162 | def 静态方法名(): 163 | pass 164 | ``` 165 | 166 | - **静态方法** 需要用 **修饰器** `@staticmethod` 来标识,**告诉解释器这是一个静态方法** 167 | - 通过 **类名.** 调用 **静态方法** 168 | 169 | ``` 170 | class Dog(object): 171 | 172 | # 狗对象计数 173 | dog_count = 0 174 | 175 | @staticmethod 176 | def run(): 177 | 178 | # 不需要访问实例属性也不需要访问类属性的方法 179 | print("狗在跑...") 180 | 181 | def __init__(self, name): 182 | self.name = name 183 | 184 | ``` 185 | 186 | ### 方法综合案例 187 | 188 | **需求** 189 | 190 | 1. 设计一个 `Game` 类 191 | 2. 属性: 192 | - 定义一个 **类属性** `top_score` 记录游戏的 **历史最高分** 193 | - 定义一个 **实例属性** `player_name` 记录 **当前游戏的玩家姓名** 194 | 3. 方法: 195 | - **静态方法** `show_help` 显示游戏帮助信息 196 | - **类方法** `show_top_score` 显示历史最高分 197 | - **实例方法** `start_game` 开始当前玩家的游戏 198 | 4. 主程序步骤 199 | - 1. 查看帮助信息 200 | - 1. 查看历史最高分 201 | - 1. 创建游戏对象,开始游戏 202 | 203 | ![image-20210401155105667](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210401155105667.png) 204 | 205 | #### 案例小结 206 | 207 | 1. 实例方法 方法内部需要访问实例属性 208 | - **实例方法** 内部可以使用 **类名.** 访问类属性 209 | 2. **类方法** —— 方法内部 **只** 需要访问 **类属性** 210 | 3. **静态方法** —— 方法内部,不需要访问 **实例属性** 和 **类属性** 211 | 212 | **提问** 213 | 214 | 如果方法内部 即需要访问 **实例属性**,又需要访问 **类属性**,应该定义成什么方法? 215 | 216 | **答案** 217 | 218 | - 应该定义 **实例方法** 219 | - 因为,**类只有一个**,在 **实例方法** 内部可以使用 **类名.** 访问类属性 220 | 221 | ``` 222 | class Game(object): 223 | 224 | # 游戏最高分,类属性 225 | top_score = 0 226 | 227 | @staticmethod 228 | def show_help(): 229 | print("帮助信息:让僵尸走进房间") 230 | 231 | @classmethod 232 | def show_top_score(cls): 233 | print("游戏最高分是 %d" % cls.top_score) 234 | 235 | def __init__(self, player_name): 236 | self.player_name = player_name 237 | 238 | def start_game(self): 239 | print("[%s] 开始游戏..." % self.player_name) 240 | 241 | # 使用类名.修改历史最高分 242 | Game.top_score = 999 243 | 244 | # 1. 查看游戏帮助 245 | Game.show_help() 246 | 247 | # 2. 查看游戏最高分 248 | Game.show_top_score() 249 | 250 | # 3. 创建游戏对象,开始游戏 251 | game = Game("小明") 252 | 253 | game.start_game() 254 | 255 | # 4. 游戏结束,查看游戏最高分 256 | Game.show_top_score() 257 | ``` 258 | 259 | ## 运算符重载 - __add__ / __sub__ / __or__ /__getitem__ / __setitem__ / __len__ / __repr__ / __gt__ / __lt__ / __le__ / __ge__ / __eq__ / __ne__ / __contains__ 260 | 261 | 后面有一篇单独的文章补充 262 | 263 | ![名片](https://gitee.com/chushi123/picgo/raw/master/picture/名片.png) 264 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 介绍:本仓库为自己学习过程中,汇集的知识点和一些面试经验等。如果对大家有帮助,请多多star,谢谢各位! 2 | 3 | ## 资源获取 4 | 上百本计算机书籍pdf收集 5 | > https://github.com/hellgoddess/ITBook 6 | 7 | 获取Python100道练习题 8 | > https://github.com/hellgoddess/PythonGuide/tree/main/Python100%E9%81%93%E7%BB%83%E4%B9%A0%E9%A2%98 9 | 10 | 思维导图 11 | > 链接:https://pan.baidu.com/s/1Gc7A9qdXnfqVkEJl2-PhaA 提取码:k063 Python思维导图 12 | 13 | > 链接:https://pan.baidu.com/s/1SJKo-DArTU1JHgd7e4mOTg 提取码:du5o MySQL思维导图 14 | 15 | 16 | 计算机网络pdf: 17 | > 链接: https://github.com/hellgoddess/PythonGuide/blob/main/docs/%E8%8E%B7%E5%8F%96%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9Cpdf.md 18 | 19 | # Python 20 | ## 基础 21 | #### 知识点/面试题(必看:muscle:) 22 | 1. [python基础连载系列](https://github.com/hellgoddess/PythonGuide/tree/main/Python%E5%9F%BA%E7%A1%80%E8%BF%9E%E8%BD%BD)(点击跳转到页面::heartpulse:) 23 | 2. [python常见面试题汇总](https://github.com/hellgoddess/PythonGuide/blob/main/docs/python_%20Interview.md) 24 | 3. [十二张Python思维导图带你学会Python](https://mp.weixin.qq.com/s/cTi12tOugs8y52hmBBTafg) 高清图片获取在gihub顶部 25 | 26 | #### 重要知识点讲解: 27 | 1. [python的生成器,迭代器和装饰器详解](https://mp.weixin.qq.com/s/hKMk285LRmGt7nDbMdepXw) 28 | 2. [一文读懂python68个内置函数](https://mp.weixin.qq.com/s/vtMHgt6kknU94fVZwfWjUA) 29 | 3. [学了这么久Python,你知道Python有多少关键字吗?硬核总结来了!](https://mp.weixin.qq.com/s/tIaegWbFC-sHawKBmaiwnw) 30 | 4. [Python魔法方法指南](https://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html#id8) 31 | 32 | #### python的容器分析: 33 | 34 | 35 | #### python并发编程: 36 | 1. [python多线程多进程详解](https://mp.weixin.qq.com/s/2aA7ke4lpcpdLK0etDmK4g) 37 | 38 | #### python垃圾回收机制: 39 | 40 | ## 网络 41 | 1. [一文搞定HTTP面试内容-上](https://mp.weixin.qq.com/s/7sO8CteDjkz2d6y4jX2eNQ) 42 | 2. [一文搞定HTTP面试内容-下](https://mp.weixin.qq.com/s/1Umm6Ror1z-7oBCEBe6o5g) 43 | 3. [60道计算机网络面试题请你查收~(上)(1-30](https://mp.weixin.qq.com/s/NAE4Lzvu8LO1Q6GrrdZHxg) 44 | 4. [60道计算机网络面试题请你查收~(下)(31-61](https://mp.weixin.qq.com/s/LcnOAdKq_8qG8hJ6ORAVsw) 45 | 46 | ## 操作系统 47 | 48 | 49 | ### 数据结构与算法 50 | #### 常见数据结构 51 | #### 算法 52 | 53 | + [算法学习和书籍推荐](https://www.zhihu.com/question/323359308/answer/1545320858) 54 | + [如何刷Leetcode](https://www.zhihu.com/question/31092580/answer/1534887374) 55 | 56 | 算法总结: 57 | 58 | 1.[一文学会Python十大排序算法](https://github.com/hellgoddess/PythonGuide/blob/main/docs/%E4%B8%80%E6%96%87%E5%AD%A6%E4%BC%9APython%E5%8D%81%E5%A4%A7%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) 59 | 60 | 61 | ## 数据库 62 | #### MySQL 63 | #### 总结: 64 | 1.[MySQL知识思维导图总结!](https://mp.weixin.qq.com/s/aMWgviKYMOX4GJ_ZfhJJXA) 65 | 66 | 2.[一千行MySQL命令](https://github.com/hellgoddess/PythonGuide/blob/main/docs/%E4%B8%80%E5%8D%83%E8%A1%8CMySQL%E5%91%BD%E4%BB%A4.md) 67 | 68 | 3.[MySQL高性能优化规范建议](https://github.com/hellgoddess/PythonGuide/blob/main/docs/MySQL%E9%AB%98%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E8%A7%84%E8%8C%83%E5%BB%BA%E8%AE%AE.md) 69 | 70 | #### 重要知识点: 71 | #### 面经问题总结: 72 | #### Redis 73 | 74 | ## 常见应用 75 | 76 | #### 爬虫学习与实战: 77 | 1.[从0到1学习爬虫-爬虫是什么,爬虫能吃吗?](https://mp.weixin.qq.com/s/ObfZMqEPAO8Ap78wRGN3rw) 78 | #### Django学习与实战 79 | #### Flask学习与实战 80 | 81 | 82 | ## python学习常见问题汇总 83 | 84 | ## Python实战与小技巧 85 | 86 | > 公众号为:CookDev,文章首发公众号,谢谢大家的关注,一起进步。 87 | 88 | ![公众号二维码](https://gitee.com/chushi123/picgo/raw/master/picture/公众号二维码.jpg) 89 | -------------------------------------------------------------------------------- /code/snake.py: -------------------------------------------------------------------------------- 1 | ## 导入相关模块 2 | import random 3 | import pygame 4 | import sys 5 | 6 | from pygame.locals import * 7 | 8 | 9 | snake_speed = 15 #贪吃蛇的速度 10 | windows_width = 800 11 | windows_height = 600 #游戏窗口的大小 12 | cell_size = 20 #贪吃蛇身体方块大小,注意身体大小必须能被窗口长宽整除 13 | 14 | ''' #初始化区 15 | 由于我们的贪吃蛇是有大小尺寸的, 因此地图的实际尺寸是相对于贪吃蛇的大小尺寸而言的 16 | ''' 17 | map_width = int(windows_width / cell_size) 18 | map_height = int(windows_height / cell_size) 19 | 20 | # 颜色定义 21 | white = (255, 255, 255) 22 | black = (0, 0, 0) 23 | gray = (230, 230, 230) 24 | dark_gray = (40, 40, 40) 25 | DARKGreen = (0, 155, 0) 26 | Green = (0, 255, 0) 27 | Red = (255, 0, 0) 28 | blue = (0, 0, 255) 29 | dark_blue =(0,0, 139) 30 | 31 | 32 | BG_COLOR = black #游戏背景颜色 33 | 34 | # 定义方向 35 | UP = 1 36 | DOWN = 2 37 | LEFT = 3 38 | RIGHT = 4 39 | 40 | HEAD = 0 #贪吃蛇头部下标 41 | #主函数 42 | def main(): 43 | pygame.init() # 模块初始化 44 | snake_speed_clock = pygame.time.Clock() # 创建Pygame时钟对象 45 | screen = pygame.display.set_mode((windows_width, windows_height)) # 46 | screen.fill(white) 47 | 48 | pygame.display.set_caption("Python 贪吃蛇小游戏") #设置标题 49 | show_start_info(screen) #欢迎信息 50 | while True: 51 | running_game(screen, snake_speed_clock) 52 | show_gameover_info(screen) 53 | #游戏运行主体 54 | def running_game(screen,snake_speed_clock): 55 | startx = random.randint(3, map_width - 8) #开始位置 56 | starty = random.randint(3, map_height - 8) 57 | snake_coords = [{'x': startx, 'y': starty}, #初始贪吃蛇 58 | {'x': startx - 1, 'y': starty}, 59 | {'x': startx - 2, 'y': starty}] 60 | 61 | direction = RIGHT # 开始时向右移动 62 | 63 | food = get_random_location() #实物随机位置 64 | 65 | while True: 66 | for event in pygame.event.get(): 67 | if event.type == QUIT: 68 | terminate() 69 | elif event.type == KEYDOWN: 70 | if (event.key == K_LEFT or event.key == K_a) and direction != RIGHT: 71 | direction = LEFT 72 | elif (event.key == K_RIGHT or event.key == K_d) and direction != LEFT: 73 | direction = RIGHT 74 | elif (event.key == K_UP or event.key == K_w) and direction != DOWN: 75 | direction = UP 76 | elif (event.key == K_DOWN or event.key == K_s) and direction != UP: 77 | direction = DOWN 78 | elif event.key == K_ESCAPE: 79 | terminate() 80 | 81 | move_snake(direction, snake_coords) #移动蛇 82 | 83 | ret = snake_is_alive(snake_coords) 84 | if not ret: 85 | break #蛇跪了. 游戏结束 86 | snake_is_eat_food(snake_coords, food) #判断蛇是否吃到食物 87 | 88 | screen.fill(BG_COLOR) 89 | #draw_grid(screen) 90 | draw_snake(screen, snake_coords) 91 | draw_food(screen, food) 92 | draw_score(screen, len(snake_coords) - 3) 93 | pygame.display.update() 94 | snake_speed_clock.tick(snake_speed) #控制fps 95 | #将食物画出来 96 | def draw_food(screen, food): 97 | x = food['x'] * cell_size 98 | y = food['y'] * cell_size 99 | appleRect = pygame.Rect(x, y, cell_size, cell_size) 100 | pygame.draw.rect(screen, Red, appleRect) 101 | #将贪吃蛇画出来 102 | def draw_snake(screen, snake_coords): 103 | for coord in snake_coords: 104 | x = coord['x'] * cell_size 105 | y = coord['y'] * cell_size 106 | wormSegmentRect = pygame.Rect(x, y, cell_size, cell_size) 107 | pygame.draw.rect(screen, dark_blue, wormSegmentRect) 108 | wormInnerSegmentRect = pygame.Rect( #蛇身子里面的第二层亮绿色 109 | x + 4, y + 4, cell_size - 8, cell_size - 8) 110 | pygame.draw.rect(screen, blue, wormInnerSegmentRect) 111 | #移动贪吃蛇 112 | def move_snake(direction, snake_coords): 113 | if direction == UP: 114 | newHead = {'x': snake_coords[HEAD]['x'], 'y': snake_coords[HEAD]['y'] - 1} 115 | elif direction == DOWN: 116 | newHead = {'x': snake_coords[HEAD]['x'], 'y': snake_coords[HEAD]['y'] + 1} 117 | elif direction == LEFT: 118 | newHead = {'x': snake_coords[HEAD]['x'] - 1, 'y': snake_coords[HEAD]['y']} 119 | elif direction == RIGHT: 120 | newHead = {'x': snake_coords[HEAD]['x'] + 1, 'y': snake_coords[HEAD]['y']} 121 | 122 | snake_coords.insert(0, newHead) 123 | #判断蛇死了没 124 | def snake_is_alive(snake_coords): 125 | tag = True 126 | if snake_coords[HEAD]['x'] == -1 or snake_coords[HEAD]['x'] == map_width or snake_coords[HEAD]['y'] == -1 or \ 127 | snake_coords[HEAD]['y'] == map_height: 128 | tag = False # 蛇碰壁啦 129 | for snake_body in snake_coords[1:]: 130 | if snake_body['x'] == snake_coords[HEAD]['x'] and snake_body['y'] == snake_coords[HEAD]['y']: 131 | tag = False # 蛇碰到自己身体啦 132 | return tag 133 | #判断贪吃蛇是否吃到食物 134 | def snake_is_eat_food(snake_coords, food): #如果是列表或字典,那么函数内修改参数内容,就会影响到函数体外的对象。 135 | if snake_coords[HEAD]['x'] == food['x'] and snake_coords[HEAD]['y'] == food['y']: 136 | food['x'] = random.randint(0, map_width - 1) 137 | food['y'] = random.randint(0, map_height - 1) # 实物位置重新设置 138 | else: 139 | del snake_coords[-1] # 如果没有吃到实物, 就向前移动, 那么尾部一格删掉 140 | #食物随机生成 141 | def get_random_location(): 142 | return {'x': random.randint(0, map_width - 1), 'y': random.randint(0, map_height - 1)} 143 | #开始信息显示 144 | def show_start_info(screen): 145 | font = pygame.font.Font('myfont.ttf', 40) 146 | tip = font.render('按任意键开始游戏~~~', True, (65, 105, 225)) 147 | gamestart = pygame.image.load('gamestart.png') 148 | screen.blit(gamestart, (140, 30)) 149 | screen.blit(tip, (240, 550)) 150 | pygame.display.update() 151 | 152 | while True: #键盘监听事件 153 | for event in pygame.event.get(): # event handling loop 154 | if event.type == QUIT: 155 | terminate() #终止程序 156 | elif event.type == KEYDOWN: 157 | if (event.key == K_ESCAPE): #终止程序 158 | terminate() #终止程序 159 | else: 160 | return #结束此函数, 开始游戏 161 | #游戏结束信息显示 162 | def show_gameover_info(screen): 163 | font = pygame.font.Font('myfont.ttf', 40) 164 | tip = font.render('按Q或者ESC退出游戏, 按任意键重新开始游戏~', True, (65, 105, 225)) 165 | gamestart = pygame.image.load('gameover.png') 166 | screen.blit(gamestart, (60, 0)) 167 | screen.blit(tip, (80, 300)) 168 | pygame.display.update() 169 | 170 | while True: #键盘监听事件 171 | for event in pygame.event.get(): # event handling loop 172 | if event.type == QUIT: 173 | terminate() #终止程序 174 | elif event.type == KEYDOWN: 175 | if event.key == K_ESCAPE or event.key == K_q: #终止程序 176 | terminate() #终止程序 177 | else: 178 | return #结束此函数, 重新开始游戏 179 | #画成绩 180 | def draw_score(screen,score): 181 | font = pygame.font.Font('myfont.ttf', 30) 182 | scoreSurf = font.render('得分: %s' % score, True, Green) 183 | scoreRect = scoreSurf.get_rect() 184 | scoreRect.topleft = (windows_width - 120, 10) 185 | screen.blit(scoreSurf, scoreRect) 186 | #程序终止 187 | def terminate(): 188 | pygame.quit() 189 | sys.exit() 190 | main() 191 | -------------------------------------------------------------------------------- /docs/MySQL高性能优化规范建议.md: -------------------------------------------------------------------------------- 1 | # 数据库命令规范 2 | 3 | 1. 所有数据库对象名称必须使用小写字母并用下划线分割 4 | 2. 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 5 | 3. 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 6 | 4. 临时库表必须以tmp_为前缀并以日期为后缀,备份表必须以bak_为前缀并以日期(时间戳)为后缀 7 | 5. 所有存储相同数据的列名和列类型必须一致(一般作为关联列,如果查询时关联列类型不一致会自动进行数据类型隐式转换,会造成列上的索引失效,导致查询效率降低) 8 | 9 | # 数据库基本设计规范 10 | 11 | ## 1. 所有表必须使用Innodb存储引擎 12 | 13 | 14 | 15 | ``` 16 | 没有特殊要求(即Innodb无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用Innodb存储引擎(mysql5.5之前默认使用Myisam,5.6以后默认的为Innodb) 17 | Innodb 支持事务,支持行级锁,更好的恢复性,高并发下性能更好 18 | ``` 19 | 20 | ## 2. 数据库和表的字符集统一使用UTF8 21 | 22 | 23 | 24 | ``` 25 | 兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效,如果数据库中有存储emoji表情的需要,字符集需要采用utf8mb4字符集 26 | ``` 27 | 28 | ## 3. 所有表和字段都需要添加注释 29 | 30 | 31 | 32 | ``` 33 | 使用comment从句添加表和列的备注 34 | 从一开始就进行数据字典的维护 35 | ``` 36 | 37 | ## 4. 尽量控制单表数据量的大小,建议控制在500万以内 38 | 39 | 500万并不是Mysql数据库的限制,过大会造成修改表结构,备份,恢复都会有很大的问题 40 | 可以用历史数据归档(应用于日志数据),分库分表(应用于业务数据)等手段来控制数据量大小 41 | 42 | ## 5. 谨慎使用Mysql分区表 43 | 44 | 45 | 46 | ``` 47 | 分区表在物理上表现为多个文件,在逻辑上表现为一个表 48 | 谨慎选择分区键,跨分区查询效率可能更低 49 | 建议采用物理分表的方式管理大数据 50 | ``` 51 | 52 | ## 6. 尽量做到冷热数据分离,减小表的宽度 53 | 54 | 55 | 56 | ``` 57 | Mysql限制每个表最多存储4096列,并且每一行数据的大小不能超过65535字节 58 | 59 | 减少磁盘IO,保证热数据的内存缓存命中率(表越宽,把表装载进内存缓冲池时所占用的内存也就越大,也会消耗更多的IO) 60 | 更有效的利用缓存,避免读入无用的冷数据 61 | 经常一起使用的列放到一个表中(避免更多的关联操作) 62 | ``` 63 | 64 | ## 7. 禁止在表中建立预留字段 65 | 66 | 67 | 68 | ``` 69 | 预留字段的命名很难做到见名识义 70 | 预留字段无法确认存储的数据类型,所以无法选择合适的类型 71 | 对预留字段类型的修改,会对表进行锁定 72 | ``` 73 | 74 | ## 8. 禁止在数据库中存储图片,文件等大的二进制数据 75 | 76 | 77 | 78 | ``` 79 | 通常文件很大,会短时间内造成数据量快速增长,数据库进行数据库读取时,通常会进行大量的随机IO操作,文件很大时,IO操作很耗时 80 | 通常存储于文件服务器,数据库只存储文件地址信息 81 | ``` 82 | 83 | ## 9. 禁止在线上做数据库压力测试 84 | 85 | ## 10. 禁止从开发环境,测试环境直接连接生成环境数据库 86 | 87 | # 数据库字段设计规范 88 | 89 | ## 1. 优先选择符合存储需要的最小的数据类型 90 | 91 | 92 | 93 | ``` 94 | 原因是:列的字段越大,建立索引时所需要的空间也就越大,这样一页中所能存储的索引节点的数量也就越少也越少,在遍历时所需要的IO次数也就越多, 95 | 索引的性能也就越差 96 | 方法: 97 | ``` 98 | 99 | - 将字符串转换成数字类型存储,如:将IP地址转换成整形数据 100 | 101 | mysql提供了两个方法来处理ip地址 102 | 103 | inet_aton 把ip转为无符号整型(4-8位) 104 | inet_ntoa 把整型的ip转为地址 105 | 106 | 插入数据前,先用inet_aton把ip地址转为整型,可以节省空间 107 | 显示数据时,使用inet_ntoa把整型的ip地址转为地址显示即可。 108 | 109 | - 对于非负型的数据(如自增ID、整型IP)来说,要优先使用无符号整型来存储 110 | 111 | 因为:无符号相对于有符号可以多出一倍的存储空间 112 | SIGNED INT -2147483648~2147483647 113 | UNSIGNED INT 0~4294967295 114 | 115 | VARCHAR(N)中的N代表的是字符数,而不是字节数 116 | 使用UTF8存储255个汉字 Varchar(255)=765个字节 117 | 118 | 过大的长度会消耗更多的内存 119 | 120 | ## 2. 避免使用TEXT、BLOB数据类型,最常见的TEXT类型可以存储64k的数据 121 | 122 | - 建议把BLOB或是TEXT列分离到单独的扩展表中 123 | 124 | Mysql内存临时表不支持TEXT、BLOB这样的大数据类型,如果查询中包含这样的数据,在排序等操作时,就不能使用内存临时表,必须使用磁盘临时表进行 125 | 而且对于这种数据,Mysql还是要进行二次查询,会使sql性能变得很差,但是不是说一定不能使用这样的数据类型 126 | 127 | 如果一定要使用,建议把BLOB或是TEXT列分离到单独的扩展表中,查询时一定不要使用select * 而只需要取出必要的列,不需要TEXT列的数据时不要对该列进行查询 128 | 129 | - TEXT或BLOB类型只能使用前缀索引 130 | 131 | 因为MySQL对索引字段长度是有限制的,所以TEXT类型只能使用前缀索引,并且TEXT列上是不能有默认值的 132 | 133 | ## 3. 避免使用ENUM类型 134 | 135 | 修改ENUM值需要使用ALTER语句 136 | ENUM类型的ORDER BY操作效率低,需要额外操作 137 | 禁止使用数值作为ENUM的枚举值 138 | 139 | ## 4. 尽可能把所有列定义为NOT NULL 140 | 141 | 原因: 142 | 索引NULL列需要额外的空间来保存,所以要占用更多的空间 143 | 进行比较和计算时要对NULL值做特别的处理 144 | 145 | ## 5. 使用TIMESTAMP(4个字节)或DATETIME类型(8个字节)存储时间 146 | 147 | TIMESTAMP 存储的时间范围 1970-01-01 00:00:01 ~ 2038-01-19-03:14:07 148 | TIMESTAMP 占用4字节和INT相同,但比INT可读性高 149 | 超出TIMESTAMP取值范围的使用DATETIME类型存储 150 | 151 | 经常会有人用字符串存储日期型的数据(不正确的做法) 152 | 缺点1:无法用日期函数进行计算和比较 153 | 缺点2:用字符串存储日期要占用更多的空间 154 | 155 | ## 6. 同财务相关的金额类数据必须使用decimal类型 156 | 157 | - 非精准浮点:float,double 158 | - 精准浮点:decimal 159 | 160 | Decimal类型为精准浮点数,在计算时不会丢失精度 161 | 占用空间由定义的宽度决定,每4个字节可以存储9位数字,并且小数点要占用一个字节 162 | 可用于存储比bigint更大的整型数据 163 | 164 | # 索引设计规范 165 | 166 | ## 1. 限制每张表上的索引数量,建议单张表索引不超过5个 167 | 168 | 169 | 170 | ``` 171 | 索引并不是越多越好!索引可以提高效率同样可以降低效率 172 | 173 | 索引可以增加查询效率,但同样也会降低插入和更新的效率,甚至有些情况下会降低查询效率 174 | 175 | 因为mysql优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划,如果同时有很多个 176 | 索引都可以用于查询,就会增加mysql优化器生成执行计划的时间,同样会降低查询性能 177 | ``` 178 | 179 | ## 2. 禁止给表中的每一列都建立单独的索引 180 | 181 | 182 | 183 | ``` 184 | 5.6版本之前,一个sql只能使用到一个表中的一个索引,5.6以后,虽然有了合并索引的优化方式,但是还是远远没有使用一个联合索引的查询方式好 185 | ``` 186 | 187 | ## 3. 每个Innodb表必须有个主键 188 | 189 | 190 | 191 | ``` 192 | Innodb是一种索引组织表:数据的存储的逻辑顺序和索引的顺序是相同的 193 | 每个表都可以有多个索引,但是表的存储顺序只能有一种 194 | Innodb是按照主键索引的顺序来组织表的 195 | 196 | 不要使用更新频繁的列作为主键,不适用多列主键(相当于联合索引) 197 | 不要使用UUID,MD5,HASH,字符串列作为主键(无法保证数据的顺序增长) 198 | 主键建议使用自增ID值 199 | ``` 200 | 201 | # 常见索引列建议 202 | 203 | 1. 出现在SELECT、UPDATE、DELETE语句的WHERE从句中的列 204 | 205 | 2. 包含在ORDER BY、GROUP BY、DISTINCT中的字段 206 | 207 | 并不要将符合1和2中的字段的列都建立一个索引, 通常将1、2中的字段建立联合索引效果更好 208 | 209 | 3. 多表join的关联列 210 | 211 | # 如何选择索引列的顺序 212 | 213 | 214 | 215 | ``` 216 | 建立索引的目的是:希望通过索引进行数据查找,减少随机IO,增加查询性能 ,索引能过滤出越少的数据,则从磁盘中读入的数据也就越少 217 | ``` 218 | 219 | 1. 区分度最高的放在联合索引的最左侧(区分度=列中不同值的数量/列的总行数) 220 | 2. 尽量把字段长度小的列放在联合索引的最左侧(因为字段长度越小,一页能存储的数据量越大,IO性能也就越好) 221 | 3. 使用最频繁的列放到联合索引的左侧(这样可以比较少的建立一些索引) 222 | 223 | # 避免建立冗余索引和重复索引(增加了查询优化器生成执行计划的时间) 224 | 225 | 226 | 227 | ``` 228 | 重复索引示例:primary key(id)、index(id)、unique index(id) 229 | 冗余索引示例:index(a,b,c)、index(a,b)、index(a) 230 | ``` 231 | 232 | # 对于频繁的查询优先考虑使用覆盖索引 233 | 234 | 覆盖索引:就是包含了所有查询字段(where,select,ordery by,group by包含的字段)的索引 235 | 236 | 覆盖索引的好处: 237 | 238 | 1. 避免Innodb表进行索引的二次查询 239 | 240 | Innodb是以聚集索引的顺序来存储的,对于Innodb来说,二级索引在叶子节点中所保存的是行的主键信息, 241 | 如果是用二级索引查询数据的话,在查找到相应的键值后,还要通过主键进行二次查询才能获取我们真实所需要的数据 242 | 而在覆盖索引中,二级索引的键值中可以获取所有的数据,避免了对主键的二次查询 ,减少了IO操作,提升了查询效率 243 | 244 | 1. 可以把随机IO变成顺序IO加快查询效率 245 | 246 | 由于覆盖索引是按键值的顺序存储的,对于IO密集型的范围查找来说,对比随机从磁盘读取每一行的数据IO要少的多, 247 | 因此利用覆盖索引在访问时也可以把磁盘的随机读取的IO转变成索引查找的顺序IO 248 | 249 | # 索引SET规范 250 | 251 | ## 尽量避免使用外键约束 252 | 253 | 不建议使用外键约束(foreign key),但一定要在表与表之间的关联键上建立索引 254 | 外键可用于保证数据的参照完整性,但建议在业务端实现 255 | 外键会影响父表和子表的写操作从而降低性能 256 | 257 | # 数据库SQL开发规范 258 | 259 | ## 1. 建议使用预编译语句进行数据库操作 260 | 261 | 262 | 263 | ``` 264 | 预编译语句可以重复使用这些计划,减少SQL编译所需要的时间,还可以解决动态SQL所带来的SQL注入的问题 265 | 只传参数,比传递SQL语句更高效 266 | 相同语句可以一次解析,多次使用,提高处理效率 267 | ``` 268 | 269 | ## 2. 避免数据类型的隐式转换 270 | 271 | 272 | 273 | ``` 274 | 隐式转换会导致索引失效 275 | 如: select name,phone from customer where id = '111'; 276 | ``` 277 | 278 | ## 3. 充分利用表上已经存在的索引 279 | 280 | ### 避免使用双%号的查询条件。 281 | 282 | 如 a like '%123%',(如果无前置%,只有后置%,是可以用到列上的索引的) 283 | 284 | ### 一个SQL只能利用到复合索引中的一列进行范围查询 285 | 286 | 287 | 288 | ``` 289 | 如 有 a,b,c列的联合索引,在查询条件中有a列的范围查询,则在b,c列上的索引将不会被用到, 290 | 在定义联合索引时,如果a列要用到范围查找的话,就要把a列放到联合索引的右侧 291 | ``` 292 | 293 | ### 使用left join 或 not exists 来优化not in 操作 294 | 295 | 296 | 297 | ``` 298 | 因为not in 也通常会使用索引失效 299 | ``` 300 | 301 | ## 4. 数据库设计时,应该要对以后扩展进行考虑 302 | 303 | ## 5. 程序连接不同的数据库使用不同的账号,进制跨库查询 304 | 305 | 306 | 307 | ``` 308 | 为数据库迁移和分库分表留出余地 309 | 降低业务耦合度 310 | 避免权限过大而产生的安全风险 311 | ``` 312 | 313 | ## 6. 禁止使用SELECT * 必须使用SELECT <字段列表> 查询 314 | 315 | 316 | 317 | ``` 318 | 原因: 319 | 消耗更多的CPU和IO以网络带宽资源 320 | 无法使用覆盖索引 321 | 可减少表结构变更带来的影响 322 | ``` 323 | 324 | ## 7. 禁止使用不含字段列表的INSERT语句 325 | 326 | 327 | 328 | ``` 329 | 如: insert into values ('a','b','c'); 330 | 应使用 insert into t(c1,c2,c3) values ('a','b','c'); 331 | ``` 332 | 333 | ## 8. 避免使用子查询,可以把子查询优化为join操作 334 | 335 | 336 | 337 | ``` 338 | 通常子查询在in子句中,且子查询中为简单SQL(不包含union、group by、order by、limit从句)时,才可以把子查询转化为关联查询进行优化 339 | 340 | 子查询性能差的原因: 341 | 342 | 子查询的结果集无法使用索引,通常子查询的结果集会被存储到临时表中,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响 343 | 特别是对于返回结果集比较大的子查询,其对查询性能的影响也就越大 344 | 由于子查询会产生大量的临时表也没有索引,所以会消耗过多的CPU和IO资源,产生大量的慢查询 345 | ``` 346 | 347 | ## 9. 避免使用JOIN关联太多的表 348 | 349 | 350 | 351 | ``` 352 | 对于Mysql来说,是存在关联缓存的,缓存的大小可以由join_buffer_size参数进行设置 353 | 在Mysql中,对于同一个SQL多关联(join)一个表,就会多分配一个关联缓存,如果在一个SQL中关联的表越多, 354 | 所占用的内存也就越大 355 | 356 | 如果程序中大量的使用了多表关联的操作,同时join_buffer_size设置的也不合理的情况下,就容易造成服务器内存溢出的情况, 357 | 就会影响到服务器数据库性能的稳定性 358 | 359 | 同时对于关联操作来说,会产生临时表操作,影响查询效率 360 | Mysql最多允许关联61个表,建议不超过5个 361 | ``` 362 | 363 | ## 10. 减少同数据库的交互次数 364 | 365 | 366 | 367 | ``` 368 | 数据库更适合处理批量操作 369 | 合并多个相同的操作到一起,可以提高处理效率 370 | ``` 371 | 372 | ## 11. 对应同一列进行or判断时,使用in代替or 373 | 374 | 375 | 376 | ``` 377 | in 的值不要超过500个 378 | in 操作可以更有效的利用索引,or大多数情况下很少能利用到索引 379 | ``` 380 | 381 | ## 12. 禁止使用order by rand() 进行随机排序 382 | 383 | 384 | 385 | ``` 386 | 会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值,如果满足条件的数据集非常大, 387 | 就会消耗大量的CPU和IO及内存资源 388 | 推荐在程序中获取一个随机值,然后从数据库中获取数据的方式 389 | ``` 390 | 391 | ## 13. WHERE从句中禁止对列进行函数转换和计算 392 | 393 | 394 | 395 | ``` 396 | 对列进行函数转换或计算时会导致无法使用索引 397 | 398 | 不推荐: 399 | where date(create_time)='20190101' 400 | 推荐: 401 | where create_time >= '20190101' and create_time < '20190102' 402 | ``` 403 | 404 | ## 14. 在明显不会有重复值时使用UNION ALL 而不是UNION 405 | 406 | 407 | 408 | ``` 409 | UNION 会把两个结果集的所有数据放到临时表中后再进行去重操作 410 | UNION ALL 不会再对结果集进行去重操作 411 | ``` 412 | 413 | ## 15. 拆分复杂的大SQL为多个小SQL 414 | 415 | 416 | 417 | ``` 418 | 大SQL:逻辑上比较复杂,需要占用大量CPU进行计算的SQL 419 | MySQL 一个SQL只能使用一个CPU进行计算 420 | SQL拆分后可以通过并行执行来提高处理效率 421 | ``` 422 | 423 | # 数据库操作行为规范 424 | 425 | ## 超100万行的批量写(UPDATE、DELETE、INSERT)操作,要分批多次进行操作 426 | 427 | ### 1. 大批量操作可能会造成严重的主从延迟 428 | 429 | 主从环境中,大批量操作可能会造成严重的主从延迟,大批量的写操作一般都需要执行一定长的时间, 430 | 而只有当主库上执行完成后,才会在其他从库上执行,所以会造成主库与从库长时间的延迟情况 431 | 432 | ### 2. binlog日志为row格式时会产生大量的日志 433 | 434 | 大批量写操作会产生大量日志,特别是对于row格式二进制数据而言,由于在row格式中会记录每一行数据的修改,我们一次修改的数据越多, 435 | 产生的日志量也就会越多,日志的传输和恢复所需要的时间也就越长,这也是造成主从延迟的一个原因 436 | 437 | ### 3. 避免产生大事务操作 438 | 439 | 大批量修改数据,一定是在一个事务中进行的,这就会造成表中大批量数据进行锁定,从而导致大量的阻塞,阻塞会对MySQL的性能产生非常大的影响 440 | 特别是长时间的阻塞会占满所有数据库的可用连接,这会使生产环境中的其他应用无法连接到数据库,因此一定要注意大批量写操作要进行分批 441 | 442 | ## 对于大表使用pt-online-schema-change修改表结构 443 | 444 | 1. 避免大表修改产生的主从延迟 445 | 2. 避免在对表字段进行修改时进行锁表 446 | 447 | 对大表数据结构的修改一定要谨慎,会造成严重的锁表操作,尤其是生产环境,是不能容忍的 448 | 449 | pt-online-schema-change它会首先建立一个与原表结构相同的新表,并且在新表上进行表结构的修改,然后再把原表中的数据复制到新表中,并在原表中增加一些触发器 450 | 把原表中新增的数据也复制到新表中,在行所有数据复制完成之后,把新表命名成原表,并把原来的表删除掉 451 | 把原来一个DDL操作,分解成多个小的批次进行 452 | 453 | ## 禁止为程序使用的账号赋予super权限 454 | 455 | 456 | 457 | ``` 458 | 当达到最大连接数限制时,还运行1个有super权限的用户连接 459 | super权限只能留给DBA处理问题的账号使用 460 | ``` 461 | 462 | ## 对于程序连接数据库账号,遵循权限最小原则 463 | 464 | 465 | 466 | ``` 467 | 程序使用数据库账号只能在一个DB下使用,不准跨库 468 | 程序使用的账号原则上不准有drop权限 469 | ``` 470 | 471 | > 原文链接:https://www.cnblogs.com/huchong/p/10219318.html 472 | > 473 | > 作者:**[听风。](https://www.cnblogs.com/huchong/p/10219318.html)** 474 | 475 | -------------------------------------------------------------------------------- /docs/python_ Interview.md: -------------------------------------------------------------------------------- 1 | # Python语言特性 2 | 3 | ## 1 Python的函数参数传递 4 | 5 | 看两个例子: 6 | 7 | ``` 8 | a = 1 9 | def fun(a): 10 | a = 2 11 | fun(a) 12 | print a # 1 13 | a = [] 14 | def fun(a): 15 | a.append(1) 16 | fun(a) 17 | print a # [1] 18 | ``` 19 | 20 | 所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。 21 | 22 | 通过`id`来看引用`a`的内存地址可以比较理解: 23 | 24 | ``` 25 | a = 1 26 | def fun(a): 27 | print "func_in",id(a) # func_in 41322472 28 | a = 2 29 | print "re-point",id(a), id(2) # re-point 41322448 41322448 30 | print "func_out",id(a), id(1) # func_out 41322472 41322472 31 | fun(a) 32 | print a # 1 33 | ``` 34 | 35 | 注:具体的值在不同电脑上运行时可能不同。 36 | 37 | 可以看到,在执行完`a = 2`之后,`a`引用中保存的值,即内存地址发生变化,由原来`1`对象的所在的地址变成了`2`这个实体对象的内存地址。 38 | 39 | 而第2个例子`a`引用保存的内存值就不会发生变化: 40 | 41 | ``` 42 | a = [] 43 | def fun(a): 44 | print "func_in",id(a) # func_in 53629256 45 | a.append(1) 46 | print "func_out",id(a) # func_out 53629256 47 | fun(a) 48 | print a # [1] 49 | ``` 50 | 51 | 这里记住的是类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而 list, dict, set 等则是可以修改的对象。(这就是这个问题的重点) 52 | 53 | 当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改. 54 | 55 | 如果还不明白的话,这里有更好的解释: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference 56 | 57 | ## 2 Python中的元类(metaclass) 58 | 59 | 这个非常的不常用,但是像ORM这种复杂的结构还是会需要的,详情请看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 60 | 61 | ## 3 @staticmethod和@classmethod 62 | 63 | Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下: 64 | 65 | ``` 66 | def foo(x): 67 | print "executing foo(%s)"%(x) 68 | 69 | class A(object): 70 | def foo(self,x): 71 | print "executing foo(%s,%s)"%(self,x) 72 | 73 | @classmethod 74 | def class_foo(cls,x): 75 | print "executing class_foo(%s,%s)"%(cls,x) 76 | 77 | @staticmethod 78 | def static_foo(x): 79 | print "executing static_foo(%s)"%x 80 | 81 | a=A() 82 | ``` 83 | 84 | 这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定,对于一般的函数来说我们可以这么调用`foo(x)`,这个函数就是最常用的,它的工作跟任何东西(类,实例)无关.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是`foo(self, x)`,为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的`a.foo(x)`(其实是`foo(a, x)`).类方法一样,只不过它传递的是类而不是实例,`A.class_foo(x)`.注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好. 85 | 86 | 对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用`a.static_foo(x)`或者`A.static_foo(x)`来调用. 87 | 88 | | \ | 实例方法 | 类方法 | 静态方法 | 89 | | ------- | -------- | -------------- | --------------- | 90 | | a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) | 91 | | A | 不可用 | A.class_foo(x) | A.static_foo(x) | 92 | 93 | 更多关于这个问题: 94 | 95 | 1. http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python 96 | 2. https://realpython.com/blog/python/instance-class-and-static-methods-demystified/ 97 | 98 | ## 4 类变量和实例变量 99 | 100 | **类变量:** 101 | 102 | > 是可在类的所有实例之间共享的值(也就是说,它们不是单独分配给每个实例的)。例如下例中,num_of_instance 就是类变量,用于跟踪存在着多少个Test 的实例。 103 | 104 | **实例变量:** 105 | 106 | > 实例化之后,每个实例单独拥有的变量。 107 | 108 | ``` 109 | class Test(object): 110 | num_of_instance = 0 111 | def __init__(self, name): 112 | self.name = name 113 | Test.num_of_instance += 1 114 | 115 | if __name__ == '__main__': 116 | print Test.num_of_instance # 0 117 | t1 = Test('jack') 118 | print Test.num_of_instance # 1 119 | t2 = Test('lucy') 120 | print t1.name , t1.num_of_instance # jack 2 121 | print t2.name , t2.num_of_instance # lucy 2 122 | ``` 123 | 124 | > 补充的例子 125 | 126 | ``` 127 | class Person: 128 | name="aaa" 129 | 130 | p1=Person() 131 | p2=Person() 132 | p1.name="bbb" 133 | print p1.name # bbb 134 | print p2.name # aaa 135 | print Person.name # aaa 136 | ``` 137 | 138 | 这里`p1.name="bbb"`是实例调用了类变量,这其实和上面第一个问题一样,就是函数传参的问题,`p1.name`一开始是指向的类变量`name="aaa"`,但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了. 139 | 140 | 可以看看下面的例子: 141 | 142 | ``` 143 | class Person: 144 | name=[] 145 | 146 | p1=Person() 147 | p2=Person() 148 | p1.name.append(1) 149 | print p1.name # [1] 150 | print p2.name # [1] 151 | print Person.name # [1] 152 | ``` 153 | 154 | 参考:http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block 155 | 156 | ## 5 Python自省 157 | 158 | 这个也是python彪悍的特性. 159 | 160 | 自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance(). 161 | 162 | ``` 163 | a = [1,2,3] 164 | b = {'a':1,'b':2,'c':3} 165 | c = True 166 | print type(a),type(b),type(c) # 167 | print isinstance(a,list) # True 168 | ``` 169 | 170 | ## 6 字典推导式 171 | 172 | 可能你见过列表推导时,却没有见过字典推导式,在2.7中才加入的: 173 | 174 | ``` 175 | d = {key: value for (key, value) in iterable} 176 | ``` 177 | 178 | ## 7 Python中单下划线和双下划线 179 | 180 | ``` 181 | >>> class MyClass(): 182 | ... def __init__(self): 183 | ... self.__superprivate = "Hello" 184 | ... self._semiprivate = ", world!" 185 | ... 186 | >>> mc = MyClass() 187 | >>> print mc.__superprivate 188 | Traceback (most recent call last): 189 | File "", line 1, in 190 | AttributeError: myClass instance has no attribute '__superprivate' 191 | >>> print mc._semiprivate 192 | , world! 193 | >>> print mc.__dict__ 194 | {'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'} 195 | ``` 196 | 197 | `__foo__`:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突,就是例如`__init__()`,`__del__()`,`__call__()`这些特殊方法 198 | 199 | `_foo`:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.不能用from module import * 导入,其他方面和公有一样访问; 200 | 201 | `__foo`:这个有真正的意义:解析器用`_classname__foo`来代替这个名字,以区别和其他类相同的命名,它无法直接像公有成员一样随便访问,通过对象名._类名__xxx这样的方式可以访问. 202 | 203 | 详情见:http://stackoverflow.com/questions/1301346/the-meaning-of-a-single-and-a-double-underscore-before-an-object-name-in-python 204 | 205 | 或者: http://www.zhihu.com/question/19754941 206 | 207 | ## 8 字符串格式化:%和.format 208 | 209 | .format在许多方面看起来更便利.对于`%`最烦人的是它无法同时传递一个变量和元组.你可能会想下面的代码不会有什么问题: 210 | 211 | ``` 212 | "hi there %s" % name 213 | ``` 214 | 215 | 但是,如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做: 216 | 217 | ``` 218 | "hi there %s" % (name,) # 提供一个单元素的数组而不是一个参数 219 | ``` 220 | 221 | 但是有点丑..format就没有这些问题.你给的第二个问题也是这样,.format好看多了. 222 | 223 | 你为什么不用它? 224 | 225 | - 不知道它(在读这个之前) 226 | - 为了和Python2.5兼容(譬如logging库建议使用`%`([issue #4](https://github.com/taizilongxu/interview_python/issues/4))) 227 | 228 | http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format 229 | 230 | ## 9 迭代器和生成器 231 | 232 | 这个是stackoverflow里python排名第一的问题,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python 233 | 234 | 这是中文版: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/1/README.html 235 | 236 | 这里有个关于生成器的创建问题面试官有考: 问: 将列表生成式中[]改成() 之后数据结构是否改变? 答案:是,从列表变为生成器 237 | 238 | ``` 239 | >>> L = [x*x for x in range(10)] 240 | >>> L 241 | [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 242 | >>> g = (x*x for x in range(10)) 243 | >>> g 244 | at 0x0000028F8B774200> 245 | ``` 246 | 247 | 通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问前面的几个元素,后面大部分元素所占的空间都是浪费的。因此,没有必要创建完整的列表(节省大量内存空间)。在Python中,我们可以采用生成器:边循环,边计算的机制—>generator 248 | 249 | ## 10 `*args` and `**kwargs` 250 | 251 | 用`*args`和`**kwargs`只是为了方便并没有强制使用它们. 252 | 253 | 当你不确定你的函数里将要传递多少参数时你可以用`*args`.例如,它可以传递任意数量的参数: 254 | 255 | ``` 256 | >>> def print_everything(*args): 257 | for count, thing in enumerate(args): 258 | ... print '{0}. {1}'.format(count, thing) 259 | ... 260 | >>> print_everything('apple', 'banana', 'cabbage') 261 | 0. apple 262 | 1. banana 263 | 2. cabbage 264 | ``` 265 | 266 | 相似的,`**kwargs`允许你使用没有事先定义的参数名: 267 | 268 | ``` 269 | >>> def table_things(**kwargs): 270 | ... for name, value in kwargs.items(): 271 | ... print '{0} = {1}'.format(name, value) 272 | ... 273 | >>> table_things(apple = 'fruit', cabbage = 'vegetable') 274 | cabbage = vegetable 275 | apple = fruit 276 | ``` 277 | 278 | 你也可以混着用.命名参数首先获得参数值然后所有的其他参数都传递给`*args`和`**kwargs`.命名参数在列表的最前端.例如: 279 | 280 | ``` 281 | def table_things(titlestring, **kwargs) 282 | ``` 283 | 284 | `*args`和`**kwargs`可以同时在函数的定义中,但是`*args`必须在`**kwargs`前面. 285 | 286 | 当调用函数时你也可以用`*`和`**`语法.例如: 287 | 288 | ``` 289 | >>> def print_three_things(a, b, c): 290 | ... print 'a = {0}, b = {1}, c = {2}'.format(a,b,c) 291 | ... 292 | >>> mylist = ['aardvark', 'baboon', 'cat'] 293 | >>> print_three_things(*mylist) 294 | 295 | a = aardvark, b = baboon, c = cat 296 | ``` 297 | 298 | 就像你看到的一样,它可以传递列表(或者元组)的每一项并把它们解包.注意必须与它们在函数里的参数相吻合.当然,你也可以在函数定义或者函数调用时用*. 299 | 300 | http://stackoverflow.com/questions/3394835/args-and-kwargs 301 | 302 | ## 11 面向切面编程AOP和装饰器 303 | 304 | 这个AOP一听起来有点懵,同学面阿里的时候就被问懵了... 305 | 306 | 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,**装饰器的作用就是为已经存在的对象添加额外的功能。** 307 | 308 | 这个问题比较大,推荐: http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python 309 | 310 | 中文: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/3/README.html 311 | 312 | ## 12 鸭子类型 313 | 314 | “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 315 | 316 | 我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。 317 | 318 | 比如在python中,有很多file-like的东西,比如StringIO,GzipFile,socket。它们有很多相同的方法,我们把它们当作文件使用。 319 | 320 | 又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器等. 321 | 322 | 鸭子类型在动态语言中经常使用,非常灵活,使得python不像java那样专门去弄一大堆的设计模式。 323 | 324 | ## 13 Python中重载 325 | 326 | 引自知乎:http://www.zhihu.com/question/20053359 327 | 328 | 函数重载主要是为了解决两个问题。 329 | 330 | 1. 可变参数类型。 331 | 2. 可变参数个数。 332 | 333 | 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。 334 | 335 | 好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。 336 | 337 | 那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。 338 | 339 | 好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。 340 | 341 | ## 14 新式类和旧式类 342 | 343 | 这个面试官问了,我说了老半天,不知道他问的真正意图是什么. 344 | 345 | [stackoverflow](http://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python) 346 | 347 | 这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html 348 | 349 | 新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类继承是根据C3算法,旧式类是深度优先),里讲的也很多. 350 | 351 | > 一个旧式类的深度优先的例子 352 | 353 | ``` 354 | class A(): 355 | def foo1(self): 356 | print "A" 357 | class B(A): 358 | def foo2(self): 359 | pass 360 | class C(A): 361 | def foo1(self): 362 | print "C" 363 | class D(B, C): 364 | pass 365 | 366 | d = D() 367 | d.foo1() 368 | 369 | # A 370 | ``` 371 | 372 | **按照经典类的查找顺序`从左到右深度优先`的规则,在访问`d.foo1()`的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过** 373 | 374 | ## 15 `__new__`和`__init__`的区别 375 | 376 | 这个`__new__`确实很少见到,先做了解吧. 377 | 378 | 1. `__new__`是一个静态方法,而`__init__`是一个实例方法. 379 | 2. `__new__`方法会返回一个创建的实例,而`__init__`什么都不返回. 380 | 3. 只有在`__new__`返回一个cls的实例时后面的`__init__`才能被调用. 381 | 4. 当创建一个新实例时调用`__new__`,初始化一个实例时用`__init__`. 382 | 383 | [stackoverflow](http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init) 384 | 385 | ps: `__metaclass__`是创建类时起作用.所以我们可以分别使用`__metaclass__`,`__new__`和`__init__`来分别在类创建,实例创建和实例初始化的时候做一些小手脚. 386 | 387 | ## 16 单例模式 388 | 389 | > 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。 390 | > 391 | > `__new__()`在`__init__()`之前被调用,用于生成实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。单例模式是指创建唯一对象,单例模式设计的类只能实例 **这个绝对常考啊.绝对要记住1~2个方法,当时面试官是让手写的.** 392 | 393 | ### 1 使用`__new__`方法 394 | 395 | ``` 396 | class Singleton(object): 397 | def __new__(cls, *args, **kw): 398 | if not hasattr(cls, '_instance'): 399 | orig = super(Singleton, cls) 400 | cls._instance = orig.__new__(cls, *args, **kw) 401 | return cls._instance 402 | 403 | class MyClass(Singleton): 404 | a = 1 405 | ``` 406 | 407 | ### 2 共享属性 408 | 409 | 创建实例时把所有实例的`__dict__`指向同一个字典,这样它们具有相同的属性和方法. 410 | 411 | ``` 412 | class Borg(object): 413 | _state = {} 414 | def __new__(cls, *args, **kw): 415 | ob = super(Borg, cls).__new__(cls, *args, **kw) 416 | ob.__dict__ = cls._state 417 | return ob 418 | 419 | class MyClass2(Borg): 420 | a = 1 421 | ``` 422 | 423 | ### 3 装饰器版本 424 | 425 | ``` 426 | def singleton(cls): 427 | instances = {} 428 | def getinstance(*args, **kw): 429 | if cls not in instances: 430 | instances[cls] = cls(*args, **kw) 431 | return instances[cls] 432 | return getinstance 433 | 434 | @singleton 435 | class MyClass: 436 | ... 437 | ``` 438 | 439 | ### 4 import方法 440 | 441 | 作为python的模块是天然的单例模式 442 | 443 | ``` 444 | # mysingleton.py 445 | class My_Singleton(object): 446 | def foo(self): 447 | pass 448 | 449 | my_singleton = My_Singleton() 450 | 451 | # to use 452 | from mysingleton import my_singleton 453 | 454 | my_singleton.foo() 455 | ``` 456 | 457 | **[单例模式伯乐在线详细解释](http://python.jobbole.com/87294/)** 458 | 459 | ## 17 Python中的作用域 460 | 461 | Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。 462 | 463 | 当 Python 遇到一个变量的话他会按照这样的顺序进行搜索: 464 | 465 | 本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in) 466 | 467 | ## 18 GIL线程全局锁 468 | 469 | 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.**对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。** 470 | 471 | 见[Python 最难的问题](http://www.oschina.net/translate/pythons-hardest-problem) 472 | 473 | 解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能). 474 | 475 | ## 19 协程 476 | 477 | 知乎被问到了,呵呵哒,跪了 478 | 479 | 简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态. 480 | 481 | Python里最常见的yield就是协程的思想!可以查看第九个问题. 482 | 483 | ## 20 闭包 484 | 485 | 闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。 486 | 487 | 当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点: 488 | 489 | 1. 必须有一个内嵌函数 490 | 2. 内嵌函数必须引用外部函数中的变量 491 | 3. 外部函数的返回值必须是内嵌函数 492 | 493 | 感觉闭包还是有难度的,几句话是说不明白的,还是查查相关资料. 494 | 495 | 重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上. 496 | 497 | 闭包就像个空心球一样,你知道外面和里面,但你不知道中间是什么样. 498 | 499 | ## 21 lambda函数 500 | 501 | 其实就是一个匿名函数,为什么叫lambda?因为和后面的函数式编程有关. 502 | 503 | 推荐: [知乎](http://www.zhihu.com/question/20125256) 504 | 505 | ## 22 Python函数式编程 506 | 507 | 这个需要适当的了解一下吧,毕竟函数式编程在Python中也做了引用. 508 | 509 | 推荐: [酷壳](http://coolshell.cn/articles/10822.html) 510 | 511 | python中函数式编程支持: 512 | 513 | filter 函数的功能相当于过滤器。调用一个布尔函数`bool_func`来迭代遍历每个seq中的元素;返回一个使`bool_seq`返回值为true的元素的序列。 514 | 515 | ``` 516 | >>>a = [1,2,3,4,5,6,7] 517 | >>>b = filter(lambda x: x > 5, a) 518 | >>>print b 519 | >>>[6,7] 520 | ``` 521 | 522 | map函数是对一个序列的每个项依次执行函数,下面是对一个序列每个项都乘以2: 523 | 524 | ``` 525 | >>> a = map(lambda x:x*2,[1,2,3]) 526 | >>> list(a) 527 | [2, 4, 6] 528 | ``` 529 | 530 | reduce函数是对一个序列的每个项迭代调用函数,下面是求3的阶乘: 531 | 532 | ``` 533 | >>> reduce(lambda x,y:x*y,range(1,4)) 534 | 6 535 | ``` 536 | 537 | ## 23 Python里的拷贝 538 | 539 | 引用和copy(),deepcopy()的区别 540 | 541 | ``` 542 | import copy 543 | a = [1, 2, 3, 4, ['a', 'b']] #原始对象 544 | 545 | b = a #赋值,传对象的引用 546 | c = copy.copy(a) #对象拷贝,浅拷贝 547 | d = copy.deepcopy(a) #对象拷贝,深拷贝 548 | 549 | a.append(5) #修改对象a 550 | a[4].append('c') #修改对象a中的['a', 'b']数组对象 551 | 552 | print 'a = ', a 553 | print 'b = ', b 554 | print 'c = ', c 555 | print 'd = ', d 556 | 557 | 输出结果: 558 | a = [1, 2, 3, 4, ['a', 'b', 'c'], 5] 559 | b = [1, 2, 3, 4, ['a', 'b', 'c'], 5] 560 | c = [1, 2, 3, 4, ['a', 'b', 'c']] 561 | d = [1, 2, 3, 4, ['a', 'b']] 562 | ``` 563 | 564 | ## 24 Python垃圾回收机制 565 | 566 | Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。 567 | 568 | ### 1 引用计数 569 | 570 | PyObject是每个对象必有的内容,其中`ob_refcnt`就是做为引用计数。当一个对象有新的引用时,它的`ob_refcnt`就会增加,当引用它的对象被删除,它的`ob_refcnt`就会减少.引用计数为0时,该对象生命就结束了。 571 | 572 | 优点: 573 | 574 | 1. 简单 575 | 2. 实时性 576 | 577 | 缺点: 578 | 579 | 1. 维护引用计数消耗资源 580 | 2. 循环引用 581 | 582 | ### 2 标记-清除机制 583 | 584 | 基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。 585 | 586 | ### 3 分代技术 587 | 588 | 分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。 589 | 590 | Python默认定义了三代对象集合,索引数越大,对象存活时间越长。 591 | 592 | 举例: 当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。 593 | 594 | ## 25 Python的List 595 | 596 | 推荐: http://www.jianshu.com/p/J4U6rR 597 | 598 | ## 26 Python的is 599 | 600 | is是对比地址,==是对比值 601 | 602 | ## 27 read,readline和readlines 603 | 604 | - read 读取整个文件 605 | - readline 读取下一行,使用生成器方法 606 | - readlines 读取整个文件到一个迭代器以供我们遍历 607 | 608 | ## 28 Python2和3的区别 609 | 610 | 推荐:[Python 2.7.x 与 Python 3.x 的主要差异](http://chenqx.github.io/2014/11/10/Key-differences-between-Python-2-7-x-and-Python-3-x/) 611 | 612 | 613 | 614 | -------------------------------------------------------------------------------- /docs/一千行MySQL命令.md: -------------------------------------------------------------------------------- 1 | ### 基本操作 2 | 3 | ``` 4 | /* Windows服务 */ 5 | -- 启动MySQL 6 | net start mysql 7 | -- 创建Windows服务 8 | sc create mysql binPath= mysqld_bin_path(注意:等号与值之间有空格) 9 | /* 连接与断开服务器 */ 10 | mysql -h 地址 -P 端口 -u 用户名 -p 密码 11 | SHOW PROCESSLIST -- 显示哪些线程正在运行 12 | SHOW VARIABLES -- 显示系统变量信息 13 | ``` 14 | 15 | ### 数据库操作 16 | 17 | ``` 18 | /* 数据库操作 */ ------------------ 19 | -- 查看当前数据库 20 | SELECT DATABASE(); 21 | -- 显示当前时间、用户名、数据库版本 22 | SELECT now(), user(), version(); 23 | -- 创建库 24 | CREATE DATABASE[ IF NOT EXISTS] 数据库名 数据库选项 25 | 数据库选项: 26 | CHARACTER SET charset_name 27 | COLLATE collation_name 28 | -- 查看已有库 29 | SHOW DATABASES[ LIKE 'PATTERN'] 30 | -- 查看当前库信息 31 | SHOW CREATE DATABASE 数据库名 32 | -- 修改库的选项信息 33 | ALTER DATABASE 库名 选项信息 34 | -- 删除库 35 | DROP DATABASE[ IF EXISTS] 数据库名 36 | 同时删除该数据库相关的目录及其目录内容 37 | ``` 38 | 39 | ### 表的操作 40 | 41 | ``` 42 | -- 创建表 43 | CREATE [TEMPORARY] TABLE[ IF NOT EXISTS] [库名.]表名 ( 表的结构定义 )[ 表选项] 44 | 每个字段必须有数据类型 45 | 最后一个字段后不能有逗号 46 | TEMPORARY 临时表,会话结束时表自动消失 47 | 对于字段的定义: 48 | 字段名 数据类型 [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY] [COMMENT 'string'] 49 | -- 表选项 50 | -- 字符集 51 | CHARSET = charset_name 52 | 如果表没有设定,则使用数据库字符集 53 | -- 存储引擎 54 | ENGINE = engine_name 55 | 表在管理数据时采用的不同的数据结构,结构不同会导致处理方式、提供的特性操作等不同 56 | 常见的引擎:InnoDB MyISAM Memory/Heap BDB Merge Example CSV MaxDB Archive 57 | 不同的引擎在保存表的结构和数据时采用不同的方式 58 | MyISAM表文件含义:.frm表定义,.MYD表数据,.MYI表索引 59 | InnoDB表文件含义:.frm表定义,表空间数据和日志文件 60 | SHOW ENGINES -- 显示存储引擎的状态信息 61 | SHOW ENGINE 引擎名 {LOGS|STATUS} -- 显示存储引擎的日志或状态信息 62 | -- 自增起始数 63 | AUTO_INCREMENT = 行数 64 | -- 数据文件目录 65 | DATA DIRECTORY = '目录' 66 | -- 索引文件目录 67 | INDEX DIRECTORY = '目录' 68 | -- 表注释 69 | COMMENT = 'string' 70 | -- 分区选项 71 | PARTITION BY ... (详细见手册) 72 | -- 查看所有表 73 | SHOW TABLES[ LIKE 'pattern'] 74 | SHOW TABLES FROM 库名 75 | -- 查看表结构 76 | SHOW CREATE TABLE 表名 (信息更详细) 77 | DESC 表名 / DESCRIBE 表名 / EXPLAIN 表名 / SHOW COLUMNS FROM 表名 [LIKE 'PATTERN'] 78 | SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern'] 79 | -- 修改表 80 | -- 修改表本身的选项 81 | ALTER TABLE 表名 表的选项 82 | eg: ALTER TABLE 表名 ENGINE=MYISAM; 83 | -- 对表进行重命名 84 | RENAME TABLE 原表名 TO 新表名 85 | RENAME TABLE 原表名 TO 库名.表名 (可将表移动到另一个数据库) 86 | -- RENAME可以交换两个表名 87 | -- 修改表的字段机构(13.1.2. ALTER TABLE语法) 88 | ALTER TABLE 表名 操作名 89 | -- 操作名 90 | ADD[ COLUMN] 字段定义 -- 增加字段 91 | AFTER 字段名 -- 表示增加在该字段名后面 92 | FIRST -- 表示增加在第一个 93 | ADD PRIMARY KEY(字段名) -- 创建主键 94 | ADD UNIQUE [索引名] (字段名)-- 创建唯一索引 95 | ADD INDEX [索引名] (字段名) -- 创建普通索引 96 | DROP[ COLUMN] 字段名 -- 删除字段 97 | MODIFY[ COLUMN] 字段名 字段属性 -- 支持对字段属性进行修改,不能修改字段名(所有原有属性也需写上) 98 | CHANGE[ COLUMN] 原字段名 新字段名 字段属性 -- 支持对字段名修改 99 | DROP PRIMARY KEY -- 删除主键(删除主键前需删除其AUTO_INCREMENT属性) 100 | DROP INDEX 索引名 -- 删除索引 101 | DROP FOREIGN KEY 外键 -- 删除外键 102 | -- 删除表 103 | DROP TABLE[ IF EXISTS] 表名 ... 104 | -- 清空表数据 105 | TRUNCATE [TABLE] 表名 106 | -- 复制表结构 107 | CREATE TABLE 表名 LIKE 要复制的表名 108 | -- 复制表结构和数据 109 | CREATE TABLE 表名 [AS] SELECT * FROM 要复制的表名 110 | -- 检查表是否有错误 111 | CHECK TABLE tbl_name [, tbl_name] ... [option] ... 112 | -- 优化表 113 | OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... 114 | -- 修复表 115 | REPAIR [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... [QUICK] [EXTENDED] [USE_FRM] 116 | -- 分析表 117 | ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... 118 | ``` 119 | 120 | ### 数据操作 121 | 122 | ``` 123 | /* 数据操作 */ ------------------ 124 | -- 增 125 | INSERT [INTO] 表名 [(字段列表)] VALUES (值列表)[, (值列表), ...] 126 | -- 如果要插入的值列表包含所有字段并且顺序一致,则可以省略字段列表。 127 | -- 可同时插入多条数据记录! 128 | REPLACE 与 INSERT 完全一样,可互换。 129 | INSERT [INTO] 表名 SET 字段名=值[, 字段名=值, ...] 130 | -- 查 131 | SELECT 字段列表 FROM 表名[ 其他子句] 132 | -- 可来自多个表的多个字段 133 | -- 其他子句可以不使用 134 | -- 字段列表可以用*代替,表示所有字段 135 | -- 删 136 | DELETE FROM 表名[ 删除条件子句] 137 | 没有条件子句,则会删除全部 138 | -- 改 139 | UPDATE 表名 SET 字段名=新值[, 字段名=新值] [更新条件] 140 | ``` 141 | 142 | ### 字符集编码 143 | 144 | ``` 145 | /* 字符集编码 */ ------------------ 146 | -- MySQL、数据库、表、字段均可设置编码 147 | -- 数据编码与客户端编码不需一致 148 | SHOW VARIABLES LIKE 'character_set_%' -- 查看所有字符集编码项 149 | character_set_client 客户端向服务器发送数据时使用的编码 150 | character_set_results 服务器端将结果返回给客户端所使用的编码 151 | character_set_connection 连接层编码 152 | SET 变量名 = 变量值 153 | SET character_set_client = gbk; 154 | SET character_set_results = gbk; 155 | SET character_set_connection = gbk; 156 | SET NAMES GBK; -- 相当于完成以上三个设置 157 | -- 校对集 158 | 校对集用以排序 159 | SHOW CHARACTER SET [LIKE 'pattern']/SHOW CHARSET [LIKE 'pattern'] 查看所有字符集 160 | SHOW COLLATION [LIKE 'pattern'] 查看所有校对集 161 | CHARSET 字符集编码 设置字符集编码 162 | COLLATE 校对集编码 设置校对集编码 163 | ``` 164 | 165 | ### 数据类型(列类型) 166 | 167 | ``` 168 | /* 数据类型(列类型) */ ------------------ 169 | 1. 数值类型 170 | -- a. 整型 ---------- 171 | 类型 字节 范围(有符号位) 172 | tinyint 1字节 -128 ~ 127 无符号位:0 ~ 255 173 | smallint 2字节 -32768 ~ 32767 174 | mediumint 3字节 -8388608 ~ 8388607 175 | int 4字节 176 | bigint 8字节 177 | int(M) M表示总位数 178 | - 默认存在符号位,unsigned 属性修改 179 | - 显示宽度,如果某个数不够定义字段时设置的位数,则前面以0补填,zerofill 属性修改 180 | 例:int(5) 插入一个数'123',补填后为'00123' 181 | - 在满足要求的情况下,越小越好。 182 | - 1表示bool值真,0表示bool值假。MySQL没有布尔类型,通过整型0和1表示。常用tinyint(1)表示布尔型。 183 | -- b. 浮点型 ---------- 184 | 类型 字节 范围 185 | float(单精度) 4字节 186 | double(双精度) 8字节 187 | 浮点型既支持符号位 unsigned 属性,也支持显示宽度 zerofill 属性。 188 | 不同于整型,前后均会补填0. 189 | 定义浮点型时,需指定总位数和小数位数。 190 | float(M, D) double(M, D) 191 | M表示总位数,D表示小数位数。 192 | M和D的大小会决定浮点数的范围。不同于整型的固定范围。 193 | M既表示总位数(不包括小数点和正负号),也表示显示宽度(所有显示符号均包括)。 194 | 支持科学计数法表示。 195 | 浮点数表示近似值。 196 | -- c. 定点数 ---------- 197 | decimal -- 可变长度 198 | decimal(M, D) M也表示总位数,D表示小数位数。 199 | 保存一个精确的数值,不会发生数据的改变,不同于浮点数的四舍五入。 200 | 将浮点数转换为字符串来保存,每9位数字保存为4个字节。 201 | 2. 字符串类型 202 | -- a. char, varchar ---------- 203 | char 定长字符串,速度快,但浪费空间 204 | varchar 变长字符串,速度慢,但节省空间 205 | M表示能存储的最大长度,此长度是字符数,非字节数。 206 | 不同的编码,所占用的空间不同。 207 | char,最多255个字符,与编码无关。 208 | varchar,最多65535字符,与编码有关。 209 | 一条有效记录最大不能超过65535个字节。 210 | utf8 最大为21844个字符,gbk 最大为32766个字符,latin1 最大为65532个字符 211 | varchar 是变长的,需要利用存储空间保存 varchar 的长度,如果数据小于255个字节,则采用一个字节来保存长度,反之需要两个字节来保存。 212 | varchar 的最大有效长度由最大行大小和使用的字符集确定。 213 | 最大有效长度是65532字节,因为在varchar存字符串时,第一个字节是空的,不存在任何数据,然后还需两个字节来存放字符串的长度,所以有效长度是65535-1-2=65532字节。 214 | 例:若一个表定义为 CREATE TABLE tb(c1 int, c2 char(30), c3 varchar(N)) charset=utf8; 问N的最大值是多少? 答:(65535-1-2-4-30*3)/3 215 | -- b. blob, text ---------- 216 | blob 二进制字符串(字节字符串) 217 | tinyblob, blob, mediumblob, longblob 218 | text 非二进制字符串(字符字符串) 219 | tinytext, text, mediumtext, longtext 220 | text 在定义时,不需要定义长度,也不会计算总长度。 221 | text 类型在定义时,不可给default值 222 | -- c. binary, varbinary ---------- 223 | 类似于char和varchar,用于保存二进制字符串,也就是保存字节字符串而非字符字符串。 224 | char, varchar, text 对应 binary, varbinary, blob. 225 | 3. 日期时间类型 226 | 一般用整型保存时间戳,因为PHP可以很方便的将时间戳进行格式化。 227 | datetime 8字节 日期及时间 1000-01-01 00:00:00 到 9999-12-31 23:59:59 228 | date 3字节 日期 1000-01-01 到 9999-12-31 229 | timestamp 4字节 时间戳 19700101000000 到 2038-01-19 03:14:07 230 | time 3字节 时间 -838:59:59 到 838:59:59 231 | year 1字节 年份 1901 - 2155 232 | datetime YYYY-MM-DD hh:mm:ss 233 | timestamp YY-MM-DD hh:mm:ss 234 | YYYYMMDDhhmmss 235 | YYMMDDhhmmss 236 | YYYYMMDDhhmmss 237 | YYMMDDhhmmss 238 | date YYYY-MM-DD 239 | YY-MM-DD 240 | YYYYMMDD 241 | YYMMDD 242 | YYYYMMDD 243 | YYMMDD 244 | time hh:mm:ss 245 | hhmmss 246 | hhmmss 247 | year YYYY 248 | YY 249 | YYYY 250 | YY 251 | 4. 枚举和集合 252 | -- 枚举(enum) ---------- 253 | enum(val1, val2, val3...) 254 | 在已知的值中进行单选。最大数量为65535. 255 | 枚举值在保存时,以2个字节的整型(smallint)保存。每个枚举值,按保存的位置顺序,从1开始逐一递增。 256 | 表现为字符串类型,存储却是整型。 257 | NULL值的索引是NULL。 258 | 空字符串错误值的索引值是0。 259 | -- 集合(set) ---------- 260 | set(val1, val2, val3...) 261 | create table tab ( gender set('男', '女', '无') ); 262 | insert into tab values ('男, 女'); 263 | 最多可以有64个不同的成员。以bigint存储,共8个字节。采取位运算的形式。 264 | 当创建表时,SET成员值的尾部空格将自动被删除。 265 | ``` 266 | 267 | ### 列属性(列约束) 268 | 269 | ``` 270 | /* 列属性(列约束) */ ------------------ 271 | 1. PRIMARY 主键 272 | - 能唯一标识记录的字段,可以作为主键。 273 | - 一个表只能有一个主键。 274 | - 主键具有唯一性。 275 | - 声明字段时,用 primary key 标识。 276 | 也可以在字段列表之后声明 277 | 例:create table tab ( id int, stu varchar(10), primary key (id)); 278 | - 主键字段的值不能为null。 279 | - 主键可以由多个字段共同组成。此时需要在字段列表后声明的方法。 280 | 例:create table tab ( id int, stu varchar(10), age int, primary key (stu, age)); 281 | 2. UNIQUE 唯一索引(唯一约束) 282 | 使得某字段的值也不能重复。 283 | 3. NULL 约束 284 | null不是数据类型,是列的一个属性。 285 | 表示当前列是否可以为null,表示什么都没有。 286 | null, 允许为空。默认。 287 | not null, 不允许为空。 288 | insert into tab values (null, 'val'); 289 | -- 此时表示将第一个字段的值设为null, 取决于该字段是否允许为null 290 | 4. DEFAULT 默认值属性 291 | 当前字段的默认值。 292 | insert into tab values (default, 'val'); -- 此时表示强制使用默认值。 293 | create table tab ( add_time timestamp default current_timestamp ); 294 | -- 表示将当前时间的时间戳设为默认值。 295 | current_date, current_time 296 | 5. AUTO_INCREMENT 自动增长约束 297 | 自动增长必须为索引(主键或unique) 298 | 只能存在一个字段为自动增长。 299 | 默认为1开始自动增长。可以通过表属性 auto_increment = x进行设置,或 alter table tbl auto_increment = x; 300 | 6. COMMENT 注释 301 | 例:create table tab ( id int ) comment '注释内容'; 302 | 7. FOREIGN KEY 外键约束 303 | 用于限制主表与从表数据完整性。 304 | alter table t1 add constraint `t1_t2_fk` foreign key (t1_id) references t2(id); 305 | -- 将表t1的t1_id外键关联到表t2的id字段。 306 | -- 每个外键都有一个名字,可以通过 constraint 指定 307 | 存在外键的表,称之为从表(子表),外键指向的表,称之为主表(父表)。 308 | 作用:保持数据一致性,完整性,主要目的是控制存储在外键表(从表)中的数据。 309 | MySQL中,可以对InnoDB引擎使用外键约束: 310 | 语法: 311 | foreign key (外键字段) references 主表名 (关联字段) [主表记录删除时的动作] [主表记录更新时的动作] 312 | 此时需要检测一个从表的外键需要约束为主表的已存在的值。外键在没有关联的情况下,可以设置为null.前提是该外键列,没有not null。 313 | 可以不指定主表记录更改或更新时的动作,那么此时主表的操作被拒绝。 314 | 如果指定了 on update 或 on delete:在删除或更新时,有如下几个操作可以选择: 315 | 1. cascade,级联操作。主表数据被更新(主键值更新),从表也被更新(外键值更新)。主表记录被删除,从表相关记录也被删除。 316 | 2. set null,设置为null。主表数据被更新(主键值更新),从表的外键被设置为null。主表记录被删除,从表相关记录外键被设置成null。但注意,要求该外键列,没有not null属性约束。 317 | 3. restrict,拒绝父表删除和更新。 318 | 注意,外键只被InnoDB存储引擎所支持。其他引擎是不支持的。 319 | ``` 320 | 321 | ### 建表规范 322 | 323 | ``` 324 | /* 建表规范 */ ------------------ 325 | -- Normal Format, NF 326 | - 每个表保存一个实体信息 327 | - 每个具有一个ID字段作为主键 328 | - ID主键 + 原子表 329 | -- 1NF, 第一范式 330 | 字段不能再分,就满足第一范式。 331 | -- 2NF, 第二范式 332 | 满足第一范式的前提下,不能出现部分依赖。 333 | 消除复合主键就可以避免部分依赖。增加单列关键字。 334 | -- 3NF, 第三范式 335 | 满足第二范式的前提下,不能出现传递依赖。 336 | 某个字段依赖于主键,而有其他字段依赖于该字段。这就是传递依赖。 337 | 将一个实体信息的数据放在一个表内实现。 338 | ``` 339 | 340 | ### SELECT 341 | 342 | ``` 343 | /* SELECT */ ------------------ 344 | SELECT [ALL|DISTINCT] select_expr FROM -> WHERE -> GROUP BY [合计函数] -> HAVING -> ORDER BY -> LIMIT 345 | a. select_expr 346 | -- 可以用 * 表示所有字段。 347 | select * from tb; 348 | -- 可以使用表达式(计算公式、函数调用、字段也是个表达式) 349 | select stu, 29+25, now() from tb; 350 | -- 可以为每个列使用别名。适用于简化列标识,避免多个列标识符重复。 351 | - 使用 as 关键字,也可省略 as. 352 | select stu+10 as add10 from tb; 353 | b. FROM 子句 354 | 用于标识查询来源。 355 | -- 可以为表起别名。使用as关键字。 356 | SELECT * FROM tb1 AS tt, tb2 AS bb; 357 | -- from子句后,可以同时出现多个表。 358 | -- 多个表会横向叠加到一起,而数据会形成一个笛卡尔积。 359 | SELECT * FROM tb1, tb2; 360 | -- 向优化符提示如何选择索引 361 | USE INDEX、IGNORE INDEX、FORCE INDEX 362 | SELECT * FROM table1 USE INDEX (key1,key2) WHERE key1=1 AND key2=2 AND key3=3; 363 | SELECT * FROM table1 IGNORE INDEX (key3) WHERE key1=1 AND key2=2 AND key3=3; 364 | c. WHERE 子句 365 | -- 从from获得的数据源中进行筛选。 366 | -- 整型1表示真,0表示假。 367 | -- 表达式由运算符和运算数组成。 368 | -- 运算数:变量(字段)、值、函数返回值 369 | -- 运算符: 370 | =, <=>, <>, !=, <=, <, >=, >, !, &&, ||, 371 | in (not) null, (not) like, (not) in, (not) between and, is (not), and, or, not, xor 372 | is/is not 加上ture/false/unknown,检验某个值的真假 373 | <=>与<>功能相同,<=>可用于null比较 374 | d. GROUP BY 子句, 分组子句 375 | GROUP BY 字段/别名 [排序方式] 376 | 分组后会进行排序。升序:ASC,降序:DESC 377 | 以下[合计函数]需配合 GROUP BY 使用: 378 | count 返回不同的非NULL值数目 count(*)、count(字段) 379 | sum 求和 380 | max 求最大值 381 | min 求最小值 382 | avg 求平均值 383 | group_concat 返回带有来自一个组的连接的非NULL值的字符串结果。组内字符串连接。 384 | e. HAVING 子句,条件子句 385 | 与 where 功能、用法相同,执行时机不同。 386 | where 在开始时执行检测数据,对原数据进行过滤。 387 | having 对筛选出的结果再次进行过滤。 388 | having 字段必须是查询出来的,where 字段必须是数据表存在的。 389 | where 不可以使用字段的别名,having 可以。因为执行WHERE代码时,可能尚未确定列值。 390 | where 不可以使用合计函数。一般需用合计函数才会用 having 391 | SQL标准要求HAVING必须引用GROUP BY子句中的列或用于合计函数中的列。 392 | f. ORDER BY 子句,排序子句 393 | order by 排序字段/别名 排序方式 [,排序字段/别名 排序方式]... 394 | 升序:ASC,降序:DESC 395 | 支持多个字段的排序。 396 | g. LIMIT 子句,限制结果数量子句 397 | 仅对处理好的结果进行数量限制。将处理好的结果的看作是一个集合,按照记录出现的顺序,索引从0开始。 398 | limit 起始位置, 获取条数 399 | 省略第一个参数,表示从索引0开始。limit 获取条数 400 | h. DISTINCT, ALL 选项 401 | distinct 去除重复记录 402 | 默认为 all, 全部记录 403 | ``` 404 | 405 | ### UNION 406 | 407 | ``` 408 | /* UNION */ ------------------ 409 | 将多个select查询的结果组合成一个结果集合。 410 | SELECT ... UNION [ALL|DISTINCT] SELECT ... 411 | 默认 DISTINCT 方式,即所有返回的行都是唯一的 412 | 建议,对每个SELECT查询加上小括号包裹。 413 | ORDER BY 排序时,需加上 LIMIT 进行结合。 414 | 需要各select查询的字段数量一样。 415 | 每个select查询的字段列表(数量、类型)应一致,因为结果中的字段名以第一条select语句为准。 416 | ``` 417 | 418 | ### 子查询 419 | 420 | ``` 421 | /* 子查询 */ ------------------ 422 | - 子查询需用括号包裹。 423 | -- from型 424 | from后要求是一个表,必须给子查询结果取个别名。 425 | - 简化每个查询内的条件。 426 | - from型需将结果生成一个临时表格,可用以原表的锁定的释放。 427 | - 子查询返回一个表,表型子查询。 428 | select * from (select * from tb where id>0) as subfrom where id>1; 429 | -- where型 430 | - 子查询返回一个值,标量子查询。 431 | - 不需要给子查询取别名。 432 | - where子查询内的表,不能直接用以更新。 433 | select * from tb where money = (select max(money) from tb); 434 | -- 列子查询 435 | 如果子查询结果返回的是一列。 436 | 使用 in 或 not in 完成查询 437 | exists 和 not exists 条件 438 | 如果子查询返回数据,则返回1或0。常用于判断条件。 439 | select column1 from t1 where exists (select * from t2); 440 | -- 行子查询 441 | 查询条件是一个行。 442 | select * from t1 where (id, gender) in (select id, gender from t2); 443 | 行构造符:(col1, col2, ...) 或 ROW(col1, col2, ...) 444 | 行构造符通常用于与对能返回两个或两个以上列的子查询进行比较。 445 | -- 特殊运算符 446 | != all() 相当于 not in 447 | = some() 相当于 in。any 是 some 的别名 448 | != some() 不等同于 not in,不等于其中某一个。 449 | all, some 可以配合其他运算符一起使用。 450 | ``` 451 | 452 | ### 连接查询(join) 453 | 454 | ``` 455 | /* 连接查询(join) */ ------------------ 456 | 将多个表的字段进行连接,可以指定连接条件。 457 | -- 内连接(inner join) 458 | - 默认就是内连接,可省略inner。 459 | - 只有数据存在时才能发送连接。即连接结果不能出现空行。 460 | on 表示连接条件。其条件表达式与where类似。也可以省略条件(表示条件永远为真) 461 | 也可用where表示连接条件。 462 | 还有 using, 但需字段名相同。 using(字段名) 463 | -- 交叉连接 cross join 464 | 即,没有条件的内连接。 465 | select * from tb1 cross join tb2; 466 | -- 外连接(outer join) 467 | - 如果数据不存在,也会出现在连接结果中。 468 | -- 左外连接 left join 469 | 如果数据不存在,左表记录会出现,而右表为null填充 470 | -- 右外连接 right join 471 | 如果数据不存在,右表记录会出现,而左表为null填充 472 | -- 自然连接(natural join) 473 | 自动判断连接条件完成连接。 474 | 相当于省略了using,会自动查找相同字段名。 475 | natural join 476 | natural left join 477 | natural right join 478 | select info.id, info.name, info.stu_num, extra_info.hobby, extra_info.sex from info, extra_info where info.stu_num = extra_info.stu_id; 479 | ``` 480 | 481 | ### TRUNCATE 482 | 483 | ``` 484 | /* TRUNCATE */ ------------------ 485 | TRUNCATE [TABLE] tbl_name 486 | 清空数据 487 | 删除重建表 488 | 区别: 489 | 1,truncate 是删除表再创建,delete 是逐条删除 490 | 2,truncate 重置auto_increment的值。而delete不会 491 | 3,truncate 不知道删除了几条,而delete知道。 492 | 4,当被用于带分区的表时,truncate 会保留分区 493 | ``` 494 | 495 | ### 备份与还原 496 | 497 | ``` 498 | /* 备份与还原 */ ------------------ 499 | 备份,将数据的结构与表内数据保存起来。 500 | 利用 mysqldump 指令完成。 501 | -- 导出 502 | mysqldump [options] db_name [tables] 503 | mysqldump [options] ---database DB1 [DB2 DB3...] 504 | mysqldump [options] --all--database 505 | 1. 导出一张表 506 |   mysqldump -u用户名 -p密码 库名 表名 > 文件名(D:/a.sql) 507 | 2. 导出多张表 508 |   mysqldump -u用户名 -p密码 库名 表1 表2 表3 > 文件名(D:/a.sql) 509 | 3. 导出所有表 510 |   mysqldump -u用户名 -p密码 库名 > 文件名(D:/a.sql) 511 | 4. 导出一个库 512 |   mysqldump -u用户名 -p密码 --lock-all-tables --database 库名 > 文件名(D:/a.sql) 513 | 可以-w携带WHERE条件 514 | -- 导入 515 | 1. 在登录mysql的情况下: 516 |   source 备份文件 517 | 2. 在不登录的情况下 518 |   mysql -u用户名 -p密码 库名 < 备份文件 519 | ``` 520 | 521 | ### 视图 522 | 523 | ``` 524 | 什么是视图: 525 | 视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。 526 | 视图具有表结构文件,但不存在数据文件。 527 | 对其中所引用的基础表来说,视图的作用类似于筛选。定义视图的筛选可以来自当前或其它数据库的一个或多个表,或者其它视图。通过视图进行查询没有任何限制,通过它们进行数据修改时的限制也很少。 528 | 视图是存储在数据库中的查询的sql语句,它主要出于两种原因:安全原因,视图可以隐藏一些数据,如:社会保险基金表,可以用视图只显示姓名,地址,而不显示社会保险号和工资数等,另一原因是可使复杂的查询易于理解和使用。 529 | -- 创建视图 530 | CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW view_name [(column_list)] AS select_statement 531 | - 视图名必须唯一,同时不能与表重名。 532 | - 视图可以使用select语句查询到的列名,也可以自己指定相应的列名。 533 | - 可以指定视图执行的算法,通过ALGORITHM指定。 534 | - column_list如果存在,则数目必须等于SELECT语句检索的列数 535 | -- 查看结构 536 | SHOW CREATE VIEW view_name 537 | -- 删除视图 538 | - 删除视图后,数据依然存在。 539 | - 可同时删除多个视图。 540 | DROP VIEW [IF EXISTS] view_name ... 541 | -- 修改视图结构 542 | - 一般不修改视图,因为不是所有的更新视图都会映射到表上。 543 | ALTER VIEW view_name [(column_list)] AS select_statement 544 | -- 视图作用 545 | 1. 简化业务逻辑 546 | 2. 对客户端隐藏真实的表结构 547 | -- 视图算法(ALGORITHM) 548 | MERGE 合并 549 | 将视图的查询语句,与外部查询需要先合并再执行! 550 | TEMPTABLE 临时表 551 | 将视图执行完毕后,形成临时表,再做外层查询! 552 | UNDEFINED 未定义(默认),指的是MySQL自主去选择相应的算法。 553 | ``` 554 | 555 | ### 事务(transaction) 556 | 557 | ``` 558 | 事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。 559 | - 支持连续SQL的集体成功或集体撤销。 560 | - 事务是数据库在数据完整性方面的一个功能。 561 | - 需要利用 InnoDB 或 BDB 存储引擎,对自动提交的特性支持完成。 562 | - InnoDB被称为事务安全型引擎。 563 | -- 事务开启 564 | START TRANSACTION; 或者 BEGIN; 565 | 开启事务后,所有被执行的SQL语句均被认作当前事务内的SQL语句。 566 | -- 事务提交 567 | COMMIT; 568 | -- 事务回滚 569 | ROLLBACK; 570 | 如果部分操作发生问题,映射到事务开启前。 571 | -- 事务的特性 572 | 1. 原子性(Atomicity) 573 | 事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 574 | 2. 一致性(Consistency) 575 | 事务前后数据的完整性必须保持一致。 576 | - 事务开始和结束时,外部数据一致 577 | - 在整个事务过程中,操作是连续的 578 | 3. 隔离性(Isolation) 579 | 多个用户并发访问数据库时,一个用户的事务不能被其它用户的事物所干扰,多个并发事务之间的数据要相互隔离。 580 | 4. 持久性(Durability) 581 | 一个事务一旦被提交,它对数据库中的数据改变就是永久性的。 582 | -- 事务的实现 583 | 1. 要求是事务支持的表类型 584 | 2. 执行一组相关的操作前开启事务 585 | 3. 整组操作完成后,都成功,则提交;如果存在失败,选择回滚,则会回到事务开始的备份点。 586 | -- 事务的原理 587 | 利用InnoDB的自动提交(autocommit)特性完成。 588 | 普通的MySQL执行语句后,当前的数据提交操作均可被其他客户端可见。 589 | 而事务是暂时关闭“自动提交”机制,需要commit提交持久化数据操作。 590 | -- 注意 591 | 1. 数据定义语言(DDL)语句不能被回滚,比如创建或取消数据库的语句,和创建、取消或更改表或存储的子程序的语句。 592 | 2. 事务不能被嵌套 593 | -- 保存点 594 | SAVEPOINT 保存点名称 -- 设置一个事务保存点 595 | ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点 596 | RELEASE SAVEPOINT 保存点名称 -- 删除保存点 597 | -- InnoDB自动提交特性设置 598 | SET autocommit = 0|1; 0表示关闭自动提交,1表示开启自动提交。 599 | - 如果关闭了,那普通操作的结果对其他客户端也不可见,需要commit提交后才能持久化数据操作。 600 | - 也可以关闭自动提交来开启事务。但与START TRANSACTION不同的是, 601 | SET autocommit是永久改变服务器的设置,直到下次再次修改该设置。(针对当前连接) 602 | 而START TRANSACTION记录开启前的状态,而一旦事务提交或回滚后就需要再次开启事务。(针对当前事务) 603 | ``` 604 | 605 | ### 锁表 606 | 607 | ``` 608 | /* 锁表 */ 609 | 表锁定只用于防止其它客户端进行不正当地读取和写入 610 | MyISAM 支持表锁,InnoDB 支持行锁 611 | -- 锁定 612 | LOCK TABLES tbl_name [AS alias] 613 | -- 解锁 614 | UNLOCK TABLES 615 | ``` 616 | 617 | ### 触发器 618 | 619 | ``` 620 | /* 触发器 */ ------------------ 621 | 触发程序是与表有关的命名数据库对象,当该表出现特定事件时,将激活该对象 622 | 监听:记录的增加、修改、删除。 623 | -- 创建触发器 624 | CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt 625 | 参数: 626 | trigger_time是触发程序的动作时间。它可以是 before 或 after,以指明触发程序是在激活它的语句之前或之后触发。 627 | trigger_event指明了激活触发程序的语句的类型 628 | INSERT:将新行插入表时激活触发程序 629 | UPDATE:更改某一行时激活触发程序 630 | DELETE:从表中删除某一行时激活触发程序 631 | tbl_name:监听的表,必须是永久性的表,不能将触发程序与TEMPORARY表或视图关联起来。 632 | trigger_stmt:当触发程序激活时执行的语句。执行多个语句,可使用BEGIN...END复合语句结构 633 | -- 删除 634 | DROP TRIGGER [schema_name.]trigger_name 635 | 可以使用old和new代替旧的和新的数据 636 | 更新操作,更新前是old,更新后是new. 637 | 删除操作,只有old. 638 | 增加操作,只有new. 639 | -- 注意 640 | 1. 对于具有相同触发程序动作时间和事件的给定表,不能有两个触发程序。 641 | -- 字符连接函数 642 | concat(str1,str2,...]) 643 | concat_ws(separator,str1,str2,...) 644 | -- 分支语句 645 | if 条件 then 646 | 执行语句 647 | elseif 条件 then 648 | 执行语句 649 | else 650 | 执行语句 651 | end if; 652 | -- 修改最外层语句结束符 653 | delimiter 自定义结束符号 654 | SQL语句 655 | 自定义结束符号 656 | delimiter ; -- 修改回原来的分号 657 | -- 语句块包裹 658 | begin 659 | 语句块 660 | end 661 | -- 特殊的执行 662 | 1. 只要添加记录,就会触发程序。 663 | 2. Insert into on duplicate key update 语法会触发: 664 | 如果没有重复记录,会触发 before insert, after insert; 665 | 如果有重复记录并更新,会触发 before insert, before update, after update; 666 | 如果有重复记录但是没有发生更新,则触发 before insert, before update 667 | 3. Replace 语法 如果有记录,则执行 before insert, before delete, after delete, after insert 668 | ``` 669 | 670 | ### SQL编程 671 | 672 | ``` 673 | /* SQL编程 */ ------------------ 674 | --// 局部变量 ---------- 675 | -- 变量声明 676 | declare var_name[,...] type [default value] 677 | 这个语句被用来声明局部变量。要给变量提供一个默认值,请包含一个default子句。值可以被指定为一个表达式,不需要为一个常数。如果没有default子句,初始值为null。 678 | -- 赋值 679 | 使用 set 和 select into 语句为变量赋值。 680 | - 注意:在函数内是可以使用全局变量(用户自定义的变量) 681 | --// 全局变量 ---------- 682 | -- 定义、赋值 683 | set 语句可以定义并为变量赋值。 684 | set @var = value; 685 | 也可以使用select into语句为变量初始化并赋值。这样要求select语句只能返回一行,但是可以是多个字段,就意味着同时为多个变量进行赋值,变量的数量需要与查询的列数一致。 686 | 还可以把赋值语句看作一个表达式,通过select执行完成。此时为了避免=被当作关系运算符看待,使用:=代替。(set语句可以使用= 和 :=)。 687 | select @var:=20; 688 | select @v1:=id, @v2=name from t1 limit 1; 689 | select * from tbl_name where @var:=30; 690 | select into 可以将表中查询获得的数据赋给变量。 691 | -| select max(height) into @max_height from tb; 692 | -- 自定义变量名 693 | 为了避免select语句中,用户自定义的变量与系统标识符(通常是字段名)冲突,用户自定义变量在变量名前使用@作为开始符号。 694 | @var=10; 695 | - 变量被定义后,在整个会话周期都有效(登录到退出) 696 | --// 控制结构 ---------- 697 | -- if语句 698 | if search_condition then 699 | statement_list 700 | [elseif search_condition then 701 | statement_list] 702 | ... 703 | [else 704 | statement_list] 705 | end if; 706 | -- case语句 707 | CASE value WHEN [compare-value] THEN result 708 | [WHEN [compare-value] THEN result ...] 709 | [ELSE result] 710 | END 711 | -- while循环 712 | [begin_label:] while search_condition do 713 | statement_list 714 | end while [end_label]; 715 | - 如果需要在循环内提前终止 while循环,则需要使用标签;标签需要成对出现。 716 | -- 退出循环 717 | 退出整个循环 leave 718 | 退出当前循环 iterate 719 | 通过退出的标签决定退出哪个循环 720 | --// 内置函数 ---------- 721 | -- 数值函数 722 | abs(x) -- 绝对值 abs(-10.9) = 10 723 | format(x, d) -- 格式化千分位数值 format(1234567.456, 2) = 1,234,567.46 724 | ceil(x) -- 向上取整 ceil(10.1) = 11 725 | floor(x) -- 向下取整 floor (10.1) = 10 726 | round(x) -- 四舍五入去整 727 | mod(m, n) -- m%n m mod n 求余 10%3=1 728 | pi() -- 获得圆周率 729 | pow(m, n) -- m^n 730 | sqrt(x) -- 算术平方根 731 | rand() -- 随机数 732 | truncate(x, d) -- 截取d位小数 733 | -- 时间日期函数 734 | now(), current_timestamp(); -- 当前日期时间 735 | current_date(); -- 当前日期 736 | current_time(); -- 当前时间 737 | date('yyyy-mm-dd hh:ii:ss'); -- 获取日期部分 738 | time('yyyy-mm-dd hh:ii:ss'); -- 获取时间部分 739 | date_format('yyyy-mm-dd hh:ii:ss', '%d %y %a %d %m %b %j'); -- 格式化时间 740 | unix_timestamp(); -- 获得unix时间戳 741 | from_unixtime(); -- 从时间戳获得时间 742 | -- 字符串函数 743 | length(string) -- string长度,字节 744 | char_length(string) -- string的字符个数 745 | substring(str, position [,length]) -- 从str的position开始,取length个字符 746 | replace(str ,search_str ,replace_str) -- 在str中用replace_str替换search_str 747 | instr(string ,substring) -- 返回substring首次在string中出现的位置 748 | concat(string [,...]) -- 连接字串 749 | charset(str) -- 返回字串字符集 750 | lcase(string) -- 转换成小写 751 | left(string, length) -- 从string2中的左边起取length个字符 752 | load_file(file_name) -- 从文件读取内容 753 | locate(substring, string [,start_position]) -- 同instr,但可指定开始位置 754 | lpad(string, length, pad) -- 重复用pad加在string开头,直到字串长度为length 755 | ltrim(string) -- 去除前端空格 756 | repeat(string, count) -- 重复count次 757 | rpad(string, length, pad) --在str后用pad补充,直到长度为length 758 | rtrim(string) -- 去除后端空格 759 | strcmp(string1 ,string2) -- 逐字符比较两字串大小 760 | -- 流程函数 761 | case when [condition] then result [when [condition] then result ...] [else result] end 多分支 762 | if(expr1,expr2,expr3) 双分支。 763 | -- 聚合函数 764 | count() 765 | sum(); 766 | max(); 767 | min(); 768 | avg(); 769 | group_concat() 770 | -- 其他常用函数 771 | md5(); 772 | default(); 773 | --// 存储函数,自定义函数 ---------- 774 | -- 新建 775 | CREATE FUNCTION function_name (参数列表) RETURNS 返回值类型 776 | 函数体 777 | - 函数名,应该合法的标识符,并且不应该与已有的关键字冲突。 778 | - 一个函数应该属于某个数据库,可以使用db_name.funciton_name的形式执行当前函数所属数据库,否则为当前数据库。 779 | - 参数部分,由"参数名"和"参数类型"组成。多个参数用逗号隔开。 780 | - 函数体由多条可用的mysql语句,流程控制,变量声明等语句构成。 781 | - 多条语句应该使用 begin...end 语句块包含。 782 | - 一定要有 return 返回值语句。 783 | -- 删除 784 | DROP FUNCTION [IF EXISTS] function_name; 785 | -- 查看 786 | SHOW FUNCTION STATUS LIKE 'partten' 787 | SHOW CREATE FUNCTION function_name; 788 | -- 修改 789 | ALTER FUNCTION function_name 函数选项 790 | --// 存储过程,自定义功能 ---------- 791 | -- 定义 792 | 存储存储过程 是一段代码(过程),存储在数据库中的sql组成。 793 | 一个存储过程通常用于完成一段业务逻辑,例如报名,交班费,订单入库等。 794 | 而一个函数通常专注与某个功能,视为其他程序服务的,需要在其他语句中调用函数才可以,而存储过程不能被其他调用,是自己执行 通过call执行。 795 | -- 创建 796 | CREATE PROCEDURE sp_name (参数列表) 797 | 过程体 798 | 参数列表:不同于函数的参数列表,需要指明参数类型 799 | IN,表示输入型 800 | OUT,表示输出型 801 | INOUT,表示混合型 802 | 注意,没有返回值。 803 | ``` 804 | 805 | ### 存储过程 806 | 807 | ``` 808 | /* 存储过程 */ ------------------ 809 | 存储过程是一段可执行性代码的集合。相比函数,更偏向于业务逻辑。 810 | 调用:CALL 过程名 811 | -- 注意 812 | - 没有返回值。 813 | - 只能单独调用,不可夹杂在其他语句中 814 | -- 参数 815 | IN|OUT|INOUT 参数名 数据类型 816 | IN 输入:在调用过程中,将数据输入到过程体内部的参数 817 | OUT 输出:在调用过程中,将过程体处理完的结果返回到客户端 818 | INOUT 输入输出:既可输入,也可输出 819 | -- 语法 820 | CREATE PROCEDURE 过程名 (参数列表) 821 | BEGIN 822 | 过程体 823 | END 824 | ``` 825 | 826 | ### 用户和权限管理 827 | 828 | ``` 829 | /* 用户和权限管理 */ ------------------ 830 | -- root密码重置 831 | 1. 停止MySQL服务 832 | 2. [Linux] /usr/local/mysql/bin/safe_mysqld --skip-grant-tables & 833 | [Windows] mysqld --skip-grant-tables 834 | 3. use mysql; 835 | 4. UPDATE `user` SET PASSWORD=PASSWORD("密码") WHERE `user` = "root"; 836 | 5. FLUSH PRIVILEGES; 837 | 用户信息表:mysql.user 838 | -- 刷新权限 839 | FLUSH PRIVILEGES; 840 | -- 增加用户 841 | CREATE USER 用户名 IDENTIFIED BY [PASSWORD] 密码(字符串) 842 | - 必须拥有mysql数据库的全局CREATE USER权限,或拥有INSERT权限。 843 | - 只能创建用户,不能赋予权限。 844 | - 用户名,注意引号:如 'user_name'@'192.168.1.1' 845 | - 密码也需引号,纯数字密码也要加引号 846 | - 要在纯文本中指定密码,需忽略PASSWORD关键词。要把密码指定为由PASSWORD()函数返回的混编值,需包含关键字PASSWORD 847 | -- 重命名用户 848 | RENAME USER old_user TO new_user 849 | -- 设置密码 850 | SET PASSWORD = PASSWORD('密码') -- 为当前用户设置密码 851 | SET PASSWORD FOR 用户名 = PASSWORD('密码') -- 为指定用户设置密码 852 | -- 删除用户 853 | DROP USER 用户名 854 | -- 分配权限/添加用户 855 | GRANT 权限列表 ON 表名 TO 用户名 [IDENTIFIED BY [PASSWORD] 'password'] 856 | - all privileges 表示所有权限 857 | - *.* 表示所有库的所有表 858 | - 库名.表名 表示某库下面的某表 859 | GRANT ALL PRIVILEGES ON `pms`.* TO 'pms'@'%' IDENTIFIED BY 'pms0817'; 860 | -- 查看权限 861 | SHOW GRANTS FOR 用户名 862 | -- 查看当前用户权限 863 | SHOW GRANTS; 或 SHOW GRANTS FOR CURRENT_USER; 或 SHOW GRANTS FOR CURRENT_USER(); 864 | -- 撤消权限 865 | REVOKE 权限列表 ON 表名 FROM 用户名 866 | REVOKE ALL PRIVILEGES, GRANT OPTION FROM 用户名 -- 撤销所有权限 867 | -- 权限层级 868 | -- 要使用GRANT或REVOKE,您必须拥有GRANT OPTION权限,并且您必须用于您正在授予或撤销的权限。 869 | 全局层级:全局权限适用于一个给定服务器中的所有数据库,mysql.user 870 | GRANT ALL ON *.*和 REVOKE ALL ON *.*只授予和撤销全局权限。 871 | 数据库层级:数据库权限适用于一个给定数据库中的所有目标,mysql.db, mysql.host 872 | GRANT ALL ON db_name.*和REVOKE ALL ON db_name.*只授予和撤销数据库权限。 873 | 表层级:表权限适用于一个给定表中的所有列,mysql.talbes_priv 874 | GRANT ALL ON db_name.tbl_name和REVOKE ALL ON db_name.tbl_name只授予和撤销表权限。 875 | 列层级:列权限适用于一个给定表中的单一列,mysql.columns_priv 876 | 当使用REVOKE时,您必须指定与被授权列相同的列。 877 | -- 权限列表 878 | ALL [PRIVILEGES] -- 设置除GRANT OPTION之外的所有简单权限 879 | ALTER -- 允许使用ALTER TABLE 880 | ALTER ROUTINE -- 更改或取消已存储的子程序 881 | CREATE -- 允许使用CREATE TABLE 882 | CREATE ROUTINE -- 创建已存储的子程序 883 | CREATE TEMPORARY TABLES -- 允许使用CREATE TEMPORARY TABLE 884 | CREATE USER -- 允许使用CREATE USER, DROP USER, RENAME USER和REVOKE ALL PRIVILEGES。 885 | CREATE VIEW -- 允许使用CREATE VIEW 886 | DELETE -- 允许使用DELETE 887 | DROP -- 允许使用DROP TABLE 888 | EXECUTE -- 允许用户运行已存储的子程序 889 | FILE -- 允许使用SELECT...INTO OUTFILE和LOAD DATA INFILE 890 | INDEX -- 允许使用CREATE INDEX和DROP INDEX 891 | INSERT -- 允许使用INSERT 892 | LOCK TABLES -- 允许对您拥有SELECT权限的表使用LOCK TABLES 893 | PROCESS -- 允许使用SHOW FULL PROCESSLIST 894 | REFERENCES -- 未被实施 895 | RELOAD -- 允许使用FLUSH 896 | REPLICATION CLIENT -- 允许用户询问从属服务器或主服务器的地址 897 | REPLICATION SLAVE -- 用于复制型从属服务器(从主服务器中读取二进制日志事件) 898 | SELECT -- 允许使用SELECT 899 | SHOW DATABASES -- 显示所有数据库 900 | SHOW VIEW -- 允许使用SHOW CREATE VIEW 901 | SHUTDOWN -- 允许使用mysqladmin shutdown 902 | SUPER -- 允许使用CHANGE MASTER, KILL, PURGE MASTER LOGS和SET GLOBAL语句,mysqladmin debug命令;允许您连接(一次),即使已达到max_connections。 903 | UPDATE -- 允许使用UPDATE 904 | USAGE -- “无权限”的同义词 905 | GRANT OPTION -- 允许授予权限 906 | ``` 907 | 908 | ### 表维护 909 | 910 | ``` 911 | /* 表维护 */ 912 | -- 分析和存储表的关键字分布 913 | ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE 表名 ... 914 | -- 检查一个或多个表是否有错误 915 | CHECK TABLE tbl_name [, tbl_name] ... [option] ... 916 | option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED} 917 | -- 整理数据文件的碎片 918 | OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... 919 | ``` 920 | 921 | 922 | 923 | ### 杂项 924 | 925 | ``` 926 | /* 杂项 */ ------------------ 927 | 1. 可用反引号(`)为标识符(库名、表名、字段名、索引、别名)包裹,以避免与关键字重名!中文也可以作为标识符! 928 | 2. 每个库目录存在一个保存当前数据库的选项文件db.opt。 929 | 3. 注释: 930 | 单行注释 # 注释内容 931 | 多行注释 /* 注释内容 */ 932 | 单行注释 -- 注释内容 (标准SQL注释风格,要求双破折号后加一空格符(空格、TAB、换行等)) 933 | 4. 模式通配符: 934 | _ 任意单个字符 935 | % 任意多个字符,甚至包括零字符 936 | 单引号需要进行转义 \' 937 | 5. CMD命令行内的语句结束符可以为 ";", "\G", "\g",仅影响显示结果。其他地方还是用分号结束。delimiter 可修改当前对话的语句结束符。 938 | 6. SQL对大小写不敏感 939 | 7. 清除已有语句:\c 940 | ``` 941 | 942 | > 原文地址:https://shockerli.net/post/1000-line-mysql-note/ 作者:格物 943 | -------------------------------------------------------------------------------- /docs/一文学会Python十大排序算法.md: -------------------------------------------------------------------------------- 1 | ## 一文学会Python十大排序算法 2 | 3 | > 本文已在个人Github开源项目:[PythonGuide](https://github.com/hellgoddess/PythonGuide)中收录,其中包含Python各个方向的自学编程路线、面试题集合/面经及系列技术文章等,并且不断收集了上百本著名的计算机书籍pdf版本,会不断的更新与完善开源项目... 4 | > 5 | > 本文作者:海森堡 6 | 7 | ![image-20210423193028151](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210423193028151.png) 8 | 9 | > 关键术语说明 10 | 11 | - **稳定** :如果a原本在b前面,而a=b,排序之后a仍然在b的前面; 12 | - **不稳定** :如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面; 13 | - **内排序** :所有排序操作都在内存中完成; 14 | - **外排序** :由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行; 15 | - **时间复杂度** : 一个算法执行所耗费的时间。 16 | - **空间复杂度** :运行完一个程序所需内存的大小。 17 | 18 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210423192325972.png) 19 | 20 | ### 1.冒泡排序(Bubble Sort) 21 | 22 | 冒泡排序时针对**相邻元素之间**的比较,可以将大的数慢慢“沉底”(数组尾部) 23 | 24 | ![1](https://gitee.com/chushi123/picgo/raw/master/picture/1.gif) 25 | 26 | ```python 27 | #-*-coding:utf-8-*- 28 | def bubble_sort(alist): 29 | """冒泡排序""" 30 | n = len(alist) 31 | for j in range(0, n-1): 32 | count = 0 # 优化排序 33 | for i in range(0, n-1-j): 34 | # 班长从头走到尾 35 | if alist[i] > alist[i+1]: 36 | alist[i], alist[i+1] = alist[i+1], alist[i] 37 | count += 1 38 | if 0 == count: 39 | break 40 | 41 | if __name__ == '__main__': 42 | li = [9, 8, 7, 6, 2, 3, 4, 1] 43 | print(li) 44 | bubble_sort(li) 45 | print(li) 46 | ``` 47 | 48 | ## 2.选择排序(Selection Sort) 49 | 50 | **选择排序(Selection-sort)** 是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 51 | 52 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/849589-20171015224719590-1433219824.gif) 53 | 54 | ```python 55 | #-*-coding:utf-8-*- 56 | def select_sort(alist): 57 | """ι€‰ζ‹©ζŽ’εΊ""" 58 | n = len(alist) 59 | for j in range(n-1): 60 | min_index = j 61 | for i in range(j+1, n): 62 | if alist[min_index] > alist[i]: 63 | min_index = i 64 | alist[j], alist[min_index] = alist[min_index], alist[j] 65 | ``` 66 | 67 | ## 3.插入排序(Insertion Sort) 68 | 69 | **插入排序(Insertion-Sort)** 的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。 70 | 71 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/3.gif) 72 | 73 | ```python 74 | #-*-coding:utf-8-*- 75 | def insertSort(relist): 76 | len_ = len(relist) 77 | for i in range(1, len_): 78 | for j in range(i): 79 | if relist[i] < relist[j]: 80 | relist.insert(j,relist[i]) 81 | relist.pop(i+1) 82 | break 83 | return relist 84 | 85 | a = [9,8,7,6,5,4,3,2,1] 86 | print(insertSort(a)) 87 | ``` 88 | 89 | ## 4.希尔排序(Shell Sort) 90 | 91 | **希尔排序是希尔(Donald Shell)** 于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。 92 | 93 | 希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 94 | 95 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210423221737356.png) 96 | 97 | ```python 98 | def shell_sort(nums): 99 | n = len(nums) 100 | gap = n // 2 101 | while gap: 102 | for i in range(gap, n): 103 | while i - gap >= 0 and nums[i - gap] > nums[i]: 104 | nums[i - gap], nums[i] = nums[i], nums[i - gap] 105 | i -= gap 106 | gap //= 2 107 | return nums 108 | 109 | ``` 110 | 111 | 112 | 113 | ## 5. 归并排序(Merge Sort) 114 | 115 | **归并排序** 是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。 116 | 117 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/5.gif) 118 | 119 | ```python 120 | #-*-coding:utf-8-*- 121 | def merge(left, right): 122 | result = [] 123 | while left and right: 124 | result.append(left.pop(0) if left[0] <= right[0] else right.pop(0)) 125 | while left: 126 | result.append(left.pop(0)) 127 | while right: 128 | result.append(right.pop(0)) 129 | return result 130 | 131 | def mergeSort(relist): 132 | if len(relist) <= 1: 133 | return relist 134 | mid_index = len(relist)//2 135 | left = mergeSort(relist[:mid_index]) 136 | right = mergeSort(relist[mid_index:]) 137 | return merge(left, right) 138 | 139 | print(mergeSort([9,8,7,6,5,4])) 140 | ``` 141 | 142 | ## 6.快速排序 143 | 144 | **快速排序** 的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。 145 | 146 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/6.gif) 147 | 148 | 一 149 | 150 | ```python 151 | #-*-coding:utf-8-*- 152 | def quick_sort(alist, first, last): 153 | """快速排序""" 154 | if first >= last: 155 | return 156 | mid_value = alist[first] 157 | low = first 158 | high = last 159 | while low < high: 160 | while low < high and alist[high] >= mid_value: 161 | high -= 1 162 | alist[low] = alist[high] 163 | 164 | while low < high and alist[low] < mid_value: 165 | low += 1 166 | alist[high] = alist[low] 167 | # 从循环退出时,low == high 168 | alist[low] = mid_value 169 | # 对low左边的列表执行快速排序 170 | quick_sort(alist, first, low-1) 171 | # 对low右边的列表执行快速排序 172 | quick_sort(alist, low+1, last) 173 | 174 | 175 | if __name__ == '__main__': 176 | li = [0, 6, 7, 9, 8, 6, 2, 3, 1, 4, 5] 177 | print(li) 178 | quick_sort(li, 0, len(li)-1) 179 | print(li) 180 | ``` 181 | 182 | 二: 183 | 184 | ```python 185 | #-*-coding:utf-8-*- 186 | def quick_sort(nums): 187 | if not nums: 188 | return [] 189 | div = nums[0] 190 | left = quick_sort([l for l in nums[1:] if l <= div]) 191 | right = quick_sort([r for r in nums[1:] if r > div]) 192 | return left + [div] + right 193 | ``` 194 | 195 | ## 7.堆排序(Heap Sort) 196 | 197 | **堆排序(Heapsort)** 是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 198 | 199 | ![7.2](https://gitee.com/chushi123/picgo/raw/master/picture/7.2.gif) 200 | 201 | ```python 202 | def heap_sort(nums): 203 | # 调整堆 204 | # 迭代写法 205 | def adjust_heap(nums, startpos, endpos): 206 | newitem = nums[startpos] 207 | pos = startpos 208 | childpos = pos * 2 + 1 209 | while childpos < endpos: 210 | rightpos = childpos + 1 211 | if rightpos < endpos and nums[rightpos] >= nums[childpos]: 212 | childpos = rightpos 213 | if newitem < nums[childpos]: 214 | nums[pos] = nums[childpos] 215 | pos = childpos 216 | childpos = pos * 2 + 1 217 | else: 218 | break 219 | nums[pos] = newitem 220 | 221 | # 递归写法 222 | def adjust_heap(nums, startpos, endpos): 223 | pos = startpos 224 | chilidpos = pos * 2 + 1 225 | if chilidpos < endpos: 226 | rightpos = chilidpos + 1 227 | if rightpos < endpos and nums[rightpos] > nums[chilidpos]: 228 | chilidpos = rightpos 229 | if nums[chilidpos] > nums[pos]: 230 | nums[pos], nums[chilidpos] = nums[chilidpos], nums[pos] 231 | adjust_heap(nums, pos, endpos) 232 | 233 | n = len(nums) 234 | # 建堆 235 | for i in reversed(range(n // 2)): 236 | adjust_heap(nums, i, n) 237 | # 调整堆 238 | for i in range(n - 1, -1, -1): 239 | nums[0], nums[i] = nums[i], nums[0] 240 | adjust_heap(nums, 0, i) 241 | return nums 242 | 243 | ``` 244 | 245 | ## 8.计数排序(Counting Sort) 246 | 247 | **计数排序** 的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。 248 | 249 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/8.gif) 250 | 251 | ```python 252 | def counting_sort(nums): 253 | if not nums: return [] 254 | n = len(nums) 255 | _min = min(nums) 256 | _max = max(nums) 257 | tmp_arr = [0] * (_max - _min + 1) 258 | for num in nums: 259 | tmp_arr[num - _min] += 1 260 | j = 0 261 | for i in range(n): 262 | while tmp_arr[j] == 0: 263 | j += 1 264 | nums[i] = j + _min 265 | tmp_arr[j] -= 1 266 | return nums 267 | 268 | ``` 269 | 270 | ## 9.桶排序(Bucket Sort) 271 | 272 | 桶排序是计数排序的升级版,原理是:输入数据服从均匀分布的,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的算法或是以递归方式继续使用桶排序,此文编码采用递归方式) 273 | 274 | 算法描述: 275 | 276 | 人为设置一个桶的BucketSize,作为每个桶放置多少个不同数值(意思就是BucketSize = 5,可以放5个不同数字比如[1, 2, 3,4,5]也可以放 100000个3,只是表示该桶能存几个不同的数值) 277 | 遍历待排序数据,并且把数据一个一个放到对应的桶里去 278 | 对每个不是桶进行排序,可以使用其他排序方法,也递归排序 279 | 不是空的桶里数据拼接起来 280 | 281 | ```python 282 | def bucket_sort(nums, bucketSize): 283 | if len(nums) < 2: 284 | return nums 285 | _min = min(nums) 286 | _max = max(nums) 287 | # 需要桶个数 288 | bucketNum = (_max - _min) // bucketSize + 1 289 | buckets = [[] for _ in range(bucketNum)] 290 | for num in nums: 291 | # 放入相应的桶中 292 | buckets[(num - _min) // bucketSize].append(num) 293 | res = [] 294 | 295 | for bucket in buckets: 296 | if not bucket: continue 297 | if bucketSize == 1: 298 | res.extend(bucket) 299 | else: 300 | # 当都装在一个桶里,说明桶容量大了 301 | if bucketNum == 1: 302 | bucketSize -= 1 303 | res.extend(bucket_sort(bucket, bucketSize)) 304 | return res 305 | 306 | ``` 307 | 308 | ## 10.基数排序(Radix Sort) 309 | 310 | **基数排序**也是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),为数组长度,k为数组中的数的最大的位数; 311 | 312 | **基数排序**是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。 313 | 314 | ![](https://gitee.com/chushi123/picgo/raw/master/picture/10.gif) 315 | 316 | ```python 317 | def Radix_sort(nums): 318 | if not nums: return [] 319 | _max = max(nums) 320 | # 最大位数 321 | maxDigit = len(str(_max)) 322 | bucketList = [[] for _ in range(10)] 323 | # 从低位开始排序 324 | div, mod = 1, 10 325 | for i in range(maxDigit): 326 | for num in nums: 327 | bucketList[num % mod // div].append(num) 328 | div *= 10 329 | mod *= 10 330 | idx = 0 331 | for j in range(10): 332 | for item in bucketList[j]: 333 | nums[idx] = item 334 | idx += 1 335 | bucketList[j] = [] 336 | return nums 337 | 338 | ``` 339 | 340 | 参考文章:https://blog.csdn.net/MobiusStrip/article/details/83785159?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task 341 | 342 | -------------------------------------------------------------------------------- /docs/获取计算机网络pdf.md: -------------------------------------------------------------------------------- 1 | 关注公众号:CookDev 回复:计算机网络 2 | 3 | ![image-20210508151551993](https://gitee.com/chushi123/picgo/raw/master/picture/image-20210508151551993.png) 4 | 5 | ![公众号二维码](https://gitee.com/chushi123/picgo/raw/master/picture/公众号二维码.jpg) 6 | 7 | 8 | -------------------------------------------------------------------------------- /python100题/获取python100题pdf.md: -------------------------------------------------------------------------------- 1 | 获取Python100题pdf资源:链接:https://pan.baidu.com/s/1VDNMxqxzPpMx4Y6BaaQ2vw 提取码:765y 2 | 3 | --------------------------------------------------------------------------------