首页 > 编程语言 >2024年韩顺平老师Python教程保姆级笔记

2024年韩顺平老师Python教程保姆级笔记

时间:2024-10-24 12:46:57浏览次数:3  
标签:__ name Python self list 2024 print def 顺平

代码获取:https://github.com/qingxuly/hsp_python_course

Python 语言描述

Python 转义字符

  1. Python 常用的转义字符

转义字符说明
\t制表符,实现对齐的功能
\n换行符,
\\一个\
\"一个 "
\'一个'
\r一个回车
  1. 代码演示

# \t制表符
print("jack\t20")
​
# \n换行
print("Hello,jack\nhello,tom")
​
# \\输出\
print("D:\\Pycharm\\chapter02")
​
# \"输出"     \'输出'
print("郭靖说:\"hello\"")
print("郭靖说:\'hello\'")  # 单引号也可以不适用转义 \
​
# \r回车
print("嘻嘻哈哈, \r咚咚锵锵")
​
"""
输出如下文案
姓名  年龄  籍贯  住址
tom 12  河北  北京
"""
print("姓名\t年龄\t籍贯\t住址\ntom\t12\t河北\t北京")

注释(comment)

  1. 基本介绍

  2. 用于注解说明程序的文字就是注释。

    • 被注释的语句,就不会再执行。

    • 注释提高了代码的可读性。

    • 注释是一个程序员必须要具有的良好编程习惯,将自己的思想通过注释先整理出来,再用代码去体现。

  3. 单行注释

    # 这是一个注释
  4. 多行注释

    • 格式:三个单引号 ''' 注释文字 ''' 或者 三个双引号 "" " 注释文字 " ""

    """
    print("hello world 4")
    print("hello world 5")
    print("hello world 6")
    """
    
    '''
    print("hello world 1")
    print("hello world 2")
    print("hello world 3")
    '''
  5. 文件编码声明注释

    • 格式:# coding: 编码,在文件开头加上编码声明,用以指定文件的编码格式。

    # coding:utf-8
  6. 使用细节

    • 对于单行和多行的注释,被注释的文字,不会被解释器执行。

    • 多行注释里面不要有多行注释嵌套,容易混淆。

    # 错误演示
    """
    print("hello")
    """print("world")"""
    """

Python 代码规范

  1. 正确的注释和注释风格

    • 使用多行注释来注释多行说明。

    • 如果注释函数或者其中的某个步骤,使用单行注释。

  2. 正确的缩进和空白

    • 使用一次 Tab 操作,实现缩进,默认整体向右移动,使用 Shift+Tab 整体向左移动。

    • = 两边习惯各加一个空格比较规范。

    • 变量之间使用逗号间隔比较清晰。

    # 代码参考演示
    def test_circular_linked_list() -> None:
        """
        Test cases for the CircularLinkedList class.
        >>> test_circular_linked_list()
        """
        circular_linked_list = CircularLinkedList()
        assert len(circular_linked_list) == 0
        assert circular_linked_list.is_empty() is True
        assert str(circular_linked_list) == ""
    
        try:
            circular_linked_list.delete_front()
            raise AssertionError  # This should not happen
        except IndexError:
            assert True  # This should happen

Python 文档

  1. 使用说明:Python 语言提供了函数、模块、数值、字符串,也给出了相应的文档,用于告诉开发者如何使用。

  2. 演示:查看如何使用 Python 的内置函数 abs

 

fa020fe9acd40aa78a11d5d8d9e4ae87.png

# 演示内置函数 abs 的使用

print("-100的绝对值", abs(-100))
print("-100.45的绝对值", abs(-100.45))
print("200的绝对值", abs(-200))

本章作业

  1. 环境变量 path 配置的作用是什么?

配置环境变量后,我们可以在任意目录去执行path指定的目录下的程序或指令,比如python.exe。

变量

为什么需要变量

  1. 一个程序就是一个世界

  2. 变量是程序的基本组成单位

    • 不论是使用哪种高级程序语言编写程序,变量都是其程序的基本组成单位。

    • 变量有三个基本要素(类型 + 名称 + 值)。

# 变量的实例
# 定义了一个变量,变量的名称是a,变量的值是1,1是int类型(整数类型)
a = 1
# 变量a的值修改为2,变量a的值是2,2是int类型
a = 2
# 输出变量的值
print("a的值是", a, "类型是", type(a))
  1. 简单的原理示意图

    • 当程序/代码执行后,变量的值是存在计算机内存的。

    • 计算机内存介绍

      • 内存(Memory)是计算机的重要部件,它用于暂时存放 CPU 中的运算数据,以及与硬盘等外部存储交换的数据。它是外存于 CPU 进行沟通的桥梁,计算机中所有程序的运行都在内存中进行。

 

f85a8c102a018481255fe6b9876ac136.png

变量的介绍

  1. 变量的概念

    • 变量相当于内存中一个数据存储空间的表示。

    • 你可以把变量看做是一个房间的门牌号,通过门牌号我看可以找到房间,而通过变量名可以访问到变量(值)。

  2. 变量的定义

    • 定义变量:a = 60

    • 使用:print(a)

  3. 注意:需要先定义变量,才能使用,否则会提示 not defined

  4. 代码演示

# 变量使用的错误形式
# print(c)
# c = 10

变量快速入门

  • 演示记录人的信息并输出的程序。

# 演示记录人的信息并输出的程序
name = "tom"  # 字符串
age = 20  # 整形
score = 90.4  # 浮点型(小数)
gender = "男"  # 字符串

# 输出信息
print("个人信息如下:")
print(name)
print(age)
print(score)
print(gender)

# 输出信息
print("个人信息如下:", name, age, score, gender)

格式化输出

  1. %操作符

  • 在 Python 中,使用 % 操作符进行格式化输出时,需要在字符串中使用占位符来表示需要替换的值。占位符由一个百分号 % 和一个格式说明符组成,格式说明符用于指定变量的类型和格式。

  • 除了基本的占位符外,还可以使用一些修饰符来控制输出格式。例如,我们可以使用 : 来指定宽度和对齐方式,使用 . 来指定小数点后的位数等。

%操作符说明备注
%s输出字符串 
%d输出整数%03d 表示宽度为 3,右对齐,不足的部分用 0 填充
%f输出浮点数%.2f 表示小数点后保留两位有效数字
%%输出% 
# 定义变量
age = 80
score = 77.5
gender = '男'
name = "贾宝玉"

# %操作符输出
print("个人信息:%s %d %.1f %s" % (name, age, score, gender))
  1. format()函数

  • 除了使用 % 操作符进行格式化输出外,Python 还提供了 format() 函数来进行字符串格式化。format() 函数通过花括号 {} 作为占位符,并通过位置、关键字或属性来指定替换的值。

# format()函数
print("个人信息:{} {} {} {}".format(name, age, score, gender))
  1. f-strings [推荐]

  • f-strings 是 Python 3.6 及更高版本中引入的一种新的字符串格式化方法。它们使用花括号 {} 包围变量名或表达式,并在字符串前加上字母 "f" 或 "F"。在花括号内,可以直接插入变量名或表达式,而不需要使用占位符和格式说明符。这使得字符串格式化更加简洁和易读。

# f-strings
print(f"个人信息:{name} {age} {score} {gender}")

程序中 +号的使用

  1. 当左右两边都是数值型时,则做加法运算。

  2. 当左右两边都是字符串,则做拼接运算。

# +号的使用案例

name = "king"
score = 50.8

print(score + 90)  # 140.8
print(name + "hi")  # kinghi
print("100" + "98")  # 10098
print(34.5 + 100)  # 134.5

# print("100" + 100)  # TypeError: can only concatenate str (not "int") to str

数据类型

基本数据类型

  1. 基本数据类型

类型描述
整型 int整数:如 1, -1, 200
浮点型 float小数:如 1.1, -4.5, 900.9
布尔值 bool布尔值就是我们常说的逻辑,可以理解为对(True)或错(False)
字符串 (string)字符串就是字符组成的一串内容,python 中用成对的单引号或双引号括起来,如“hello world”
  1. 基本介绍

    • Python 中的变量在使用前都必须赋值,变量赋值以后该变量才会被创建。

    • 我们所说的“类型”是变量所指的内存数据的类型。

  2. 案例说明:a = 100

    • a 是变量,它是没有类型的。

    • a 变量指向的/代表的数据 100 是有类型的。

    • 100 有些地方也称为 字面量

  3. type() 函数查看数据类型。

  • type() 基本用法

    • 语法:type(object)。

    • object 就是你要查看类型的数据,可以是一个具体的数据(即:字面量),也可以是变量(也就是查看该变量指向的数据的类型)。

# 演示type() 使用

age = 80
score = 77.5
gender = '男'
name = "贾宝玉"
is_pass = True

# 查看变量的类型(本质是查看变量指向的数据的类型)
print(type(age))
print(type(score))
print(type(gender))
print(type(name))
print(type(is_pass))

# type() 可以直接查看具体的值(字面量) 的类型
print(f"hello 的类型是{type('hello')}")
print(f"1.1 的类型是{type(1.1)}")

整数类型

基本介绍

  • Python 整数就是用于存放整数值的,比如:12,30,3456,-1。

整数使用的使用细节

  1. Python 中的整型,可以表示很大的数(官方:the limit (4300 digits) for integer)。

# 讲解int类型的细节
import sys

n3 = 9 ** 8888
# print(n3)  # ValueError: Exceeds the limit (4300 digits) for integer string conversion;

# use sys.set_int_max_str_digits() to increase the limit
sys.set_int_max_str_digits(0)  # maxdigits must be 0 or larger than 640
print(n3)
  1. Python 的整数有十进制、十六进制、八进制、二进制

    • 十进制就是我们最常见的写法,比如:1,66,123。

    • 十六进制写法:加前缀 0x,由 0-9 和 A-F 的数字和字母结合。

    • 八进制写法:加前缀 0o,由 0-7 数字组合。

    • 二进制写法:加前缀 0b,只有 0 和 1 数字结合。

    • 运行时,会自动转化为十进制输出。

print(10)  # 十进制
print(0x10)  # 十六进制
print(0o10)  # 八进制
print(0b10)  # 二进制
  1. Python 中的整型占多少个字节?

字节(byte):计算机中基本存储单元

位(bit):计算机中的最小存储单位

1 byte = 8 bit

  • 字节数是随着数字增大而增大(即:Python 整型是变长的)。

  • 每次的增量是 4 个字节。

n1 = 0
n2 = 1
n3 = 2
n4 = 2 ** 15
n5 = 2 ** 30
n6 = 2 ** 128

# 在Python中,可以通过sys.getsizeof 返回对象(数据)的大小(按照字节单位返回)
print(n1, sys.getsizeof(n1), "类型", type(n1))
print(n2, sys.getsizeof(n2), "类型", type(n2))
print(n3, sys.getsizeof(n3), "类型", type(n3))
print(n4, sys.getsizeof(n4), "类型", type(n4))
print(n5, sys.getsizeof(n5), "类型", type(n5))
print(n6, sys.getsizeof(n6), "类型", type(n6))

print(sys.getsizeof(10))  # 28个字节

浮点类型

基本介绍

  • Python 的浮点类型可以表示一个小数,比如:123.4,7.8,-0.12。

浮点型使用细节

  1. 浮点数表示形式如下

    • 十进制数形式,如:5.12,.512(必须有小数点)。

    • 科学计数法形式,如:5.12e,5.12e-2。

  2. 浮点数有大小限制 边界值为:

    • max = 1.7976931348623157e+308。

    • min = 2.2250738585072014e-308。

  3. 浮点类型计算后,存在精度的缺失,可以使用 Decimal 类进行精确计算。

import sys

n3 = 5.12
n4 = .512
print("n4 = ", n4)

n5 = 5.12e2  # 5.12乘以10的2次方
print("n5 = ", n5)

n6 = 5.12e-2  # 5.12除以10的2次方
print("n6 = ", n6)

# float_info 是一个具名元组,存有浮点型的相关信息
print(sys.float_info.max)
print(sys.float_info.min)

# 浮点类型计算后,存在精度的缺失,可以使用 Decimal 类进行精确计算
# 1. 为了避免浮点数的精度问题,可以使用Decimal类进行精确计算
# 2. 如果使用Decimal类,需要导入Decimal类
# b = 8.1 / 3  # b = 2.6999999999999997

from decimal import Decimal
b = Decimal("8.1") / Decimal("3")
print("b = ", b)  # b =  2.7

布尔类型 bool

基本介绍

  1. 布尔类型也叫 bool 类型,取值 True 和 False。

  2. True 和 False 都是关键字,表示布尔值。

  3. bool 类型适用于逻辑运算,一般用于程序流程控制:条件控制语句、循环控制语句。

    • 比如判断某个条件是否成立,或者在某个条件满足时执行某些代码。

布尔类型使用细节

  1. 布尔类型只有 2 个值:True 和 False。

  2. 布尔类型可以和其他数据类型进行比较,比如数字、字符串等。在比较时,Python 会将 True 视为 1,False 视为 0。

  3. 在 Python 中,非 0 被视为真值,0 被视为假值。

# bool类型的基本使用‘
num1 = 100
num2 = 200
if num1 > num2:
    print("num1 > num2")
if num1 < num2:
    print("num1 < num2")

# 表示把 num1 > num2 的结果赋给result变量
result = num1 > num2
print("result =", result)

# 查看result的类型
print("result的类型:", type(result))
print(type(1 > -1))

# 布尔类型可以和其他数据类型进行比较,比如数字、字符串等。在比较时,Python会将True视为1,False视为0。
b1 = False
b2 = True

print(b1 + 10)
print(b2 + 10)

# b1 = 0:表示赋值,把0赋给b1
# b1 == 0:表示判断b1是否和0相等
if b1 == 0:
    print("ok")
if b2 == 1:
    print("Hi")

# 在Python中,非0被视为真值,0被视为假值
if 0:
    print("哈哈")
if -1:
    print("嘻嘻")
if 1.1:
    print("呵呵")
if "哈哈":
    print("啧啧")
if None:
    print("滴滴")

字符串 str

基本介绍

  1. 字符串是 Python 中很常用的数据类型,通俗来说,字符串就是字符组成的一串内容。

  2. 使用引号 '" 包括起来,创建字符串。

  3. str 就是 string 的缩写,在使用 type() 查看数据类型时,字符串类型显示的是 str。

  4. 通过加号可以连接字符串。

字符串使用注意事项

  1. Python 中不支持单字符类型,单字符在 Python 中也是作为一个字符串使用。

  2. 用三个单引号 '''内容''' 或三个双引号 """内容""" 可以使字符串内容保持原样输出,在输出格式复杂的内容是比较有用的。

  3. 在字符串前面加 r 可以使整个字符串不会被转义。

# 字符串使用注意事项
# 使用引号`'`或`"`包括起来,创建字符串
str1 = "tom说:\"hello\""
str2 = 'jack说:"hello"'
print(str1)
print(str2)

print(f"st2的类型:{type(str2)}")

# 通过加号可以连接字符串
print("hi" + " tom")

# Python中不支持单字符类型,单字符在Python中也是作为一个字符串使用
str3 = "A"
print("str3类型", type(str3))

# 用三个单引号'''内容'''或三个双引号"""内容"""可以使字符串内容保持原样输出,在输出格式复杂的内容是比较有用的。
content = """ Hi,我是你的百度翻译AI助手,我可以提供一站式翻译服务,'''f''
目前仅支持中文和英语,其它语种正在努力学习中;
所有内容均由AI提供,仅供参考,如有错误请反馈,我们将持续改进!"""
print(content)

# 在字符串前面加`r`可以使整个字符串不会被转义
str4 = "jack\ntom\tking"
print(str4)
str5 = r"jack\ntom\tking"
print(str5)

扩展知识:字符串驻留机制

说明:Python 仅保存一份相同且不可变字符串,不同的值被存放在字符串的驻留池中,Python 的驻留机制对相同的字符串只保存一份拷贝,后续创建相同字符串时,不会开辟新空间,而是把该字符串的地址赋给新创建的变量。

# 字符串驻留机制
str1 = "hello"
str2 = "hello"
str3 = "hello"

# id()函数是可以返回对象/数据的内存地址
print("str1的地址:", id(str1))
print("str2的地址:", id(str2))
print("str3的地址:", id(str3))

 

de15228880a1ca6d63be6cd2da7eb955.png

  • 驻留机制几种情况讨论(注意:需要在交互模式下 win+r+cmd 进入交互模式,输入 python

    • 字符串是由 26 个英文字母大小写,0-9,_组成。

    • 字符串长度为 0 或 1 时。

    • 字符串在编译时进行驻留,而非运行时。

    • [-5,256] 的整数数字。

 

7f143ca3d56f381c60889e746e9a1283.png

  • sys 中的 intern 方法可以强制 2 个字符串指向同一个对象。

 

f8534bbfdd3e657310e30bcebb742392.png

  • Pycharm 对字符串进行了优化处理。

# pycharm进行了优化处理
str6 = "abc123#"
str7 = "abc123#"
print(id(str6), id(str7))

num1 = -100
num2 = -100
print(id(num1), id(num2))
  • 字符串驻留机制的好处。

当需要值相同的字符串时,可以直接从字符串池里拿来使用,避免频繁的创建和销毁,提升效率和节约内存。

数据类型转换

隐式类型转换

  1. Python 变量的类型不是固定的,会根据变量当前值在运行时决定的,可以通过内置函数 type(变量) 来查看其类型,这种方式就是隐式转换,有的书也称为自动转换 。

  2. 在运算的时候,数据类型会向高精度自动转换。

# python 根据该变量使用的上下文在运行时决定的
var1 = 10  # int类型
print(type(var1))
var1 = 1.1  # float类型
print(type(var1))
var1 = 'hello'  # string类型
print(type(var1))

# 在运算的时候,数据类型会向高精度自动转换,float的精度高于int
var2 = 10
var3 = 1.2
var4 = var2 + var3
print("var4=", var4, "var4的类型:", type(var4))
var2 = var2 + 0.1
print("var2=", var2, "var2的类型:", type(var2))

显式类型转换

  1. 如果需要对变量数据类型进行转换,只需要将数据类型作为函数名即可,这种方式就是显示转换/强制转换。

  2. 以下几个内置的函数可以完成数据类型之间的转换。函数会 返回一个新的对象/值,就是强制转换的后果

函数描述
int(x [, base])将 x 转换为一个整数
float(x)将 x 转换到一个浮点数
complex(real [, imag])创建一个复数
str(x)将对象 x 转换为字符串
repr(x)将对象 x 转换为表达式字符串
eval(str)用来计算在字符串中的有效 Python 表达式,并返回一个对象
tuple(s)将序列 s 转换为一个元组
list(s)将序列 s 转换为一个列表
set(s)转换为可变集合
dict(d)创建一个字典。d 必须是一个(key, value)元组序列
frozenset(s)转换为不可变集合
chr(x)将一个整数转换为一个字符
# 显示转换案例
i = 10
j = float(i)
print("j的类型:", type(j), "j =", j)
k = str(i)
print("k的类型:", type(k), "k =", k)

显示类型转换注意事项

  1. 不管什么值的 intfloat 都可以转为 strstr(x) 将对象 x 转为字符串。

  2. int 转为 float 时,会增加小数部分,比如 123-> 123.0,float 转为 int 时,会去掉小数部分,比如 123,65-> 123。

  3. strint、float ,使用 int(x)、float(x) 将对象 x 转换为 int / float

  4. 在将 str类型 转为 基本数据类型 时,要确保 str值 能够转成有效的数据,比如我们可以把“123”转为一个整数,但是不能把“hello”转成一个整数,如果格式不正确,程序会报 ValueError,程序就会终止。

  5. 对一个变量进行强制转换,会返回一个数据/值,注意:强制转换后,并不会影响原变量的数据类型(即:不会影响原变量指向的数据/值的数据类型)。

# 显示类型转换注意事项
n1 = 100
n2 = 123.65
print(str(n1))
print(str(n2))

print(float(n1))
print(int(n2))

n3 = "12.56"
print(float(n3))
# print(int(n3))  # ValueError: invalid literal for int() with base 10: '12.56'

n4 = "hello"  # ValueError: could not convert string to float: 'hello'
# print(float(n4))
# print(int(n4))

i = 10
j = float(i)
print("i的值:", i, "i的类型:", type(i))
print("j的值:", j, "j的类型:", type(j))

k = str(i)
print("i的值:", i, "i的类型:", type(i))
print("k的值:", k, "k的类型:", type(k))
# 练习
i = 10
j = float(i)
print(type(i))  # int
print(type(j))  # float

i = j + 1
print(type(i))  # float
print(type(j))  # float

print(i)  # 11.0
print(int(i))  # 11
print(type(i))  # float

运算符

运算符介绍

  1. 运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。

  2. 算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符 [需要二进制基础]。

算术运算符

基本介绍

  • 算符运算符是对数值类型的变量进行运算的,在程序中使用的非常多。

算术运算符一览

运算符运算范例结果
+5+510
-6-42
*3* 412
/5/51
%取模(取余)7%52
//取整除-返回商的整数部分(向下取整)9//2,-9//24,-5
**返回 x 的 y 次幂2** 416

使用细节

  1. 对于除号 /,返回结果是小数。

  2. 对于取整除 //,返回商的整数部分(并且是向下取整)。

  3. 当对一个数取模时,对应的运算公式:a % b = a - a // b * b

# 演示算术运算符的使用
# /, //, %, **
# 对于除号 /,返回结果是小数
print(10 / 3)

# 对于取整数 //,返回商的整数部分(并且是向下取整)
print(10 // 3)
print(-10 // 3)

# 当对一个数取模时,对应的运算公式: a % b = a - a // b * b
print(10 % 3)
# 分析:-10 - (-10) // 3 * 3 = -10 - (-4) * 3 = -10 - (-12) = 2
print(-10 % 3)
# 分析: 10 - 10 // (-3) * (-3) = 10 - (-4) * (-3) = 10 - 12 = -2
print(10 % -3)
# 分析: -10 - (-10) // (-3) * (-3) = -10 - 3 * (-3)= -10 + 9 = -1
print(-10 % -3)

print(2 ** 5)
# 练习
# 假如还有97天放假,问:合xx个星期 零xx天
days = 97
week = days // 7
left_day = days % 7
print(f"假如还有{days}天放假,则:合{week}个星期 零{left_day}天")

# 定义一个变量保存华氏温度,华氏温度转换摄氏温度的公式为:5/9*(华氏温度-100),请求出华氏温度对应的摄氏温度
hua_shi = 234.5
she_shi = 5 / 9 * (hua_shi - 100)
print(f"华氏温度 {hua_shi} 对应的摄氏温度 {she_shi}")
print("华氏温度 %.2f 对应的摄氏温度 %.2f" % (hua_shi, she_shi))

比较运算符

基本介绍

  1. 比较运算符的结果要么是 True,要么是 False。

  2. 比较表达式 经常用在 if 结构的条件,为 True 就执行相应的语句,为 False 就不执行。

n1 = 1
if n1 > -10:
    print("hi...")

比较运算符一览

运算符运算范例结果
==等于4 == 3False
!=不等于4 != 3True
<小于4 < 3False
>大于4 > 3True
<=小于等于4 <= 3False
>=大于等于4 >= 3True
is判断两个变量引用对象是否为同一个  
is not判断两个对象引用对象是否不同  

使用细节

  1. 比较运算符组成的表达式,我们称为比较表达式,比如:a > b。

  2. 比较运算符 == 不能误写成 =

# 比较运算符的使用
a = 9
b = 8
print(a > b)
print(a >= b)
print(a <= b)
print(a < b)
print(a == b)
print(a != b)
flag = a > b
print("flag =", flag)
print(a is b)
print(a is not b)

str1 = "abc#"
str2 = "abc#"
print(str1 == str2)
print(str1 is str2)  # 交互模式下为False

逻辑/布尔运算符

基本介绍

  • 逻辑运算也被称为布尔运算。

逻辑/布尔运算符一览

  • 以下假设变量 a = 10,b = 20。

运算符逻辑表达式描述实例
andx and y布尔“与”:如果 x 为 False,返回 x 的值,否则返回 y 的计算值。(a and b) 返回 20
orx or y布尔“或”:如果 x 为 True,返回 x 的值,否则返回 y 的计算值。(a or b) 返回 10
notnot a布尔“非“:如果 x 为 True,返回 False。如果 x 为 False,它返回 True。not (a and b) 返回 False
a = 10
b = 20

print(a and b)
print(a or b)
print(not (a and b))

使用细节

短路运算符是一种特殊的逻辑运算符,它们在执行过程中会根据操作数的值提前终止运算。

  1. and 是“短路运算符”,只有当第一个为 True 时,才去验证第二个。

  2. or 是“短路运算符”,只有当第一个为 False 时才去验证第二个(换言之,如果第一个为 True,就直接返回第一个的值)。

# and 使用细节
score = 70
if (score >= 60 and score <= 80):
    print("成绩还不错~")

a = 1
b = 99
print(a and b)
print((a > b) and b)
print((a < b) and b)

# or 使用细节
score = 70
if score <= 60 or score >= 80:
    print("hi~")

a = 1
b = 99
print(a or b)
print((a > b) or b)
print((a < b) or b)

# not 使用细节
a = 3
b = not (a > 3)
print(b)
print(not False)
print(not True)
print(not 0)
print(not "jack")
print(not 1.88)
print(not a)

赋值运算符

基本介绍

  • 赋值运算符就是将某个运算后的值,赋给指定的变量。

赋值运算符一览

运算符描述实例
=简单的赋值运算符c = a + b 将 a + b 的运算结果赋值为 c
+=复合加法赋值运算符c += a 等效于 c = c + a
-=复合减法赋值运算符c -= a 等效于 c = c - a
*=复合乘法赋值运算符c * = a 等效于 c = c * a
/=复合除法赋值运算符c /= a 等效于 c = c / a
%=复合取模赋值运算符c %= a 等效于 c = c % a
**=复合幂赋值运算符c **= a 等效于 c = c **a
//=复合取整除赋值运算符c //= a 等效于 c = c // a

使用细节

  1. 运算顺序从右往左,num = a + b +c

  2. 赋值运算符的左边是变量,右边可以是变量、表达式、字面量。

    • 如:num1 = 20、num2 = 78 * 34 - 10、num3 = a、num4 = a * b。

# 赋值运算符
num1 = 10
i = 100
i += 100  # => i = i + 100
print("i =", i)
i -= 100  # => i = i - 100
print("i =", i)
i *= 3  # i = i * 3
print("i =", i)

# 有两个变量,a 和 b,要求将其进行交换,最终打印结果
# 方法1
a = 30
b = 40
print(f"没有交换前 a={a} b={b}")
temp = a
a = b
b = temp
print(f"交换后 a={a} b={b}")

# 方法2
a = 30
b = 40
print(f"没有交换前 a={a} b={b}")
a, b = b, a
print(f"交换后 a={a} b={b}")

# 方法3
a = 30
b = 40
print(f"没有交换前 a={a} b={b}")
a = a + b
b = a - b
a = a - b
print(f"交换后 a={a} b={b}")

三元运算符

基本语法

  1. Python 是一种极简主义的编程语言,它没有引入 ?: 这个运算符,而是使用 if else 关键字来实现相同的功能。

  2. 语法: max = a if a > b else b。

    • 如果 a > b 成立,就把 a 作为整个表达式的值,并赋给变量 max。

    • 如果 a > b 不成立,就把 b 作为整个表达式的值,并赋给变量 max。

代码演示

# 三元运算符
# 获取两个数的最大值
a = 10
b = 80
max = a if a > b else b
print(f"max={max}")

# 获取三个数的最大值
a = 10
b = 30
c = 20
max1 = a if a > b else b
max2 = max1 if max1 > c else c
print(f"max2={max2}")

# 可以支持嵌套使用,但是可读性差,不推荐
max = (a if a > b else b) if (a if a > b else b) > c else c
print(f"max={max}")

运算符优先级

基本语法

  • 运算符有不同的优先级,所谓优先级就是表达式的运算顺序。

分类运算符描述
算术运算符(expressions)添加圆括号的表达式
 **乘方
 *,@,/,//,%乘,矩阵乘,除,整除,取余
 + -加法减法
位运算>>,<<右移,左移运算符(移位)
 &按位与
 ^按位异或
 |按位或
比较运算in,not in,is,is not,<,<=,>,>=,!=,==比较运算,包括成员检测和标识号检测
逻辑运算not x布尔逻辑非 NOT
 and布尔逻辑与 AND
 or布尔逻辑或 OR
赋值运算=,%=,/=,//=,-=,+=,*=,**=赋值运算符

标识符的命名规则和规范

标识符概念

  1. Python 对各种变量、函数、和类等命名时使用的字符序列称为标识符。

  2. 凡是自己可以起名字的地方都叫标识符,num1 = 90。

# 代码演示
def sum(seq):
    total = 0
    for x in seq:
        if x is not None:
            total += x
    return total

标识符的命名规则

  1. 由 26 个英文字母大小写,0-9,_组成。

  2. 数字不可以开头。

  3. 不可以使用关键字,但能包含关键字。

  4. Python 区分大小写。

  5. 标识符不能包含空格。

# 1. 由26个英文字母大小写,0-9,_组成。
num9_N = 100
# 2. 数字不可以开头。
# 1num = 100
# 3. 不可以使用关键字,但能包含关键字。
# if = 100
my_if = 100
# 4. Python区分大小写。
n = 100
N = 200
print("n =", n, "N =", N)
# 5. 标识符不能包含空格。
my_name = "hi"
# my name = "hi"
# 练习:判断变量名是否正确
hello = 1
hello12 = 2
# 1hello = 1  # 不能以数字开头
# h-b = 1  # 不能包含-
# x h = 1  # 不能包含空格
# h$4 = 1  # 不能包含$
# class = 1  # 不能以关键字作为变量
int = 1
# or = 1  # 不能以关键字作为变量
# and = 1  # 不能以关键字作为变量
# if = 1  # 不能以关键字作为变量
_if = 600
stu_name = 1

标识符命名规范

  1. 变量名:变量要小写,若有多个单词,使用下划线分开。常亮全部大写。

num = 20
my_friend_age = 21
PI = 3.1415926
  1. 函数名:函数名一律小写,如果有多个单词,用下划线隔开。另外,私有函数以双下划线开头。

def my_func(var1, var2):
    pass
def __private_func(var1, var2):
    pass
  1. 类名:使用大驼峰命名。

驼峰命名法有两种,大驼峰命名和小驼峰命名。

大驼峰命名,多个单子的首字母用大写开头,比如:MyName。

小驼峰命名,第一个单词的首字母用小写,后面的单词首字母都大写,例如:myName。

class SheetParser:
    pass
class Foo:
    pass

关键字

关键字的定义和特点

  • 定义:被 Python 语言赋予了特殊含义,用做专门用途的字符串(单词)。

如何查看关键字

  1. 官方文档—语言参考—2.3 标识符和关键字—2.3.1 关键字。

 

869d902b2ad20f8437810320e484321d.png

  1. Python 的交互模式中输入 help(),再输入 keywords 即可。

 

0809cf4b6a18bc75cef7dc3e5aad4268.png

键盘输入语句

基本介绍

  1. 在编程中,需要接收用户输入的数据,就可以使用键盘语句来获取。

  2. input() 介绍:

 

9f7601376f55b2e27716a9a701af98a8.png

应用实例演示

# 可以从控制台接收用户信息,【姓名,年龄,薪水】
name = input("请输入姓名:")
age = input("请输入年龄:")
score = input("请输入成绩:")

print("\n输入的信息如下:")
print("name:", name)
print("age:", age)
print("score:", score)

# 注意:接收到的数据类型是str。
# print(10 + score)  # TypeError: unsupported operand type(s) for +: 'int' and 'str'
# 如果我们希望对接收到的数据进行算术运算,则需要进行类型转换。
print(10 + float(score))

# 当然,我们也可以在接收数据的时候,直接转成需要的类型
age = int(input("请输入年龄:"))
print("age的类型是:", type(age))

扩展知识:进制

进制

进制介绍

  1. 对于整数,有如下表示方式

    • 2 进制:0,1,满 2 进 1,以 0b 或 0B 开头。

    • 8 进制:0-7,满 8 进 1,以数字 0o 或 0O 开头。

    • 10 进制,0-9,满 10 进 1。

    • 16 进制,0-9 以及 A(10)-F(15),满 16 进 1,以 0x 或 0X 开头。此处的 A-F 不区分大小写。

# 2进制
print(0b111)  # 7
print(0B111)

# 8进制
print(0o111)  # 73
print(0O111)

# 10进制
print(111)  # 111

print(0x111)  # 273
print(0X111)

进制的图示

十进制十六进制八进制二进制
0000
1111
22210
33311
444100
555101
666110
777111
88101000
99111001
10A121010
11B131011
12C141100
13D151101
14E161110
15F171111
16102010000
17112110001

进制的转换

其它进制转十进制

  1. 二进制转成十进制(加权法/按权展开法)

  • 规则:从最低位(右边)开始,将每个位上的数提取出来,乘以 2 的(位数-1)次方,然后求和。

  • 案例:将 0b1011 转成十进制的数。

0b1011 = 
1 * 2的(1-1)次方 + 1 * 2的(2-1)次方 + 0 * 2的(3-1)次方 + 1 * 2的(4-1)次方 = 
1 + 2 + 0 + 8 = 11

print(0b1011)  # 11
  1. 八进制转成十进制

  • 规则:从最低位(右边)开始,将每个位上的数提取出来,乘以 8 的(位数-1)次方,然后求和。

  • 案例:请将 0o234 转成十进制的数。

0o234 = 
4 * 8的(1-1)次方 + 3 * 8的(2-1)次方 + 2 * 8的(3-1)次方 = 
4 + 24 + 128 = 156

print(0o234)  # 156
  1. 十六进制转成十进制

  • 规则:从最低位(右边)开始,将每个位上的数提取出来,乘以 16 的(位数-1)次方,然后求和。

  • 案例:请将 0x23A 转成十进制的数。

0x23A = 
10 * 16的(1-1)次方 + 3 * 16的(2-1)次方 + 2 * 16的(3-1)次方 = 
10 + 48 + 512 = 570

print(0x23A)  # 570
  1. 练习

1、0b110001100 转成 十进制
= 0 + 0 + 1 * 2**2 + 1 * 2**3 + 0 + 0 + 0 + 1 * 2**7 + 1 * 2**8
= 4 + 8 + 128 + 256
= 396
print(0b110001100)  # 396
2、0o2356 转成 十进制
= 6 * 8^0 + 5 * 8**1 + 3 * 8**2 + 2 * 8**3
= 6 + 40 + 192 + 1024
= 1262
print(0o2356)  # 1262
3、0xA45 转成 十进制
= 5 * 16**0 + 4 * 16**1 + 10 * 16**2
= 5 + 64 + 2560
= 2629
print(0xA45)  # 2629

十进制转其它进制

  1. 十进制转成二进制(短除法)

  • 规则:将该数不断除以 2,直到商为 0 为止,然后将每步得到的余数倒过来,就是对应的二进制。

  • 案例:请将 34 转成二进制的数。

34 = 0b100010
34
17	0
8	1
4	0
2	0
1	0
	1
print(bin(34))  # 0b100010
  1. 十进制转成八进制

  • 规则:将该数不断除以 8,直到商为 0 为止,然后将每步得到的余数倒过来,就是对应的八进制。

  • 案例:请将 131 转成八进制的数。

131 = 0o203
16	3
2	0
	2
print(oct(131))  # 0o203
  1. 十进制转成十六进制

  • 规则:将该数不断除以 16,直到商为 0 为止,然后将每步得到的余数倒过来,就是对应的十六进制。

  • 案例:请将 237 转成十六进制的数。

237 = 0xED
14	13
	14
print(hex(237))  # 0xed
  1. 练习

1、123 转成 二进制
123 = 0b1111011
61	1
30	1
15	0
7	1
3	1
1	1
	1
print(bin(123))  # 0b1111011
2、678 转成 八进制
678 = 0o1246
84	6
10	4
1	2
	1
print(oct(678))  # 0o1246
3、8912 转成 十六进制
8912 = 0x22D0
557	0
34	13
2	2
	2
print(hex(8912))  # 0x22d0

二进制转成八进制、十六进制

  1. 二进制转换成八进制

  • 规则:从低位开始,将二进制数每三位一组,转成对应的八进制数。

  • 案例:请将 0b11010101 转成八进制。

0b11010101 = 0o325
101 = 1 * 2**0 + 0 + 1 * 2**2 = 5
010 = 0 + 1 * 2**1 + 0 = 2
11 = 1 * 2**0 + 1 * 2**1 = 3
print(oct(0b11010101))  # 0o325
  1. 二进制转换成十六进制

  • 规则:从低位开始,将二进制数每四位一组,转成对应的十六进制数。

  • 案例:请将 0b11010101 转成十六进制。

0b11010101 = 0xD5
0101 = 1 * 2**0 + 0 + 1 * 2**2 + 0 = 5
1101 = 1 * 2**0 + 0 + 1 * 2**2 + 1 * 2**3 = 13
print(hex(0b11010101))  # 0xd5
  1. 练习

1、0b11100101 转成 八进制
0b11100101 = 0o345
101 = 1 + 0 + 1 * 2**2 = 5
100 = 1 * 2**2 = 4
11 = 1 + 1 * 2**1 = 3
print(oct(0b11100101))  # 0o345
2、0b1110010110 转成 十六进制
0b1110010110 = 0x396
0110 = 0 + 1 * 2**1 + 1 * 2**2 + 0 = 6
1001 = 1 + 0 + 0 + 1 * 2**3 = 9
11 = 1 + 1 * 2**1 = 3
print(hex(0b1110010110))  # 0x396

八进制、十六进制转成二进制

  1. 八进制转成二进制

  • 规则:将八进制数每 1 位,转成对应的 3 位的二进制数。

  • 案例:请将 0o237 转成二进制。

0o237 = 0b10011111
7 = 111
3 = 011
2 = 010
print(bin(0o237))  # 0b10011111
  1. 十六进制转成二进制

  • 规则:将十六进制数每 1 位,转成对应的 4 位的二进制。

  • 案例:请将 0x23B 转成二进制。

0x23B = 0b1000111011
B = 1011
3 = 0011
2 = 0010
print(bin(0x23B))  # 0b1000111011
  1. 练习

1、0o1230 转成 二进制
0o1230 = 0b1010011000
0 = 000
3 = 011
2 = 010
1 = 001
print(bin(0o1230))  # 0b1010011000
2、0xAB29 转成 二进制
0xAB29 = 0b1010101100101001
9 = 1001
2 = 0010
B = 1011
A = 1010
print(bin(0xAB29))  # 0b1010101100101001

二进制在运算中的说明

基本介绍

  1. 二进制是逢 2 进位的进位制,0、1 是基本数字符号。

  2. 现代的电子计算机技术全部采用的是二进制,因为它只使用 0、1 两个数字符号,非常简单方便,易于电子方式实现。

  3. 计算机内部处理的信息,都是采用二进制数来表示的。二进制(Binary)数用 0 和 1 两个数字及其组合来表示任何数。进位规则是“逢 2 进 1”,数字 1 在不同的位上代表不同的值。

原码、反码、补码

基本介绍

  1. 二进制的最高位是符号位:0 表示整数,1 表示负数。

print(sys.getsizeof(3))  # 3用28个字节表示
# 我们假定用一个字节来表示
3  => 	0000 0011
-3 => 	1000 0011
  1. 正数的原码、反码、补码都一样(三码合一)。

3 => 	原码:0000 0011
		反码:0000 0011
    	补码:0000 0011
  1. 负数的反码 = 它的原码符号位不变,其它位取反(0-> 1,1-> 0)。

    负数的补码 = 它的反码 + 1,负数的反码 = 负数的补码 - 1。

-3 =>	原码:1000 0011
		反码:1111 1100
  		补码:1111 1101  
  1. 0 的反码,补码都是 0

  2. 在计算机运算的时候,都是以补码的方式来运算的;当我们看运算结果的时候,要看它的原码。

1 + 3 => 
1 =>	补码:0000 0001
3 =>	补码:0000 0011
1 + 3 =>补码:0000 0100 => 原码 0000 0100 => 4

1 - 3 => 1 + (-3)
1 =>	补码:0000 0001
-3 =>	原码:1000 0011 => 反码 1111 1100 => 补码 1111 1101
	  1的补码:0000 0001
     -3的补码:1111 1101
  1 - 3的补码:1111 1110 => 反码 1111 1101 => 原码 1000 0010 => -2

位运算符

&、|、^、~

  1. ~ 按位与反

  • 规则:对数据的每个二进制位取反,即把 1 变成 0,把 0 变成 1。

~-2 = 1
-2的补码:?	=> -2的原码 1000 0010 => 反码 1111 1101 => 补码 1111 1110
~-2 = 0000 0001(补码) => 原码 0000 0001 => 1
print(~-2)  # 1

~2 = -3
2的补码:	0000 0010
~2 => 1111 1101(补码) => 反码 1111 1100 => 原码 1000 0011 => -3
print(~2)  # -3
  1. & 按位与

  • 规则:参与运算的两个值,如果两个相应位都为 1,则该为的结果为 1,否则为 0。

2 & 3 = 2
2的补码:0000 0010
3的补码:0000 0011
2 & 3 => 0000 0010(补码) => 原码 0000 0010 => 2
print(2 & 3)  # 2
  1. ^ 按位异或

  • 规则:当两个对应的二进制位相异时,结果为 1。

2 ^ -3 = -1
2的补码:	0000 0010
-3的补码:? => 原码 1000 0011 => 反码 1111 1100 => 补码 1111 1101
2 ^ -3 => 1111 1111(补码) => 反码 1111 1110 => 原码 1000 0001 => -1
print(2 ^ -3)  # -1
  1. | 按位或

  • 规则:只要对应的两个二进制位有一个为 1 时,结果位就位 1。

2 | 3 = 3
2的补码:	0000 0010
3的补码:	0000 0011
2 | 3 =>   0000 0011(补码) => 原码 0000 0011 => 3
print(2 | 3)  # 3

>>、<<

  1. << 左移

  • 规则:运算数的各二进制位全部左移若干位,由“<<”右边的数指定移动的位数,符号位不变,高位丢弃,低位补 0。

  • 左移一位相当于 * 2 一次。

5 << 1 = 10
5的补码:	0000 0101
5 << 1 =>  0000 1010(补码) => 原码 0000 1010 => 10
print(5 << 1)  # 10

-5 << 1 = -10
-5的补码:? => 原码 1000 0101 => 反码 1111 1010 => 补码 1111 1011
-5 << 1 => 1111 0110(补码) => 反码 1111 0101 => 原码 1000 1010 => -10
print(-5 << 1)  # -10
  1. >> 右移

  • 规则:把 >> 左边的运算数的各二进制位全部右移若干位,>> 右边的数指定移动的位数,低位溢出,符号位不变,并用符号位补溢出的高位。

  • 右移一位相当于 // 2 一次。

5 >> 1 = 2
5的补码:	0000 0101
5 >> 1 =>  0000 0010(补码) => 原码 0000 0010 => 2
print(5 >> 1)  # 2

-5 >> 1 = -3
-5的补码:1111 1011
-5 >> 1 => 1111 1101(补码) => 反码 1111 1100 => 原码 1000 0011 => -3
print(-5 >> 1)  # -3 

程序控制结构

程序流程控制介绍

基本介绍

  1. 程序流程控制绝对程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句。

  2. 顺序控制、分支控制、循环控制。

顺序控制

基本介绍

  1. 顺序控制:程序从上到下逐行地执行,中间没有任何判断和跳转。

# 顺序控制
print("程序开始执行")
print("1.小明去上学")
print("2.小明学习中")
print("3.小明放学了")
print("策划给你续执行结束")

# Python中定义变量时采用合法的前向引用
num1 = 12
num2 = num1 + 2

分支控制 if-else

基本介绍

  1. 让程序有选择的执行,分支控制有三种:单分支、双分支、多分支。

单分支

  1. 基本语法:单分支是通过 if 语句来实现的,if 语句的基本语法如下:

if 条件表达式:
	代码块(可以有多条语句)
  1. if 基本语法说明:

  • 当条件表达式为 True 时,就会执行代码块;如果为 False,就不执行。

  • Python 缩进非常重要,是用于界定代码块的,相当于其他编程语言中的大括号{}。

  • 最短的缩进对较长的有包含关系,缩进前后没有要求,但是每个代码块应具有相同的缩进长度(TAB 或者相同个数的空格)。

  • 可以看成,和其它语言相比:其它语言的代码块是用{}表示的,Python 缩进就代替了{}。

# 单分支 if
if 4 < 1:
    print("ok1")
    print("ok2")
print("ok3")

if 100 > 20:
    print("ok4")
    print("ok5")
    if 8 > 2:
        print("ok6")
  1. 练习

# 请编写一个程序,可以输入人的年龄,如果年龄大于等于18岁,则输出“你年龄大于等于18,要对自己的行为负责”
age = int(input("请输入你的年龄:"))
if age >= 18:
    print("你年龄大于等于18,要对自己的行为负责")
  1. 流程图

 

30308c4f6788b2b45944c914edb77edf.png

双分支

  1. 基本语法

if 条件表达式:
	执行代码块1
else:
    执行代码块2
  1. 基本语法说明

  • if、else 是关键字,是规定好的。

  • 当条件表达式成立,即执行代码块 1,否则执行代码块 2,注意;不要少写了冒号:。

  1. 应用实例

# 请编写一个程序,可以输入人的年龄,如果年龄大于等于18岁,则输出“你年龄大于等于18,要对自己的行为负责”。否则,输出“你的年龄不大,这次放过你了”
age = int(input("请输入你的年龄:"))
if age >= 18:
    print("你年龄大于等于18,要对自己的行为负责")
else:
    print("你的年龄不大,这次放过你了")
  1. 流程图

 

39cea6d5c6ea2c692556d9a200682c20.png

  1. 练习

# 对下列代码,若有输出,指出输出结果
x = 4
y = 1
if x > 2:
    if y > 2:
        print(x + y)
    print("hsp")
else:
    print("x is", x)
# 编写程序,定义2个整数变量并赋值,判断两数之和,如果大于等于50,打印“hello world”
num1 = 30
num2 = 20
if num1 + num2 >= 50:
    print("hello world")
# 编写程序,定义2个float型变量,若果第1个数大于10.0,且第2个数小于20.0,打印两数之和
num1 = 20.0
num2 = 15.0
if num1 > 10.0 and num2 < 20.0:
    print(f"{num1} + {num2} = {num1 + num2}")
# 定义两个变量int类型,判断二者之和,是否能被3又能被5整除,打印提示信息。
num1 = 10
num2 = 5
if (num1 + num2) % 3 == 0 and (num1 + num2) % 5 == 0:
    print(f"{num1 + num2} 可以被3又能被5整除")
else:
    print(f"{num1 + num2} 不可以被3又能被5整除")
# 判断一个年份是否是闰年,闰年的条件是符合下面二者之一:(1)年份能被4整除,但不能被100整除(2)能被400整除
year = 2024  
if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
    print(f"{year} 是闰年")
else:
    print(f"{year} 不是闰年")

多分支

  1. 基本语法

if 条件表达式1:
    执行代码块1
elif 条件表达式2:
    执行代码块2
...
else:
    执行代码块n+1
  1. 流程图

 

0b64b650a94f151426df2cd8a078156e.png

  • 执行流程说明

    • 当条件表达式 1 成立时,即执行代码 1。如果表达式 1 不成立,才去判断表达式 2 是否成立。

    • 如果表达式 2 成立,就执行代码块 2。

    • 以此类推,如果所有的表达式都不成立则执行 else 的代码块。

    • 只能有一个执行入口。

  1. 案例演示

# 参加Python考试,根据得分获得对应奖励
score = int(input("请输入成绩[整数]:"))
if score >= 0 and score <= 100:
    if score == 100:
        print("BWM")
    elif score > 80 and score <= 99:
        print("iphone13")
    elif score >= 60 and score <= 80:
        print("ipad")
    else:
        print("none")
else:
    print(score, "不在0~100")
# 分析代码输出结果
b = True
if b == False:
    print("a")
elif b:
    print("b")
elif not b:
    print("c")
else:
    print("d")
  1. 练习

# 婚嫁
height = float(input("请输入身高(cm):"))
money = float(input("请输入财富(万):"))
handsome = (input("请输入颜值(帅,不帅):"))

if height > 180 and money > 1000 and handsome == "帅":
    print("我一定要嫁给他")
elif height > 180 or money > 1000 or handsome == "帅":
    print("嫁吧,比上不足,比下有余")
else:
    print("不嫁")

嵌套分支

基本介绍

  1. 基本介绍

  • 嵌套分支:在一个分支结构中又嵌套了另一个分支结构。

  • 里面的分支的结构称为内层分支,外面的分支结构称为外层分支。

  • 规范:不要超过 3 层(可读性不好)

  1. 基本语法

if:
    if:
        # if-else...
    else:
        # if-else...

应用案例

# 参加歌手比赛,若果初赛成绩大于8.0进入决赛,否则提示淘汰。并且根据性别提示进入男子组或女子组。输入成绩和性别,进行判断和输出信息。
score = float(input("请输入你的成绩:"))
if score > 8.0:
    gender = input("请输入你的性别(男|女):")
    if gender == "男":
        print("男子组决赛")
    else:
        print("女子组决赛")
else:
    print("淘汰")
# 出票系统:根据淡旺季的月份和年龄,打印票价
month = int(input("请输入当前的月份:"))
age = int(input("请输入你的年龄:"))

if 4 <= month <= 10:
    if age > 60:
        print("¥20")
    elif age >= 18:
        print("¥60")
    else:
        print("¥30")
else:
    if 18 <= age <= 60:
        print("¥60")
    else:
        print("¥20")

for 循环分支

基本介绍

  1. 基本介绍:for 循环听其名而知其意,就是让你的代码可以循环的执行,是通过 for 语句来实现的。

  2. 基本语法

for <变量> in <范围/序列>:
    <循环操作语句>
  1. for 基本语法说明

  • for、in 是关键字,是规定好的。

  • <范围/序列> 可以理解成要处理的数据集,需要是可迭代对象(比如字符串,列表等...)。

  • 循环操作语句,这里可以有多条语句,也就是我们要循环执行的代码,也叫循环体。

  • python 的 for 循环是一种“轮询机制”,是对指定的数据集,进行“轮询处理”。

for 循环执行流程分析

  1. 代码演示

# 编写一个程序,可以打印5句 “hsp” 。
# 定义一个列表(后面详细介绍),可以视为一个数据集
nums = [1, 2, 3, 4, 5]
print(nums, type(nums))

for i in [1, 2, 3, 4, 5]:
    print("hsp", i)

for i in nums:
    print("hsp", i)
  1. for 循环控制流程图

 

cfb1220abf8632ecf98dd95ceacef6ac.png

 

  1. 代码执行内存分析法

 

34864e0ff877a1b6836f71f56a533010.png

注意实现和细节说明

  1. 循环时,依次将序列中的值取出赋给变量。

  2. 如需要遍历数字序列,可以使用内置 range()函数,它会生成数列。range()生成的数列是前闭后开。

# range函数解读
class range(stop)
class range(start, stop, step=1)
# 1、虽然被称为函数,但 range 实际上是一个不可变的序列类型。
# 2、range 默认增加的步长 step是1,也可以指定,start 默认是0。
# 3、通过list() 可以查看range() 生成的序列包含的数据。
# 4、range() 生成的数列是前闭后开。
# 1、生成一个 [1, 2, 3, 4, 5]
r1 = range(1, 6, 1)
r1 = range(1, 6)
print("r1 = ", list(r1))

# 2、生成一个 [0, 1, 2, 3, 4, 5]
r2 = range(0, 6, 1)
r2 = range(0, 6)
print("r2 = ", list(r2))

# 3、生成一个 r3 =  [1, 3, 5, 7, 9]
r3 = range(1, 10, 2)
print("r3 = ", list(r3))

# 4、输出10句"hello, python"
for i in range(10):
    print("hello, python")

3、for 可以和 else 配合使用

  • 语法格式

for <variable> in <ssequence>:
    <statements>
else:
    <statements>
  • 解读:什么情况下会进入 else,就是 for 循环正常的完成遍历,在遍历过程中,没有被打断(解释:比如没有执行到 break 语句)。

# for-else案例
nums = [1, 2, 3]
for i in nums:
    print("hsp")
    # 演示break
    # if i == 2:
    #     break  # 中断-提前结束for
else:
    print("没有循环数据了...")

练习

# 以下代码输出什么
languages = ["c", "c++", "java", "python"]
for language in languages:
    print(language)

while 循环分支

基本语法

  1. 基本介绍:while 循环用于在表达式为真的情况下,重复的(循环的)执行,是通过 while 语句来实现的。

  2. 基本语法

while 判断条件(condition):
    循环操作语句(statements)...
  1. 基本语法说明

  • while 是关键字,是规定好的。

  • 当判断条件为 True 时,就执行循环操作语句,如果为 False,就退出 while。

  • 循环操作语句,这里可以有多条语句,也就是我们要循环执行的代码,也叫循环体。

while 循环执行流程分析

  1. 代码演示

# 使用while完成10句 “hsp”
i = 1
while i <= 10:
    print("hsp")
    i += 1
  1. while 循环控制流程图

 

5f75964ab3c31a059cb4d679a7c2c76c.png

  1. 代码执行内存分析法

 

12fd45c0785aefc6dd87bc3bd3af7c88.png

注意事项和细节说明

  1. while 可以和 else 配合使用

  • 语法格式

while 判断条件(condition):
    循环操作语句(statements)
else:
    其它语句<additional_statements(s)>
  • 解读:在 while ... else 判断条件为 false 时,会执行 else 的语句块,即:在遍历过程中,没有被打断(解释:比如没有执行到 break 语句)。

  1. 使用案例

# while-else使用案例
i = 0
while i < 3:
    print("hsp")
    i += 1
    # 演示break 中断
    # if i == 1:
    #     break
else:
    print("i < 3 不成立 i =", i)

练习

# 1、打印 1-100 之间所有能被3整除的数
i = 1
max_num = 100
while i <= max_num:
    if i % 3 == 0:
        print(i)
    i += 1
# 2、打印 40-200 之间所有的偶数
i = 40
max_num = 200
while i <= max_num:
    if i % 2 == 0:
        print(i)
    i += 1
# 3、不断输入姓名,知道输入“exit”为止
name = ""
while name != "exit":
    name = input("Enter your name: ")
    print("输入的内容是:", name)
# 4、打印 1~100 之间所有是9的倍数的整数,统计个数及总和
i = 1
max_num = 100
count = 0
sum = 0

while i <= max_num:
    if i % 9 == 0:
        print(i)
        count += 1
        sum += i
    i += 1
print("count:", count)
print("sum:", sum)
# 5、完成下列表达式输出
num = int(input("请输入一个整数:"))
i = 0
while i <= num:
    print(f"{i} + {num - i} = {num}")
    i += 1

多重循环控制

基本介绍

  1. 基本介绍

  • 将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for、while 均可以作为外层循环和内层循环。[建议一般使用两层,最多不要超过三层,否则,代码的可读性不好]。

  • 实际上,嵌套循环就是把内层循环当做外层循环的循环体。

  • 如果外层循环次数为 m 次,内层为 n 次,则内层循环体实际上需要执行 m*n 次。

# 如果外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
for i in range(2):
    for j in range(3):
        print("i=", i, "j=", j)

应用案例

  • 编程思想:化繁为简,先死后活。

  1. 打印空心金字塔

# 打印空心金字塔
"""
思路分析
    - 先死后活
    1、先不考虑层数的变化,嘉定就是5层,后面做活
    - 化繁为简
    1、打印矩形
    2、打印直角三角形
    3、打印金字塔
    4、打印空心金字塔
"""
# 总层数
total_level = 5
# i 控制层数
for i in range(1, total_level + 1):
    # k 控制输出空格数
    for k in range(total_level - i):
        print(" ", end="")
    # j 控制每层输出的*号个数
    for j in range(2 * i - 1):
        if j == 0 or j == 2 * i - 2 or i == total_level:
            print("*", end="")
        else:
            print(" ", end="")
    # 每层输出后,换行
    print("")
  1. 打印空心菱形

# 空心菱形
n = 5
for i in range(n):
    if i == 0:
        print(" " * (n - i - 1) + "*" * (2 * i + 1))
    else:
        print(" " * (n - i - 1) + "*" + " " * (2 * i - 1) + "*")
for i in range(n - 2, -1, -1):
    if i == 0 or i == n - 1:
        print(" " * (n - i - 1) + "*" * (2 * i + 1))
    else:
        print(" " * (n - i - 1) + "*" + " " * (2 * i - 1) + "*")
  1. 使用 while 循环来实现空心金字塔

# while实现空心金字塔
i = 0
n = 5
while i < n:
    if i == 0 or i == n - 1:
        print(" " * (n - i - 1) + "*" * (2 * i + 1))
    else:
        print(" " * (n - i - 1) + "*" + " " * (2 * i - 1) + "*")
    i += 1
  1. 统计班级情况

"""
统计3个班成绩情况,每个班有5名同学,要求:
1、求出每个班的平均分和所有班的平均分,学生的成绩从键盘输入
2、统计三个班及格人数

编程思想:(1)化繁为简(2)先死后活【先考虑3个班,每个班5名同学】
# 化繁为简
1. 先统计1个班成绩情况,求出一个班的平均分
2. 统计3个班成绩情况,求出各个班的平均分、所有班级的平均分和 及格人数
"""

total = 0.0
pass_num = 0
class_num = 3
student_num = 5
# 循环地处理3个班级的成绩
for j in range(class_num):
    # 统计一个班成绩情况
    sum = 0.0
    for i in range(student_num):
        score = float(input(f"请输入第{j + 1}班的 第{i + 1}个学生的成绩:"))
        # 判断是否及格
        if score >= 60.0:
            pass_num += 1
        sum += score
    print(f"第{j + 1}班级的情况:平均分{sum / 5}")
    total = total + sum
print(f"所有班级的平均分 {total / (student_num * class_num)},及格人数 {pass_num}")
  1. 打印九九乘法表

# 九九乘法表
for i in range(1, 10):
    for j in range(1, i + 1):
        print(f"{j} * {i} = {i * j}", end="\t")
    print()

break 语句

基本介绍

  1. 基本介绍

  • break 语句用于终止某个语句块的执行,使用在循环中

  1. 基本语法

...
break
...
  1. break 语句流程图

 

18683cf8cfb9ef259e7904287f29c3c9.png

快速入门

# 随机生成 1-100 的一个数,直到生成了97这个数,看看一共用了多少次
# random.randint(a, b) 返回随机整数N满足 a<=N<=b。相当于randrange(a, b+1)
import random

count = 0
while True:
    count += 1
    n = random.randint(1, 100)
    print(n)
    if n == 97:
        break
print("count =", count)

注意实现和细节说明

  1. break 语句是用在 for 或 while 循环所嵌套的代码。

  2. 它会终结最近的外层循环,如果循环有可选的 else 子句,也会跳过该子句。

  3. 如果一个 for 循环被 break 所终结,该循环的控制变量会保持其当前值。

# 它会终结最近的外层循环,如果循环有可选的else子句,也会跳过该子句。
count = 0
while True:
    print("hi")
    count += 1
    if count == 3:
        break
    while True:
        print("ok")
        break
else:
    print("hello")

# 如果一个for循环被break所终结,该循环的控制变量会保持其当前值。
nums = [1, 2, 3, 4, 5]
for i in nums:
    if i > 3:
        break
print("i =", i)

练习

  1. 1 - 100 以内的求和,求出当和第一次大于 20 的当前控制循环的变量是多少。

sum = 0
for i in range(1, 101):
    sum += i
    if sum > 20:
        break
print(i)
  1. 实现登录验证,有三次机会,如果用户名为“张无忌”,密码“888” 提示登录成功,否则提示还有几次机会。

change = 3
for i in range(1, 4):
    name = input("请输入你的姓名:")
    passwd = int(input("请输入你的密码:"))
    change -= 1
    if name == "张无忌" and passwd == 888:
        print("登录成功")
        break
    else:
        print(f"姓名或密码错误,还有{3 - i}次机会")

continue 语句

基本介绍

  1. 基本介绍

  • continue 语句用于 for 或 while 循环所嵌套的代码中。

  • continue 语句用于结束本次循环,继续执行循环的下一次轮次。继续执行的是:该 continue 最近的外层循环的下一次轮次。

  1. 基本语法

...
continue
...
  1. continue 语句流程图

 

6cbee5fc74728b5a30bcac4a2bf414a8.png

快速入门

# 下面代码输出结果是什么
i = 1
while i <= 4:
    i += 1
    if i == 3:
        continue
    print("i =", i)

 

2a32ae479b6a914bb137d2c32a8b1c8b.png

练习

  1. 请分析下面的代码输出结果是什么。

for i in range(0, 13):
    if i == 10:
        continue
    print("i =", i)
for i in range(0, 2):
    for j in range(1, 4):
        if j == 2:
            continue
        print("i =", i, "j =", j)

return 语句

基本介绍

  1. 基本介绍

  • return 使用在函数,表示跳出所在的函数。

  1. 演示案例

def f1():
    for i in range(1, 5):
        if i == 3:
            return
            # break
            # continue
        print("i =", i)
    print("结束for循环!")


f1()

本章作业

"""
某人有100000元,没经过一次路口,需要交费,规则如下:
当现金>50000时,每次交5%;当现金<=50000时,每次交1000.
"""
money = 100000
count = 0
while True:
    if money > 50000:
        money *= 0.95
        count += 1
    elif money >= 1000:
        money -= 1000
        count += 1
    else:
        break

print("count =", count, "money =", money)

函数

函数介绍

基本介绍

  • 为完成某一功能的程序指令(语句)的集合,称为函数。

  • 在 Python 中,函数分为:系统函数、自定义函数。

    • 内置函数,Python 解释器内置了很多函数和类型,任何时候都能使用。

    • 模块提供的函数,如:math 模块提供的函数。

    • 自定义函数:定义函数使用关键字 def,后根函数名与括号内的形参列表。函数语句从下一行开始,并且必须缩进。

  • 即:内置函数和模块中提供的函数都是系统函数(由 Python 提供),自定义函数是程序员根据业务需要开发的。

函数的好处

  • 提供代码的复用性。

  • 可以将实现的细节封装起来,然后供其他用户来调用即可。

函数的定义

基本语法

 

854a7b7516594cc3021b905f1a9f6641.png

  • 函数代码块以 def 关键字开头,后接函数标识符名称和圆括号()。

  • 函数内容以冒号 : 开始,并且缩进。

  • 函数参数 (a, b),可以有多个,也可以没有,即直接是 ()(a, b) 也称为形参列表。

  • 函数可以有返回值,也可以没有,如果没有 return 相当于返回 None。

函数的调用

  • 函数定义好了,并不会自动执行,需要程序员调用,才会执行。

  • 调用方式:函数名(实参列表),比如:max(10, 20)。

快速入门

# 自定义cry 函数,输出“喵喵喵”
# 定义函数
def cry():
    print("喵喵喵")


# 调用函数
cry()
# 自定义cal01 函数,可以计算从 1+...+1000的结果
def cal01():
    total = 0
    for i in range(1, 1001):
        total += i
    print("total =", total)


cal01()
# 自定义cal02 函数,该函数可以接收一个n,计算从 1+...+n 的结果
def cal02(n):
    total = 0
    for i in range(1, n + 1):
        total += i
    print("total =", total)


# (10) 表示我调用函数时,出入了实参 10,即把10赋给了形参n:n = 10
cal02(10)
# 自定义get_sum 函数,可以计算两个数的和,并返回结果
def get_sum(num1, num2):
    return num1 + num2


"""
1.调用get_sum(10, 50) 调用get_sum
2.(10, 50) 表示传入了两个实参10, 50,把10赋给num1,把50赋给num2
3.result 就是接收get_sum 函数返回的结果
"""
result = get_sum(20, 30)
print("result =", result)
# 使用函数解决计算问题
def cal04():
    n1 = float(input("请输入第一个数:"))
    n2 = float(input("请输入第二个数:"))
    oper = input("请输入运算符 + - * / :")
    res = 0.0
    if oper == "+":
        res = n1 + n2
    elif oper == "-":
        res = n1 - n2
    elif oper == "*":
        res = n1 * n2
    elif oper == "/":
        res = n1 / n2
    else:
        print("输入的运算符错误")

    print(n1, oper, n2, "=", res)


cal04()

函数的调用机制

函数调用过程

 

ab6831fbdd32c78ed1f42c7663d64a49.png

return 语句

  1. 基本语法

def 函数名(参数列表):
    语句...
    return 返回值
  1. 说明:如果没有 return 语句,默认返回 NoneNone内置常量,通常用来代表空值的对象。

def f1():
    print("hi")


result = f1()
# r: None <class 'NoneType'> 140736690859152
print("r:", result, type(result), id(result))

函数注意事项和使用细节

  1. 函数的参数列表可以是多个,也可以没有,如:(), (n1, n2...)

  2. 函数的命名规范遵循标识符命名规则和规范,具体参考前面讲过的标识符命名规则和规范。

  3. 函数中的变量是局部的,函数外不生效。

def hi():
    n = 10
    print("n:", n)


hi()
# 函数内部定义的n,在函数外部不能使用
# print("n:", n)
  1. 如果同一个文件,出现两个函数名相同的函数,则以就近原则进行调用。

def cry():
    print("hi")


def cry():
    print("ok")


# 这个ry会默认就近原则,即第二个cry(),输出OK。
cry()
  1. 调用函数时,根据函数定义的参数位置来传递参数,这种传参方式就是位置参数,传递的实参和定义的形参顺序和个数必须一致,同时定义的形参,不用指定数据类型,会根据传入的实参决定。

def car_info(name, price, color):
    print(f"name: {name}, price: {price}, color: {color}")


# 传递的实参和定义的形参顺序和个数必须一致,否则报 TypeError 错误
# car_info("宝马", 500000, "red", 1)  # TypeError: car_info() takes 3 positional arguments but 4 were given
car_info("宝马", 500000, "red")
  1. 函数可以有多个返回值,返回数据类型不受限制。

def f2(n1, n2):
    return n1 + n2, n1 - n2


r1, r2 = f2(10, 20)
print(f"r1: {r1}, r2: {r2}")
  1. 函数支持关键字参数。

  • 函数调用时,可以通过 形参名=实参名 形式传递参数。这样可以不受参数传递顺序的限制。

def book_info(name, price, author, amount):
    print(f"name: {name}, price: {price}, author: {author}, amount: {amount}")


# 通常调用方式,一一对应
book_info("红楼梦", 60, "曹雪芹", 30000)
# 关键字参数
book_info(name="红楼梦", price=60, amount=60000, author="曹雪芹")
book_info("红楼梦", 60, amount=90000, author="曹雪芹")
  1. 函数支持默认参数/缺省参数

  • 定义函数时,可以给函数提供默认值,调用函数时,指定了实参,则以指定为准,没有指定,则以默认值为准。默认参数放在参数列表后。

# 定义函数时,可以给参数提供默认值,调用函数时,指定了实参,则以指定为准,没有指定,则以默认值为准
def book_info2(name="<<thinking in python", price=66.8, author="龟叔", amount=1000):
    print(f"name: {name}, price: {price}, author: {author}, amount: {amount}")


book_info2()
book_info2("<<study python>>")


# 默认参数,需要放在参数列表后
def book_info3(name, price, author="龟叔", amount=1000):
    print(f"name: {name}, price: {price}, author: {author}, amount: {amount}")


book_info3("<<python 揭秘>>", 999)
  1. 函数支持可变参数/不定长参数

  • 应用场景:当调用函数时,不确定传入多个实参的情况。

  • 传入的多个实参,会被组成一个元组。元组可以存储多个数据项。

# 计算多个数的和,不是不确定是几个数,*表示[0到多],使用可变参数/不定长参数(*args)
def sum(*args):
    print(f"args: {args} 类型:{type(args)}")
    total = 0
    for els in args:
        total += els
    return total


result1 = sum()
print(result1)
result2 = sum(1, 3, 5)
print(result2)
  1. 函数的可变参数/不定长参数,还支持多个关键字参数,也就是多个 形参名=实参值

  • 应用场景:当调用函数时,不确定传入多少个关键字的情况。

  • 传入的多个关键字参数,会被组成一个字典。字典可以存储多个 键=值 的数据项。

# 接收一个人的信息,但是有哪些信息是不确定的,就可以使用关键字可变参数(**kwargs)
def person_info(**kwargs):
    print(f"kwargs: {kwargs} 类型:{type(kwargs)}")
    for kwargs_name in kwargs:
        print(f"参数名:{kwargs_name} 参数值:{kwargs[kwargs_name]}")


person_info(name="hsp", age=18, email="[email protected]")
person_info(name="hsp", age=18, email="[email protected]", sex="男", address="北京")
person_info()
  1. Python 调用另一个.py 文件的函数

# f1.py
def add(x, y):
    print("x + y =", x + y)
# f2.py
# f2.py使用f1.py的函数
# 导入f1.py(模块),就可以使用
import f1

f1.add(10, 30)

函数的传参机制

字符串和数值类型传参机制

  • 字符串和数值类型是不可变数据类型,当对应的变量的值发生了变化,它对应的内存地址会发生改变。

  1. 数值传参机制

  • 代码演示

# 字符串和数值类型传参机制
def f1(a):
    print(f"f1() a的值:{a} 地址是:{id(a)}")
    a += 1
    print(f"f1() a的值:{a} 地址是:{id(a)}")


a = 10
print(f"调用f1()前 a的值:{a} 地址是:{id(a)}")
f1(a)
print(f"调用f1()后 a的值:{a} 地址是:{id(a)}")
  • 代码执行内存分析法

 

247416d4a15cf703dcfb8bd5f418f65f.png

  1. 字符串传参机制

def f2(name):
    print(f"f2() name:{name} 地址是:{id(name)}")
    name += "hi"
    print(f"f2() name的值:{name} 地址是:{id(name)}")


name = "tom"
print(f"调用f2()前 name的值:{name} 地址是:{id(name)}")
f2(name)
print(f"调用f2()后 name的值:{name} 地址是:{id(name)}")

list、tuple、set 和 dict 传参机制

  • 代码展示

def f1(my_list):
    print(f"②f1() my_list: {my_list} 地址:{id(my_list)}")
    my_list[0] = "jack"
    print(f"③f1() my_list: {my_list} 地址:{id(my_list)}")


print("-" * 30 + "list" + "-" * 30)
my_list = ["tom", "mary", "hsp"]
print(f"①my_list: {my_list} 地址:{id(my_list)}")
f1(my_list)
print(f"④my_list: {my_list} 地址:{id(my_list)}")


def f2(my_tuple):
    print(f"②f2() my_tuple: {my_tuple} 地址:{id(my_tuple)}")
    print(f"③f2() my_tuple: {my_tuple} 地址:{id(my_tuple)}")


print("-" * 30 + "tuple" + "-" * 30)
my_tuple = ("hi", "ok", "hello")
print(f"①my_tuple: {my_tuple} 地址:{id(my_tuple)}")
f2(my_tuple)
print(f"④my_tuple: {my_tuple} 地址:{id(my_tuple)}")


def f3(my_set):
    print(f"②f3() my_set: {my_set} 地址:{id(my_set)}")
    my_set.add("红楼")
    print(f"③f3() my_set: {my_set} 地址:{id(my_set)}")


print("-" * 30 + "set" + "-" * 30)
my_set = {"水浒", "西游", "三国"}
print(f"①my_set: {my_set} 地址:{id(my_set)}")
f3(my_set)
print(f"④my_set: {my_set} 地址:{id(my_set)}")


def f4(my_dict):
    print(f"②f4() my_dict: {my_dict} 地址:{id(my_dict)}")
    my_dict['address'] = "兰若寺"
    print(f"③f4() my_dict: {my_dict} 地址:{id(my_dict)}")


print("-" * 30 + "dict" + "-" * 30)
my_dict = {"name": "小倩", "age": 18}
print(f"①my_dict: {my_dict} 地址:{id(my_dict)}")
f4(my_dict)
print(f"④my_dict: {my_dict} 地址:{id(my_dict)}")
  • 代码执行内存分析法

 

1275cd314d8fb359a033a1a49569c90f.png

小结

  • python 数据类型主要有整型 int / 浮点型 float / 字符串 str / 布尔值 bool / 元组 tuple / 列表 list / 字典 dict / 集合 set,数据类型分为两个大类,一种是可变数据类型,一种是不可变数据类型。

  • 可变数据类型和不可变数据类型。

    • 可变数据类型:当该数据类型的变量的值发生了变化,如果它的内存地址不变,那么这个数据类型就是可变数据类型。

    • 不可变数据类型:当该数据类型的变量的值发生了变化,如果它的内存地址改变了,那么这个数据类型就是不可变数据类型。

  • python 的数据类型。

    • 不可变数据类型:数值类型(int、float)、bool(布尔)、string(字符串)、tuple(元组)。

    • 可变数据类型:list(列表)、set(集合)、dict(字典)。

递归机制

基本介绍

  • 简单的说:递归就是函数自己调用自己,每次调用时传入不同的值。

  • 递归有助于编程者解决复杂问题,同时可以让代码变得简洁。

递归能解决什么问题

  • 各种数学问题:8 皇后、汉诺塔、阶乘问题、迷宫问题...

  • 各种算法中也会使用到递归,比如:快排,归并排序,二分查找,分治算法。

  • 将用栈解决的问题 -> 递归代码比较整洁。

递归举例

  1. 以下代码输出结果是什么?

  • 代码演示

def test(n):
    if n > 2:
        test(n - 1)
    print("n =", n)


test(4)
  • 代码执行内存分析法

 

893d5c0470f93b556c6357336b1c36b3.png

  1. 阶乘问题

  • 代码演示

# 阶乘
def factorial(n):
    if n == 1:
        return 1
    else:
        return factorial(n - 1) * n


print(factorial(4))
  • 代码执行内存分析法

 

d7c9e9ec738e59a9dd249b6044d0d30f.png

递归重要规则

  • 执行一个函数时,就创建一个新的空间(栈空间)。

  • 函数的变量是独立的,比如 n 变量。

  • 递归必须向退出递归的条件逼近,否则就是无线递归,就会出现 RecursionError: maximum recursion depth exceeded 超出最大递归深度。

  • 当一个函数执行完毕,或者遇到 return,就好返回,遵守谁调用,就将结果返回给谁。

练习

  1. 请使用递归的方式,求出斐波那契数 1,1,2,3,5,8,13... 给你一个整数 n,求出它的值是多少?

# 斐波那契数
def fbn(n):
    """
    功能:返回n对应的斐波那契数
    :param n: 接收一个整数 n>=1.
    :return: 返回斐波那契数
    """
    if n == 1 or n == 2:
        return 1
    else:
        return fbn(n - 1) + fbn(n - 2)


print(fbn(7))
  1. 猴子吃桃问题:有一堆桃子,猴子第一天吃了其中的一半,并在多吃了一个。以后每天猴子都吃其中的一半,然后再多吃一个。当到第 10 天时,想再吃时(即还没吃),发现只有 1 个桃子了。问最初共多少个桃子?

# 猴子吃桃
"""
day == 10,桃子树:1
day == 9, 桃子树:(day10的桃子树+1)*2
...
"""


def peach(day):
    if day == 10:
        return 1
    else:
        return (peach(day + 1) + 1) * 2


print(peach(1))
  1. 求函数值,已知 f(1) = 3; f(n)= 2*f(n-1)+1;请使用递归的思想,求出 f(n)的值?

# f(n)
def f(n):
    if n == 1:
        return 3
    else:
        return 2 * f(n - 1) + 1


print(f(10))
  1. 汉诺塔

def hanoi_tower(num, a, b, c):
    """
    输出指定num个盘子移动的顺序
    :param num: 指定盘子数
    :param a: A柱
    :param b: B柱
    :param c: C柱
    """
    if num == 1:
        print(f"第1个盘从:{a}->{c}")
    else:
        # 有多个盘,我们认为只有两个,上面所有的盘和最下面的盘
        # 移动上面所有的盘到B柱子,这个过程会借助到C柱子
        hanoi_tower(num - 1, a, c, b)
        # 移动最下面的盘
        print(f"第{num}个盘从:{a}->{c}")
        # 把上面所有的盘从B柱子移动到C柱子,这个过程会使用到A柱子
        hanoi_tower(num - 1, b, a, c)


hanoi_tower(3, "A", "B", "C")

函数作为参数传递

应用实例

# 定义一个函数,可以返回两个数的最大值
def get_max_val(num1, num2):
    max_val = num1 if num1 > num2 else num2
    return max_val


def f1(fun, num1, num2):
    """
    功能:调用fun返回num1和num2的最大值
    :param fun: 表示接收一个函数
    :param num1: 传入一个数
    :param num2: 传入一个数
    :return: 返回最大值
    """
    return fun(num1, num2)


def f2(fun, num1, num2):
    """
    功能:调用fun返回num1和num2的最大值,同时返回两个数的和
    :param fun:
    :param num1:
    :param num2:
    :return:
    """
    return num1 + num2, fun(num1, num2)


print(f1(get_max_val, 10, 20))
print(f2(get_max_val, 10, 20))
x, y = f2(get_max_val, 10, 20)
print(f"x: {x}, y: {y}")

注意事项和细节

  • 函数作为参数传递,传递的不是数据,而是业务处理逻辑。

  • 一个函数,可以接收多个函数作为参数传入。

# f3接收多个函数作为参数传入
def f3(my_fun, num1, num2, my_fun2):
    return my_fun2(num1, num2), my_fun(num1, num2)


# 定义一个函数,可以返回两个数的最大值
def get_max_val(num1, num2):
    max_val = num1 if num1 > num2 else num2
    return max_val


# 定义函数,可以返回两个数的和
def get_sum(num1, num2):
    return num1 + num2


print(f3(get_max_val, 100, 200, get_sum))

lambda 匿名函数

基本介绍

  1. 基本介绍

  • 如果我们有这样一个需求,需要将函数作为参数进行传递,但是这个函数只使用一次,这时,我们可以考虑使用 lambda 匿名函数。

  1. 函数的定义

  • def 关键字,可以定义带有名称的函数,可以重复使用。

  • lambda 关键字,可以定义匿名函数(无名称),匿名函数只能使用一次。

  • 匿名函数用于临时创建一个函数,只使用一次的场景。

  1. 匿名函数基本语法

  • lambda 形参列表:函数体(一行代码)。

  • lambda 关键字,表示定义匿名函数。

  • 形参列表:比如 num1,num2 表示接收两个参数。

  • 函数体:完成的功能,只能写一行,不能写多行代码。

应用实例

def f1(fun, num1, num2):
    """
    功能:调用fun返回num1和num2的最大值
    :param fun: 接收函数(匿名的)
    :param num1:
    :param num2:
    :return:
    """
    print(f"fun类型:{type(fun)}")
    return fun(num1, num2)


"""
1. lambda a, b: a if a > b else b
2. 不需要return, 运算结果就是返回值
"""
max_value = f1(lambda a, b: a if a > b else b, 1, 2)
print(max_value)

全局变量和局部变量

基本介绍

  1. 基本介绍

  • 全局变量:在整个程序范围内都可以访问,定义在函数外,拥有全局作用域的变量。

  • 局部变量:只能在其被声明的函数范围内访问,定义在函数内部,拥有局部作用域的变量。

  1. 代码演示

# n1 是全局变量
n1 = 100


def f1():
    # n2是局部变量
    n2 = 200
    print(n2)
    # 可以访问全局变量n1
    print(n1)


# 调用
f1()
print(n1)

# 不能访问局部变量n2
# print(n2)  # NameError: name n2' is not defined.

注意事项和细节

  1. 未在函数内部重新定义 n1,那么默认使用全局变量 n1。

  2. 在函数内部重新定义了 n1,那么根据就近原则,使用的就是函数内部重新定义的 n1。

n1 = 100


def f1():
    # n1 重新定义了
    n1 = 200
    print(n1)


f1()
print(n1)
  1. 在函数内使用 global 关键字,可以标明指定使用全局变量。

n1 = 100


def f1():
    # global 关键字标明使用全局变量 n1
    global n1
    n1 = 200
    print(n1)


f1()
print(n1)

数据容器

基本介绍

  • 数据容器是一种数据类型,有些地方也简称容器/collections。

  • 数据容器可以存放多个数据,每一个数据也被称为一个元素。

  • 存放的数据/元素可以是任意类型。

  • 简单的说,数据容器就一种可以存放多个数据/元素的数据类型。

列表(list)

基本介绍

  • 列表可以存放多个不同数据类型,即:列表就是一列数据(多个数据)。

  • 列表也是一种数据类型。

列表的定义

  • 列表的定义:创建一个列表,只要用逗号分隔不同的数据项使用方括号括起来即可,示例如下:

list1 = [100, 200, 300, 400, 500]
list2 = ["red", "green", "blue", "yellow", "white", "black"]
  • 列表内存图

 

a833ddca8ca63ad5b1db5b14ec72c6f1.png

# 列表的定义
list1 = [100, 200, 300, 400, 500]
print(list1)
print(type(list1))

列表的使用

  • 列表的使用方法:列表名 [下标]

  • 注意:索引是从 0 开始计算的。

list2 = ["red", "green", "blue", "yellow", "white", "black"]
print(list2)
print(f"第三个元素是:{list2[2]}")

列表的遍历

  • 简单来说,列表的遍历就是将列表的每个元素以此取出,进行处理的操作,就是遍历/迭代。

# 内置函数 len()可以返回对象的长度(元素个数)。
list_color = ["red", "green", "blue", "yellow", "white", "black"]
index = 0
while index < len(list_color):
    print(f"第{index + 1}个元素是:{list_color[index]}")
    index += 1
list_color = ["red", "green", "blue", "yellow", "white", "black"]
for ele in list_color:
    print(f"元素是:{ele}")

列表解决养鸡场问题

hens = [3, 5, 1, 3.4, 2, 50]
total_weight = 0.0
for ele in hens:
    total_weight += ele
print(f"总体重:{total_weight} 平均体重:{round(total_weight/len(hens), 2)}")

注意事项和使用细节

  • 如果我们需要一个空列表,可以通过 []list() 方式来定义。

list1 = []
list2 = list()

print(list1, type(list1))
print(list2, type(list2))
  • 列表的元素可以有多个,而且数据类型没有限制,允许有重复元素,并且是有序的。

list3 = [100, "jack", 4.5, True, "jack"]
print(list3)

# 嵌套列表
list4 = [100, "tom", ["天龙八部", "笑傲江湖", 300]]
print(list4)
  • 列表的索引/下标是从 0 开始的。

  • 列表索引必须在指定范围内使用,否则报 IndexError: list index out of range。

list5 = [1, 2]
print(list5[2])  # IndexError: list index out of range
  • 索引也可以从尾部开始,最后一个元素的索引为-1, 往前一位为-2,以此类推。

 

9268cb484f759035e7735d3bd296b5e7.png

list6 = ["red", "green", "blue", "yellow", "white", "black"]
print(list6[-1])
print(list6[-6])
# 依然不能索引越界
print(list6[-7])  # IndexError: list index out of range
  • 通过 列表[索引]=新值 对数据进行更新,使用 列表.append(值) 方法来添加元素,使用 del语句 来删除列表的元素,注意不能超出有效索引范围。

list_a = ["天龙八部", "笑傲江湖"]
print("list_a:", list_a)
list_a[0] = "雪山飞狐"
print("list_a:", list_a)
list_a.append("倚天屠龙")
print("list_a:", list_a)
del list_a[1]
print("list_a:", list_a)
  • 列表是可变序列(要注意其使用特点)。

# 列表是可变序列(要注意其使用特点)。
list1 = [1, 2.1, "hsp"]
print(f"list1: {list1} 地址:{id(list1)} 第三个元素地址 {id(list1[2])}")
list1[2] = "python"
print(f"list1: {list1} 地址:{id(list1)} 第三个元素地址 {id(list1[2])}")

 

a0b1e01aa8536e9e701e0ba51edd9a27.png

  • 列表在赋值时的特点

# 扩展一下,列表在赋值时的特点
list1 = [1, 2.1, "hsp"]
list2 = list1
list2[0] = 500
print("list2:", list2)
print("list1:", list1)

 

46e2b0a4bbbf237e0dd4a7860b9697c0.png

  • 列表在函数传参时的特点

# 列表在函数传参时的特点
def f1(l):
    l[0] = 100
    print("l的id:", id(l))


list10 = [1, 2.1, 200]
print("list10的id:", id(list10))
f1(list10)
print("list10:", list10)

 

cc4fca07968c6f41a455846661ed976a.png

列表的常用操作

  1. 列表常用操作](内置类型 — Python 3.13.0 文档)

 

9d0663d2f20c1faa6b1fa5e96866dad1.png

  1. 常用操作一览

序号函数
1len(list):列表元素个数
2max(list):返回列表元素最大值
3min(list):返回列表元素最小值
4list(seq):将元组转换成列表
序号方法
1list.append(obj):在列表末尾添加新的对象
2list.count(obj):统计某个元素在列表中出现的次数
3list.extend(seq):在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
4list.index(obj):从列表中找出某个值第一个匹配项的索引位置
5list.insert(index, obj):将对象插入列表
6list.pop([index =-1]):移除列表中的一个元素(默认最后一个元素),并返回该元素的值
7list.remove(obj):移除列表中某个值的第一个匹配项
8list.reverse():反向列表中元素
9list.sort(key = None, reverse = False):对原列表进行排序
10list.clear():清空列表
11list.copy():复制列表
  1. 演示列表常用操作

# 演示列表常用操作
list_a = [100, 200, 300, 400, 600]
print("list_a 列表元素个数:", len(list_a))
print("list_a 列表最大元素:", max(list_a))
print("list_a 列表最小元素:", min(list_a))

# list.append(obj):在列表末尾添加新的对象
list_a.append(900)
print("list_a:", list_a)

# list.count(obj):统计某个元素在列表中出现的次数
print("100出现的次数:", list_a.count(100))

# list.extend(seq):在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
list_b = [1, 2, 3]
list_a.extend(list_b)
print("list_a:", list_a)

# list.index(obj):从列表中找出某个值第一个匹配项的索引位置
print("300第1次出现在序列的索引是:", list_a.index(300))
# 如果找不到,会报错:ValueError
# print("1000第1次出现在序列的索引是:", list_a.index(1000))  # ValueError: 1000 is not in list

# list.insert(index, obj):将对象插入列表
list_a.insert(1, 666)
print("list_a:", list_a)

# list.pop([index=-1]):移除列表中的一个元素(默认最后一个元素),并返回该元素的值
print(list_a.pop())
print("list_a:", list_a)

# list.remove(obj):移除列表中某个值的第一个匹配项                                             |
list_a.remove(666)
print("list_a:", list_a)

# list.reverse():反向列表中元素
list_a.reverse()
print("list_a:", list_a)

# list.sort(key=None, reverse=False):对原列表进行排序
list_a.sort()
print("list_a:", list_a)

# list.copy():复制列表
list_c = list_a.copy()
print("list_c:", list_a)

# list.clear():清空列表
list_a.clear()
print("list_a:", list_a)
  1. 列表生成式

  • 列表生成式就是“生成列表的公式”

  • 基本语法

[列表元素的表达式 for 自定义变量 in 可迭代对象]
  • 案例演示

# 列表生成式
list_1 = [ele * 2 for ele in range(1, 5)]
print("list_1:", list_1)  # list_1: [2, 4, 6, 8]

list2 = [ele + ele for ele in "hsp"]
print("list2:", list2)  # list2: ['hh', 'ss', 'pp']

list3 = [ele * ele for ele in range(1, 11)]
print("list3:", list3)

练习

# 循环从键盘输入5个成绩,保存到列表,并输出。
scores = []
for _ in range(5):
    score = float(input("请输入成绩:"))
    scores.append(score)
print(f"成绩情况:{scores}")

元组(tuple)

基本介绍

  • 元组(tuple)可以存放多个不同类型数据,元组是不可变序列。

    • tuple 不可变是指当你创建了 tuple 时,它就不能改变了,也就是说它没有 append(),insert() 这样的方法,但它也有获取某个索引值的方法,但是不能重新复制。

  • 元组也是一种数据类型。

元组的定义

  • 创建一个元组,只要把逗号分隔不同的数据线,使用圆括号括起来即可。

# 元组的定义
tuple_a = (100, 200, 300, 400, 500)
print("tuple_a =", tuple_a, type(tuple_a))

元组的使用

  • 元组使用语法:元组名[索引]

# 元组的使用
tuple_b = ("red", "green", "blue", "yellow", "white", "black")
print("第三个元素是:", tuple_b[2])

元组的遍历

  • 简单来说,就是将元组的每个元素以此取出,进行处理的操作,就是遍历/迭代。

# while 遍历元组
tuple_color = ("red", "green", "blue", "yellow", "white", "black")
index = 0
while index < len(tuple_color):
    print(tuple_color[index])
    index += 1
# for 变量元组
tuple_color = ("red", "green", "blue", "yellow", "white", "black")
for ele in tuple_color:
    print(ele)

注意事项和使用细节

  • 如果我们需要一个空元组,可以通过(),或者 tuple() 方式来定义。

tuple_a = ()
tuple_b = tuple()
print(f"tuple_a : {tuple_a}")
print(f"tuple_b : {tuple_b}")
  • 元组的元素可以有多个,而且数据类型没有限制(甚至可以嵌套元组),允许有重复元素,并且是有序的。

tuple_c = (100, "jack", 4.5, True, "jack")
print(f"tuple_c : {tuple_c}")

# 嵌套元组
tuple_d = (100, "tom", ("天龙八部", "笑傲江湖", 300))
print(f"tuple_d : {tuple_d}")
  • 元组的索引/下标是从 0 开始的。

  • 元组索引必须在指定范围内使用,否则报:IndexError: tuple index out of range 。

tuple_e = (1, 2.1, "hsp")

print(tuple_e[1])
# 索引越界
print(tuple_e[3])  # IndexError: tuple index out of range
  • 元组是不可变序列。

tuple_f = (1, 2.1, "hsp")

# 不能修改
# tuple_f[2] = "python"  # TypeError: 'tuple' object does not support item assignment
  • 可以修改元组内 list 的内容(包括修改、增加、删除等)

tuple_g = (1, 2.1, "hsp", ["jack", "tom", "mary"])
print(tuple_g[3])
print(tuple_g[3][0])

# 修改
tuple_g[3][0] = "HSP"
print(tuple_g)
# 不能替换整个列表元素
# tuple_g[3] = [10, 20]  # TypeError: 'tuple' object does not support item assignment

# 删除
del tuple_g[3][0]
print(tuple_g)

# 增加
tuple_g[3].append("smith")
print(tuple_g)
  • 索引也可以从尾部开始,最后一个元素的索引为 -1,往前一位为 -2,以此类推。

tuple_h = (1, 2.1, "hsp", ["jack", "tom", "mary"])
print(tuple_h[-2])
  • 定义只有一个元素的元组,需要带上逗号,否则就不是元组类型。

tuplg_i = (100,)
print(f"tuplg_i : {tuplg_i}", type(tuplg_i))
# 输出 tuplg_i : (100,) <class 'tuple'>

tuplg_j = (100)
print(f"tuplg_j : {tuplg_j}", type(tuplg_j))
# 输出 tuplg_j : 100 <class 'int'>
  • 既然有了列表,python 设计者为什么还提供元组这样的数据类型呢?

    • 在项目中,尤其是多线程环境中,有经验的程序员会考虑使用不变对象(一方面因为对象状态不能修改,索引可以避免由此引起的不必要的程序错误;另一方面一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。可以方便的被共享访问)。所以,如果不需要对元素进行添加、删除、修改的情况下,可以考虑使用元组。

    • 元组在创建时间和占用的空间什么都优于列表。

    • 元组能够对不需要修改的数据写保护。

元组的常用操作

 

78c1eb02a8fc80dc1fed1009d59d5338.png

  • 常用操作一览

序号操作
1len(tuple):元组元素个数
2max(tuple):返回元组元素最大值
3min(tuple):返回元组元素最小值
4tuple.count(obj):统计某个元素在元组中出现的次数
5tuple.index(obj):从元组中找出某个值第一个匹配项的索引位置
# 演示元组常用操作
tuple_a = (100, 200, 300, 400, 600, 200)
print("tuple_a 元组元素个数:", len(tuple_a))
print("tuple_a 元组最大元素:", max(tuple_a))
print("tuple_a 元组最小元素:", min(tuple_a))

# tuple.count(obj):统计某个元素在列表中出现的次数
print("100出现的次数:", tuple_a.count(100))
print("200出现的次数:", tuple_a.count(200))

# tuple.index(obj):从列表中找出某个值第一个匹配项的索引位置
print("200第1次出现在元组的索引:", tuple_a.index(200))
# 如果找不到,会报错:ValueError: tuple.index(x): x not in tuple
# print("1000第1次出现在元组的索引:", tuple_a.index(1000))  # ValueError: tuple.index(x): x not in tuple

# x in s:s中的某项等于x 则结果为True,否则为False
print(300 in tuple_a)  # True

练习

"""
定义一个元组:("大话西游", "周星驰", 80, ["周星驰", "小甜甜"])
信息为:(片名, 导演, 票价, 演员列表)
"""

tuple_move = ("大话西游", "周星驰", 80, ["周星驰", "小甜甜"])
print("票价对应的索引:", tuple_move.index(80))
# 遍历所有演员
for ele in tuple_move[3]:
    print(ele)

# 删除 "小甜甜", 增加演员 "牛魔王",“猪八戒”
del tuple_move[3][1]
tuple_move[3].append("牛魔王")
tuple_move[3].append("猪八戒")
print(tuple_move)

字符串(str)

基本介绍

  • 在 python 中处理文本数据是使用 str 对象,也称为字符串。字符串是由 Unicode 码位构成的不可变序列。

    • Unicode 码是一种字符编码。

    • ord():对表示单个 Unicode 字符的字符串,返回代表它 Unicode 码点的整数。例如 ord('a') 返回整数 97

    • chr():返回 Unicode 码位为整数 i 的字符的字符串格式。例如,chr(97) 返回字符串 'a'

  • 字符串字面值有三种写法。

    • 单引号:‘允许包含有“双”引号'。

    • 双引号:"运行嵌入'单'引号"。

    • 三重引号:'''三重单引号''',"" "三重双引号" "",使用三重引号的字符串可以跨越多行——其中所有的空白字符都将包含在该字符串字面值中。

  • 字符串是字符的容器,一个字符串可以存放多个字符,比如 "hi-韩顺平教育"。

 

3cb638f9c895b6ab28574aac20141395.png

字符串支持索引

  • 使用语法:字符串名[索引]

str_a = "red-green"
print("str_a的第三个值/字符是:", str_a[2], type(str_a[2]))

字符串的变量

  • 简单的说,就是将字符串的每个元素依次取出,进行处理的操作,就是遍历/迭代。

# 使用while和for遍历字符串
str_b = "hi-韩顺平教育"
index = 0
while index < len(str_b):
    print(str_b[index])
    index += 1

for ele in str_b:
    print(ele)

注意事项和使用细节

  • 字符串索引必须在指定范围内使用,否则报: 。索引也可以从尾部开始,最后一个元素的索引为-1,往前一位为-2,以此类推。

  • 字符串是不可变序列,不能修改。

str_b = "hi-韩顺平教育"

# 通过索引可以访问指定元素
print(str_b[3])
# 不能修改元素
# str_b[3] = "李"  # TypeError: 'str' object does not support item assignment
  • 在 Python 中,字符串长度没有固定的限制,取决于计算机内存大小。

字符串常用操作

 

188add19a52bb79690193d9129be94f1.png

  • 常用操作一览

序号操作
1len(str):字符串的长度,也就是包含多少个字符
2str.replace(old, new [, count]):返回字符串的副本,其中出现的所有子字符串 old 都将被替换为 new。如果给出了可选参数 count,则只替换前 count 次出现。
3str.split(sep = None, maxaplit =-1):返回一个由字符串内单词组成的列表,使用 seq 作为分隔字符串。如果给出了 maxsplit,则最多进行 maxsplit 次拆分(因此,列表最多会有 maxsplit + 1 个元素)。如果 maxsplit 未指定或为 -1,则不限制拆分次数(进行所有可能的拆分)
4str.count(sub):统计指定字符串在字符串中出现的次数
5str.index(sub):从字符串中找出指定字符串第一个匹配项的索引位置
6str.strip([chars]):返回原字符串的副本,移除其中的前导和末尾字符。chars 为指定要移除字符的字符串
7str.lower():返回原字符串小写的副本
8str.upper():返回原字符串大写的副本
# 演示字符串常用操作
str_names = " jack tom mary hsp nono tom "

# len(str):字符串的长度,也就是包含多少个字符
print(f"{str_names}有{len(str_names)}个字符")

# str.replace(old, new[, count]):返回字符串的副本,其中出现的所有子字符串 old 都将被替换为 new。如果给出了可选参数 count,则只替换前 count 次出现。
# 说明:返回字符串的副本:表示原来的字符串不变,而是返回一个新的字符串。
str_names_new = str_names.replace("tom", "汤姆", 1)
print("str_names_new:", str_names_new)
print("str_names:", str_names)

# str.split(sep=None, maxaplit=-1):返回一个由字符串内单词组成的列表,使用 seq 作为分隔字符串。如果给出了 maxsplit,则最多进行 maxsplit 次拆分(因此,列表最多会有
# maxsplit + 1个元素)。如果 maxsplit 未指定或为 -1,则不限制拆分次数(进行所有可能的拆分)
str_names_split = str_names.split(" ")
print("str_names_split:", str_names_split, type(str_names_split))

# str.count(sub):统计指定字符串在字符串中出现的次数
print("tom出现的次数:", str_names.count("tom"))

# str.index(sub):从字符串中找出指定字符串第一个匹配项的索引位置
print(f"tom出现的索引:{str_names.index('tom')}")

# str.strip([chars]):返回原字符串的副本,移除其中的前导和末尾字符。chars 为指定要移除字符的字符串
# 说明:这个方法通常用于除去前后的空格,或者去掉制定的某些字符
str_names_strip = str_names.strip()
print("str_names_strip:", str_names_strip)
print("123t2om13".strip("123"))

# str.lower():返回原字符串小写的副本
str_names = "hspHaHa"
str_names_lower = str_names.lower()
print("str_names_lower:", str_names_lower)

# str.upper():返回原字符串大写的副本
str_names_upper = str_names.upper()
print("str_names_upper:", str_names_upper)

字符串比较

  • 字符串比较

    • 运算符:>,>=,<,<=,==,!=

    • 比较规则:首先比较两个字符串中的第一个字符,如果相等则继续比较下一个字符,依次比较下去,直到两个字符串中的字符不相等时,其比较结果就是两个字符串的比较结果,两个字符串中的后续字符将不再比较。

    • 比较原理:两个字符进行比较时,比较的是其 ordinal value(原始值/码值),调用内置函数 ord 可以得到指定字符的 ordinal value。与内置函数 ord 对应的是内置函数 chr,调用内置函数 chr 时指定 ordinal value 可以得到其对应的字符。

# ord()与chr()使用
print(ord('1'))
print(ord('A'))
print(ord('a'))
print(ord('h'))

print(chr(49))
print(chr(65))
print(chr(97))
print(chr(104))
# 字符串比较
print("tom" > "hsp")
print("tom" > "to")
print("tom" > "tocat")
print("tom" < "老韩")
print("tom" > "tom")
print("tom" <= "tom")
"""
定义一个字符串,str_names="tom jack mary nono smith hsp"
统计一共有多个人名;如果有"hsp"则替换成"老韩";如果人名是英文,则把首字母改为大写
str.capitalize():字符串首字符改为大写
"""

str_names = str("tom jack mary nono smith")

str_names_list = str_names.split(" ")
print(f"人名的个数:{len(str_names_list)}")
str_names_re = str_names.replace("hsp", "韩顺平")
print(str_names_re)

str_names_upper = ""
for ele in str_names_list:
    if ele.isalpha():
        str_names_upper += ele.capitalize() + " "

# 去掉两边的" "
str_names_upper = str_names_upper.strip(" ")
print(str_names_upper)

切片

基本介绍

  • 切片:从一个序列中,取出一个子序列,在实际开发中,程序员经常对序列进行切片操作。

  • 序列:序列是指,内容连续、有序,可使用索引的一类数据容器。

 

5845e10ac3529eb2d6b53ce926ebec6e.png

  • 上文的列表(list)、元组(tuple)、字符串均可视为序列。

基本语法

  • 基本语法:序列[起始索引:结束索引:步长]

    • 表示从序列中,从指定的起始索引开始,按照指定的步长,依次取出元素,到指定结束索引为止,截取到一个新的序列。

    • 切片操作是前闭后开,即 [其实索引:结束索引),截取的子序列包括起始索引,但是不包括结束索引。

    • 步长表示,依次取出元素的间隔。

      • 步长为 1:一个一个的取出元素;

      • 步长为 2:每次跳过一个元素取出;

      • 步长为 3,每次跳过 N-1 个元素取出。

# 字符串切片,截取"hello"
str_a = "hello,world"
print(str_a[0:5:1])

# 列表切片,截取["tom", "nono"]
list_a = ["jack", "tom", 'yoyo', "nono", "hsp"]
print(list_a[1:4:2])

# 元组切片,截取(200, 300, 400, 500)
tuple_a = (100, 200, 300, 400, 500, 600)
print(tuple_a[1:5:1])

注意事项和使用细节

  • 切片语法:序列[起始索引:结束索引:步长],起始索引如果不写,默认为 0,结束索引如果不写,默认截取到结果,步长如果不写,默认为 1。

str_a = "hello,hspjy"
str_slice01 = str_a[:5:1]
print("str_slice01", str_slice01)
str_slice02 = str_a[1::1]
print("str_slice02", str_slice02)
str_slice03 = str_a[::1]
print("str_slice03", str_slice03)
str_slice04 = str_a[2:5:]
print("str_slice04", str_slice04)
  • 切片语法:序列[起始索引:结束索引:步长],步长为负数,表示反向取,同时注意起始索引和结束索引也要反向标记。

str_b = "123456"
str_slice05 = str_b[-1::-1]
print("str_slice05", str_slice05)

str_slice06 = str_b[-1:-6:-1]
print("str_slice06", str_slice06)
  • 切片操作并不会影响原序列,而是返回了一个序列。

str_c = "ABCD"
str_slice07 = str_c[1:3:1]
print("str_slice07", str_slice07)
print("str_c", str_c)

练习

# 定义列表 list_name = ["Jack", "Lisa", "Hsp", "Paul", "Smith", "Kobe"]
# 取出前三个名字;取出后三个名字,,并且保证原来顺序
list_name = ["Jack", "Lisa", "Hsp", "Paul", "Smith", "Kobe"]
list_name_f = list_name[0:3:1]
list_name_b = list_name[-1:-4:-1]
list_name_b.reverse()
print(f"前三个名字:{list_name_f}")
print(f"后三个名字:{list_name_b}")

集合(set)

基本介绍

  • python 支持集合这种数据类型,集合是由不重复元素组成的无序容器。

    • 不重复元素:集合中不会有相同的元素。

    • 无序:集合中元素取出的顺序,和你定义时元素顺序并不能保证一致。

  • 集合对象支持合集、交集、差集等数学运算。

  • 既然有了列表、元组这些数据容器,python 设计者为什么还提供集合这样的数据类型呢?

    • 在项目中,我们可能有这样的需求:需要记录一些数据,而这些数据必须保证是不重复的,而且数据的顺序并没有要求,就可以考虑使用集合。

    • 回顾:列表、元组的元素是可以重复,而且有序。

集合的定义

  • 创建一个集合,只要用逗号分隔不同的数据项,并使用{}括起来即可。

set_a = {100, 200, 300, 400, 500}
basket = {"apple", "orange", "pear", "banana"}
print(f"set_a = {set_a}, basket = {basket}")
print(f"set_a类型 = {type(set_a)}")
print(f"basket类型 = {type(basket)}")

注意事项和使用细节

  • 集合是由不重复元素组成的无序容器。

# 不重复元素组成,可以理解成自动去重
basket = {"apple", "orange", "apple", "pear", "orange", "banana"}
print(f"basket: {basket}")

# 无序,也就是你定义元素的顺序和取出的顺序不能保证一致
# 集合底层会按照自己的一套算法来存储和取数据,所以每次取出顺序是不变的
set_a = {100, 200, 300, 400, 500}
print(f"set_a: {set_a}")
print(f"set_a: {set_a}")
print(f"set_a: {set_a}")
  • 集合不支持索引。

set_a = {100, 200, 300, 400, 500}
# print(set_a[0])  # TypeError: 'set' object is not subscriptable
  • 既然集合不支持索引,所以对集合进行遍历不支持 while,只支持 for。

basket = {"apple", "orange", "apple", "pear", "orange", "banana"}
for ele in basket:
    print(ele)
  • 创建空集合只能用 set(),不能用 {}{} 创建的是空字典。

set_b = {}  # 定义空集合不对,这是空字典
set_c = set()  # 创建空集合
print(f"set_b: {type(set_b)}")
print(f"set_c: {type(set_c)}")

集合常用操作

 

b68638fad7949d5e3e594e3b70728741.png

  • 集合常用操作一览

序号操作
1len(集合):集合元素个数
2x in s:检测 x 是否为 s 中的成员
3add(elem):将元素 elem 添加到集合中
4remove(elem):从集合中移除元素 elem。如果 elem 不存在于集合中则会引发 KeyError
5pop():从集合中移除并返回任意一个元素。如果集合为空则会引发 KeyError
6clear():从集合中移除所有元素
7union(*others) set | other | ... :返回一个新集合,其中包含来自原集合以及 others 指定的所有集合中的元素
8intersection(*others) set & other & ...:返回一个新集合,其中包含原集合以及 others 指定的所有集合中共有的元素
9difference(*others) set - other - ... :返回一个新集合,其中包含原集合以及 others 指定的其他结合中不存在的元素
basket = {"apple", "orange", "apple", "pear", "orange", "banana"}
# len(集合):集合元素个数
print("basket元素个数:", len(basket))

# x in s:检测x是否为s中的成员
print("apple" in basket)

# add(elem):将元素elem添加到集合中
basket.add("grape")
print("basket:", basket)

# remove(elem):从集合中移除元素elem。如果elem不存在于集合中则会引发KeyError
basket.remove("apple")
print("basket:", basket)
# basket.remove("aaa")  # KeyError: 'aaa'

# pop():从集合中移除并返回任意一个元素。如果集合为空则会引发KeyError
ele = basket.pop()
print("ele:", ele, "type:", type(ele))
print("basket:", basket)

# clear():从集合中移除所有元素
basket = {"apple", "orange", "apple", "pear", "orange", "banana"}
print("basket:", basket)

# union(*others) <br />set | other | ... :返回一个新集合,其中包含来自原集合以及 others 指定的所有集合中的元素
books = {"天龙八部", "笑傲江湖"}
books_2 = {"雪山飞狐", "神雕侠侣", "天龙八部"}
books_3 = books.union(books_2)
books_3 = books | books_2  # 等价于上一行代码
print("books_3:", books_3)

# intersection(*others) <br />set & other & ...:返回一个新集合,其中包含原集合以及 others 指定的所有集合中共有的元素
books_4 = books.intersection(books_2)
books_4 = books & books_2  # 等价于上一行代码
print("books_4:", books_4)

# difference(*others)<br />set - other - ... :返回一个新集合,其中包含原集合以及 others 指定的其他结合中不存在的元素
books_5 = books.difference(books_2)
books_5 = books - books_2
print("books_5:", books_5)
books_6 = books_2 - books
print("books_6:", books_6)

集合生成式

  • 集合生成式就是“生成集合的公式“

  • 基本语法:{集合元素的表达式 for 自定义变量 in 可迭代对象}

set1 = {ele * 2 for ele in range(1, 5)}
print("set1:", set1)

set2 = {ele + ele for ele in "hsp"}
print("set2:", set2)
  • 注意:集合生成式和列表生成式的区别就在于,集生成式使用{},列表生成式使用[]

练习

s_history = {"小明", "张三", "李四", "王五", "Lily", "Bob"}
s_politic = {"小明", "小花", "小红", "二狗"}
s_english = {"小明", "Lily", "Bob", "Davil", "李四"}

# 求选课学生共有多少人
total = s_history.union(s_politic).union(s_english)
print("总人数:", len(total))

# 求只选了第一个学科(history)的学生数量和学生姓名
total_history = s_history.difference(s_politic).difference(s_english)
print("数量:", len(total_history), "姓名:", total_history)

# 求只选了一门学科的学生数量和学生姓名
total_politic = s_politic.difference(s_english).difference(s_history)
total_english = s_english.difference(s_history).difference(s_politic)
total_one = total_history.union(total_politic).union(total_english)
print("数量:", len(total_one), "姓名:", total_one)

# 求选了三门学科的学生数量和学生姓名
total_three = s_history.intersection(s_politic).intersection(s_english)
print("数量:", len(total_three), "姓名:", total_three)

字典(dict)

基本介绍

  • 字典(dict,完整单词是dictionary)也是一种常用的python数据类型,其他语言可能把字典称为联合内存或联合数组。

  • 字典是一种映射类型,非常适合处理通过xx查询yy的需求,这里的xx我们称为Key(键/关键字),这里的yy我们称为Value(值),即Key—Value的映射关系。

字典的定义

  • 字典的定义:创建一个字典,只要把逗号分隔的不同的元素,用{}括起来即可,存储的元素是一个个的键值对

dict_a = {key: value, key: value, key: value ...}
  • 通过key取出对应的Value的语法:字典名[key]

dict_a [key]
tel = {"jack": 100, "bob": 200}

print(f "tel:{tel}, type(tel):{type(tel)}")
print("jack 的 tel:", tel ['jack'])

注意事项和使用细节

  • 字典的key(关键字)通常是字符串或数字,value可以是任意数据类型。

字典是以 进行索引的,键可以是任何不可变类型;字符串和数字总是可以作为键。 如果一个元组只包含字符串、数字或元组则也可以作为键;如果一个元组直接或间接地包含了任何可变对象,则不能作为键。 列表不能作为键,因为列表可以使用索引赋值、切片赋值或者 append()extend() 等方法进行原地修改列表。

dict_a = {
    "jack": [100, 200, 300],
    "mary": (10, 20, "hello"),
    "nono": {"apple", "pear"},
    "smith": "计算机老师",
    "周星驰": {
        "性别": "男",
        "age": 18,
        "地址": "香港"
    },
    "key1": 100,
    "key2": 9.8,
    "key3": True
}
print(f "dict_a = {dict_a} type(dict_a) = {type(dict_a)}")
  • 字典不支持索引,会报KeyError。

print(dict_a [0])  # KeyError: 0
  • 既然字典不支持索引,所以对字典进行遍历不支持while,只支持for,注意直接对字典进行遍历,遍历得到的是key。

dict_b = {"one": 1, "two": 2, "three": 3}
# 遍历方式 1:依次取出 key,再通过 dict [key] 取出对应的 value
for key in dict_b:
    print(f "key: {key}, value: {dict_b [key]}")

# 遍历方式 2:依次取出 value
for value in dict_b.values():
    print(f "value: {value}")

# 遍历方式 3:依次取出 key-value
for key, value in dict_b.items():
    print(f "key: {key}, value: {value}")
  • 创建空字典可以通过{},或者dict()

dict_c = {}
dict_d = dict()
print(f "dict_c = {dict_c} dict_c.type = {type(dict_c)}")
print(f "dict_d = {dict_d} dict_d.type = {type(dict_d)}")
  • 字典的key必须是唯一的,如果你指定了多个相同的key,后面的键值对会覆盖前面的。

dict_e = {"one": 1, "two": 2, "three": 3, "two": 4}
print(f "dict_e = {dict_e} dict_e.type = {type(dict_e)}")

字典常用操作

 

4e71c5cb5e0d5d7ed6249f9628befec5.png

  • 常用操作一览

序号操作
1len(d):返回字典d中的项数
2d[key]:返回d中以key为键的项。如果映射中不存在key则会引发KeyError
3d[key] = value:将d[key]设为value,如果key已经存在,则是修改value,如果key没有存在,则是增加key-value
4del d[key]:将d[key]从d中移除。如果映射中不存在key,则会引发KeyError
5pop(key[, default]):如果key存在于字典中则将其移除并返回其值,否则返回default。如果default未给出且key不存在于字典中,则会引发KeyError
6keys():返回字典所有的key
7key in d:如果d中存在键key则返回True,否则返回False
8clear():移除字典中的所有元素
dict_a = {"one": 1, "two": 2, "three": 3}

# len(d):返回字典 d 中的项数
print(f "dict_a 元素个数:{len(dict_a)}")

# d [key]:返回 d 中以 key 为键的项。如果映射中不存在 key 则会引发 KeyError
print(f "key 为 three 对应的 value:{dict_a ["three"]}")
# print(f "key 为 four 对应的 value:{dict_a ["four"]}")  # KeyError: 'four'

# d [key] = value:将 d [key] 设为 value,如果 key 已经存在,则是修改 value,如果 key 没有存在,则是增加 key-value
dict_a ["one"] = "第一"
print(f "dict_a: {dict_a}")
dict_a ["four"] = 4
print(f "dict_a: {dict_a}")

# del d [key]:将 d [key] 从 d 中移除。如果映射中不存在 key,则会引发 KeyError
del dict_a ["four"]
print(f "dict_a: {dict_a}")
# del dict_a ["five"]  # KeyError: 'five'

# pop(key [, default]):如果 key 存在于字典中则将其移除并返回其值,否则返回 default。如果 default 未给出且 key 不存在于字典中,则会引发 KeyError
val = dict_a.pop("one")
print(f "val: {val}")
print(f "dict_a: {dict_a}")
# val2 = dict_a.pop("four")  # KeyError: 'four'
val = dict_a.pop("four", "哈哈")
print(f "val: {val}")
print(f "dict_a: {dict_a}")

# keys():返回字典所有的 key
dict_a_keys = dict_a.keys()
print(f "dict_a_keys: {dict_a_keys} type: {type(dict_a_keys)}")
for k in dict_a_keys:
    print(f "k: {k}")

# key in d:如果 d 中存在键 key 则返回 True,否则返回 False
print("two" in dict_a)

# clear():移除字典中的所有元素
dict_a.clear()
print(f "dict_a: {dict_a}")

字典生成式

  • 内置函数zip()

    • zip()可以将可迭代的对象作为参数,将对象中对应的元素打包成一个元组,返回由这些元组组成的列表。

  • 字典生成式基本语法

{字典 key 的表达式: 字典 value 的表达式 for 表示 key 的变量, 表示 value 的变量 in zip(可迭代对象, 可迭代对象)}
books = ["红楼梦", "三国演义", "西游记", "水浒传"]
author = ["曹雪芹", "罗贯中", "吴承恩", "施耐庵"]
dict_book = {book: author for book, author in zip(books, author)}
print(dict_book)

str1 = "hsp"
dict_str1 = {k: v * 2 for k, v in zip(str1, str1)}
print(dict_str1)

english_list = ["red", "black", "yellow", 'white']
chinese_list = ["红色", "黑色", "黄色", '白色']
dict_color = {chinese: english.upper() for chinese, english in zip(chinese_list, english_list)}
print(dict_color)

练习

# 一个公司有多个员工,请使用合适的数据类型保存员工信息(员工号、年龄、姓名、入职时间、薪水)
clerks = {
    "0001": {
        "age": 20,
        "name": "贾宝玉",
        "entry_time": "2011-11-11",
        "salary": 12000
    },
    "0002": {
        "age": 21,
        "name": "薛宝钗",
        "entry_time": "2015-12-12",
        "salary": 10000
    },
    "0010": {
        "age": 18,
        "name": "林黛玉",
        "entry_time": "2018-10-10",
        "salary": 20000
    }
}

print(f "员工号为 0010 的信息为:姓名-{clerks ['0010']['name']} "
      f "年龄-{clerks ['0010']['age']} "
      f "入职时间-{clerks ['0010']['entry_time']} "
      f "薪水-{clerks ['0010']['salary']}")

# 增加
clerks ['0020'] = {
    "age": 30,
    "name": "老韩",
    "entry_time": "2020-08-10",
    "salary": 6000
}
print("clerks:", clerks)

# 删除
del clerks ['0001']
print("clerks:", clerks)

# 修改
clerks ['0020']['name'] = '韩顺平'
clerks ['0020']['entry_time'] = '1999-10-10'
clerks ['0020']['salary'] += clerks ['0020']['salary'] * 0.1
print("clerks:", clerks)

# 遍历
keys = clerks.keys()
for key in keys:
    clerks [key]['salary'] += clerks [key]['salary'] * 0.2
print("clerks:", clerks)

# 格式化输出
for key in keys:
    print(f "员工号为{key}的员工信息如下 年龄:{clerks [key]['age']}"
          f "名字:{clerks [key]['name']}"
          f "入职时间:{clerks [key]['entry_time']}"
          f "薪水:{clerks [key]['salary']}")
print("-" * 60)
for key in keys:
    clerk_info = clerks [key]
    print(f "员工号为{key}的员工信息如下 年龄:{clerks [key]['age']}"
          f "名字:{clerk_info ['name']}"
          f "入职时间:{clerk_info ['entry_time']}"
          f "薪水:{clerk_info ['salary']}")

小结

数据容器特点比较

比较项列表(list)元组(tuple)字符串(str)集合(set)字典(dict)
是否支持多个元素YYYYY
元素类型任意任意只支持字符任意Key:通常是字符串或数字 Value:任意
是否支持元素重复YYYNKey 不能重复 Value 可以重复
是否有序YYYN3.6 版本之前是无序的 3.6 版本之后开始支持元素有序
是否支持所有YYYNN
可修改性/可变性YNNYY
使用场景可修改、可重复的多个数据不可修改、可重复的多个数据字符串不可重复的多个数据通过关键字查询对应数据的需求
定义符号[]()""''{}{key: value}

数据容器操作小结

  • 通用序列操作。

大多数序列类型,包括可变类型和不可变类型都支持下表的操作。

运算结果
x in s如果 s 中的某项等于 x 则结果为 True,否则为 False
x not in s如果 s 中的某项等于 x 则结果为 False,否则为 True
s + ts 与 t 相拼接
s*nn*s相当于 s 与自身进行 n 次拼接
s [i]s 的第 i 项,起始为 0
s [i: j]s 从 i 到 j 的切片
s [i: j: k]s 从 i 到 j 步长为 k 的切片
len(s)s 的长度
min(s)s 的最小项
max(s)s 的最大项
s.index(x [, i[, j]])x 在 s 中首次出现的索引号(索引号在 i 或其后且在 j 之前)
s.count(x)x 在 s 中出现的总次数
  • 通用转换操作一览。

序号操作
1list([iterable]):iterable 可以是序列、支持迭代的容器或其他可迭代对象, 将指定的容器转成列表
2str(容器):将制定的容器转为字符串
3tuple([iterable]):iterable 可以是序列、支持迭代的容器或其他可迭代对象, 将指定的容器转成元组
4set([iterable]):iterable 可以是序列、支持迭代的容器或其他可迭代对象, 将指定的容器转成集合
str_a = "hello"
list_a = ["jack", "tom", "mary"]
tuple_a = ("hsp", "tim")
set_a = {"red", "green", "blue"}
dict_a = {"0001": "小倩", "0002": "黑山老妖"}
# list([iterable]):iterable可以是序列、支持迭代的容器或其他可迭代对象,<br />将指定的容器转成列表
print(list(str_a))
print(list(tuple_a))
print(list(set_a))
print(list(dict_a))
print("-" * 60)

# str(容器):将制定的容器转为字符串
print(str(list_a))
print(str(tuple_a))
print(str(set_a))
print(str(dict_a))
print("-" * 60)

# tuple([iterable]):iterable 可以是序列、支持迭代的容器或其他可迭代对象,<br />将指定的容器转成元组
print(tuple(str_a))
print(tuple(list_a))
print(tuple(set_a))
print(tuple(dict_a))
print("-" * 60)

# set([iterable]):iterable 可以是序列、支持迭代的容器或其他可迭代对象,<br />将指定的容器转成集合
print(set(str_a))
print(set(list_a))
print(set(tuple_a))
print(set(dict_a))

排序和查找

排序的介绍

  • 排序是将多个数据,按照指定的顺序进行排列的过程。

  • 排序的分类

    • 冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序,基数排序。

冒泡排序法

基本介绍

  • 冒泡排序(Bubble Sorting)的基本思想是:重复地走访需要排序的元素列表,依次比较两个相邻的元素,如果排序(如从大到小或从小到大)错误就交换它们的位置。重复地进行直到没有相邻的元素需要交换,则元素列表排序完成。

  • 在冒泡排序中,值最大(或最小)的元素会通过交换慢慢“浮”到元素列表的“顶端”。就像“冒泡”一样,所以被称为冒泡排序。

冒泡排序法案例

  • 列表[24, 69, 80, 57, 13] 有5个元素,使用冒泡排序法使其排成一个从小到大的有序列表。

 

7b900da0ddda2f98e0617912cce0001c.png

# 使用 sort 方法完成排序
num_list = [24, 69, 80, 57, 13]
print("排序前".center(32, "="))
print(f "num_list: {num_list}")

num_list.sort()
print("排序后".center(32, "="))
print(f "num_list: {num_list}")
# 定义函数-完成排序
def bubble_sort(my_list):
    "" "
    功能;对传入的列表排序-顺序是从小到大
    : param my_list: 传入的列表
    : return: 无
    "" "
    # i 变量控制多少轮排序
    for i in range(1, len(my_list)):
        # j 变量控制比较的次数,同时可以作为比较元素的索引下标
        for j in range(len(my_list) - i):
            # 如果前面的数 > 后面的数,就交换
            if my_list [j] > my_list [j + 1]:
                my_list [j], my_list [j + 1] = my_list [j + 1], my_list [j]
        # print(f "第{i}轮排序后的结果 my_list: {my_list}")
    print(f "排序后的结果 my_list: {my_list}")


num_list = [24, 69, 80, 57, 13]
bubble_sort(num_list)
num_list = [24, 69, 80, 57, 13, 0, 900]
bubble_sort(num_list)

查找

基本介绍

  • 在Python中,我们应该掌握两种常见的查找方法:顺序查找、二分查找。

  • 其它查找方法:插值查找、斐波那契查找、树表查找、分块查找、哈希查找。

顺序查找

  • 列表["白眉鹰王", "金毛狮王", "紫衫龙王", "青翼蝠王"],猜名字游戏:从键盘中任意输入一个名称,判断列表中是否包含此名称【顺序查找】,要求找到了,提示找到,并给出下标值。

names_list = ["白眉鹰王", "金毛狮王", "紫衫龙王", "青翼蝠王"]
find_name = "金毛狮王"

# 使用 list.index 完成查找
res_index = names_list.index(find_name)
print("res_index:", res_index)
def seq_search(my_list, find_val):
    "" "
    功能:顺序查找指定的元素
    : param my_list: 传入的列表(即要查找的列表)
    : param find_val: 要查找的值/元素
    : return: 如果查找到则返回对应的索引下标,否则返回-1
    "" "
    find_index = -1
    for i in range(len(my_list)):
        if my_list [i] == find_val:
            print(f "恭喜,找到对应的值{find_val} 下标是{i}")
            break
    else:
        print(f "没有找到对应的值{find_val}")
    return find_index


res_index = seq_search(names_list, find_name)
print("res_index:", res_index)
  • 如果列表中有多个要查找的元素/值,比如前面的列表有两个金毛狮王,怎样才能把满足查询条件的元素的下标都返回。

# 编写查找函数,把满足查询条件的元素的下标都返回
def seq_search2(my_list, find_val):
    find_index = []
    for i in range(len(my_list)):
        if my_list [i] == find_val:
            # 将找到的下标添加到 find_index
            find_index.append(i)
    return find_index


names_list = ["白眉鹰王", "金毛狮王", "紫衫龙王", "金毛狮王", "青翼蝠王"]
find_name = "金毛狮王"
res_index = seq_search2(names_list, find_name)
print("res_index:", res_index)

二分查找

  • 请对一个列表[1, 8, 10, 89, 1000, 1234](元素是从小到大排序的)进行二分查找,输入一个数看看该列表是否存在此数,并且求出下标,如果没有就返回-1。

 

fcab156c20887d17e3d0c0fbad03402c.png

num_list = [1, 8, 10, 89, 1000, 1234]


def binary_search(my_list, find_val):
    "" "
    功能:完成二分查找
    : param my_list: 要查找的列表(该列表是由大小顺序)
    : param find_val: 要查找的元素/值
    : return:  如果找到返回对应的下标,若果没有找到,返回-1
    "" "
    left_index = 0
    right_index = len(my_list) - 1
    find_index = -1
    while left_index <= right_index:
        mid_index = (left_index + right_index) // 2
        # 如果 mid_index > find_val,则到如果 mid_index 左边查找
        if my_list [mid_index] > find_val:
            right_index = mid_index - 1
        # 如果 mid_index < find_val,则到如果 mid_index 右边查找
        elif my_list [mid_index] < find_val:
            left_index = mid_index + 1
        else:
            find_index = mid_index
            break
    return find_index


res_index = binary_search(num_list, 1)
if res_index == -1:
    print("没有找到该数")
else:
    print(f "找到数,对应的下标{res_index}")
  • 注意事项和细节

    • 二分查找的前提是该列表已经是一个排好序的列表(从小到大或从大到小)。

    • 排序的顺序是从小到大还是从大到小,会影响二分查找的代码逻辑。

本章作业

  • 随机生成10个整数(1-100的范围)保存到列表,使用冒泡排序,对其进行从大到小排序

import random

list_num = []
for _ in range(10):
    list_num.append(random.randint(1, 100))
print("list_num", list_num)


def bubble_sort(my_list):
    for i in range(1, len(my_list)):
        for j in range(len(my_list) - i):
            if my_list [j] < my_list [j + 1]:
                my_list [j], my_list [j + 1] = my_list [j + 1], my_list [j]


bubble_sort(list_num)
print("list_num", list_num)
  • 在第1题的基础上,使用二分查找,查找是否有8这个数,如果有,则返回对应的下标,如果没有,返回-1。

def binary_search(my_list, find_val):
    left_index = 0
    right_index = len(my_list) - 1
    find_index = -1
    while left_index <= right_index:
        mid_index = (left_index + right_index) // 2
        if my_list [mid_index] < find_val:
            right_index = mid_index - 1
        elif my_list [mid_index] > find_val:
            left_index = mid_index + 1
        else:
            find_index = mid_index
            break
    return find_index


res_index = binary_search(list_num, 8)
if res_index == -1:
    print("Not Found")
else:
    print(f "找到数,对应的下标{res_index}")

断点调试(Debug)

基本介绍

断点调试介绍

  • 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码即显示错误,停下进行分析从而找到这个Bug。

  • 断点调试是程序员必须掌握的技能。

  • 断点调试也能帮助我们进入到函数/方法内,学习别人是怎么实现功能的,提高程序员的编程水平。

断点调试的快捷键

 

7288f3347d73681debccf52d5a140389.png

  • F7:跳入方法/函数内。

  • F8:逐行执行代码。

  • Shift+F8:跳出方法/函数。

  • F9:直接执行到下一个断点。

断点调试应用案例

查看变量的变化情况

sum_num = 0
for i in range(10):
    sum_num += i
    print(f "i ={i}")
    print(f "sum_num ={sum_num}")

print("end...")

 

c9cc35e4ea025ccdbae04337d54592f5.gif

查看list越界的异常

names_list = ["jordan", "kobe", "james", "Messi"]
i = 0
# debug list 索引越界
while i <= len(names_list):
    print(f "names_list [{i}] = {names_list [i]}")
    i += 1

 

8b6646f027ee348c05cc8ac868b132f4.gif

演示进入到函数/方法内容

  • 将光标放在某个变量上,可以看到最新的数据。

def f2(num):
    res = 0
    for i in range(1, num + 1):
        res += i
    return res


def f1(name):
    print(f "name ={name} 1")
    print(f "name ={name} 2")
    print(f "name ={name} 3")
    r = f2(3)
    print(f "r ={r}")
    print(f "name ={name} 4")


f1("hsp")
print("end...")

 

d7bf48f2dd8051f4f53cf26ba100d180.gif

演示如何直接执行到下一个断点

  • 可以在debug过程中,动态地下断点。

 

3989254f8f938ffef4f82adab5617735.gif

模块和包

快速体验

# jack_module.py
def hi():
    print("hi jack")
# tom_module.py
def hi():
    print("hi tom")
# 导入模块(文件)
import jack_module
import tom_module

# 使用 jack-hi
jack_module.hi()

# 使用 tom-hi
tom_module.hi()

模块的基本介绍

模块(Module)是什么

  • 模块是一个py文件,后缀名.py。

  • 模块可以定义函数、类和变量,模块里也可能包含可执行的代码。

模块的作用有哪些

  • 当函数、类和变量很多时,可以很好的进行管理。

  • 开发中,程序员可以根据业务需要,把同一类型的功能代码,写到一个模块文件中,即方便管理,也方便调用。

  • 一个模块就是一个工具包,供程序员开发使用,提高开发效率。

  • Python自带标准模块库,每个模块可以帮助程序员快速实现相关功能。也可根据模块索引查找。

导入模块-import

基本语法

[from 模块名] import (函数 | 类 | 变量 | *) [as 别名]

# [] 是可选项,可以根据需求,选择合适的形式进入导入。

实例演示

  • 导入一个或者多个模块

import 模块
import 模块 1, 模块 2, ...

# 导入一个或多个模块,建议导入多个模块时,还是一行导入一个模块。
# 使用:模块.xx 方法来使用相关功能,.表示层级关系,即模块中的 xx。
# import 语句通常写在文件开头。
# import math, random

import math
import random

print(math.fabs(-11.2))
print(random.choice(['a', 'b', 'c']))
  • 导入模块的指定功能

from 模块 import 函数、类、变量...

# 导入模块指定功能。
# 使用:因为导入了具体函数、类、变量,直接使用即可,不需要再带模块名。
from math import fabs

print(fabs(-11.2))
  • 导入模块的全部功能

from 模块 import *

# 导入模块的全部功能
# 使用:直接使用,不需要带模块名。
from math import *

print(fabs(-234.5))
print(sqrt(9))
  • 给导入的模块或者功能取别名

import 模块 as 别名
from 模块 import 函数、类、变量... as 别名

# import 模块 as 别名:给导入的模块取别名,使用:别名.xx。
# from 模块 import 函数、类、变量... as 别名:给导入的某个功能取别名,使用时,直接用别名即可。
import random as r
from math import fabs as fa

print(r.choice([100, 200, 600]))
print(fa(-900.2))

自定义模块

基本介绍

  • 自定义模块:在实际开发中,Python提供的标准库模块不能满足开发需求,程序员需要一些个性化的模块,就可以进行自定义模块的实现。

注意事项和细节

  • 使用__name__可以避免模块中测试代码的执行。

    • __name__ 属性必须被设为模块的完整限定名称。 此名称被用来在导入系统中唯一地标识模块。

    • 当一个 Python 模块或包被导入时,__name__ 被设为模块的名称,若模块是在顶层代码环境中执行的,则其 __name__ 被设为字符串 '__main__'__main__顶层代码运行环境的名称。

# module1.py 文件
def hi():
    print("hsp hi")


def ok():
    print("hsp ok")


# 测试代码,当导入模块文件时,也会执行测试代码
# hi()

print(f "module1: {__name__}")

# 如果我们不希望导入模块时,去执行测试代码 hi(),可以使用 __name__
if __name__ == "__main__":
    hi()
# a.py 文件
# 导入 module1 的所有功能
from module1 import *

hi()
ok()

print(f "a.py: {__name__}")
  • 使用__all__可以控制import *时,哪些功能被导入,注意:import 模块 方式,不受__all__的限制。

    • 在module1.py中,没有__all__时,会导入所有的功能

    • 使用了__all__=['ok'] 在其它文件使用 from module1 import * 只会导入ok()

    • 注意:import 模块 方式,不受__all__的限制

# module1.py 文件
# 表示如果其它文件使用 from module1 import * 导入,则只能导入 ok()函数
__all__ = ["ok"]


def hi():
    print("hsp hi")


def ok():
    print("hsp ok")
# a.py 文件
# 导入 module1 的所有功能
from module1 import *

# hi()  # __all__ = ["ok"],设置了只允许 ok()函数被导入
ok()
# a.py 文件
# import 模块方式,不受 `__all__` 的限制
import module1

module1.hi()
module1.ok()

基本介绍

  • 从结果上看,包就是一个文件夹,在该文件夹下包含了一个__init__.py,该文件可以用于包含多个模块文件,从逻辑上看,包可以视为模块集合。

  • 包的结构示意图

 

09c1d5651616a02db87a4ee8bbad1ecb.png

  • __init__.py:控制包的导入操作。

  • 导入包和使用的基本语法

    • 导入:import 包名.模块

    • 使用:包名.模块.功能

快速入门

def hi():
    print("module01 hi()...")
def ok():
    print("module02 ok()...")
# 导入包下的模块
import package.module01
import package.module02

# 使用导入的模块
package.module01.hi()
package.module02.ok()

注意实现和使用细节

  • 导入包基本语法

    • import 包名.模块

    • from 包名 import 模块

      • 这种方式导入,在使用时,模块.功能,不用带包名。

from package import module01

module01.hi()
  • 导入包的模块的指定函数、类、变量

    • from 包名.模块 import 函数、类、变量

      • 在使用时,直接调用功能。

      • from 包名.模块 import *:表示导入包的模块的所有功能。

# 导入包的模块的指定函数、类、变量
from package.module01 import hi

# 直接使用功能名调用即可
hi()
# from 包名.模块 import *:表示导入包的模块的所有功能。
from package.module01 import *

hi()
hello()
  • __init__.py 通过__all__控制允许导入的模块

    • __init__.py中增加__all__==[允许导入的模块列表]

    • 针对from 包 import * 方式生效,对import xx 方式不生效。

# __init__.py 文件
# 控制包允许导入的模块
__all__ = ['module02']
# __init__.py 通过 __all__ 控制允许导入的模块
from package import *

module02.ok()

# 引入限制了 module01 模块导入,因此不能使用
# module01.hi()
# __all__ = ['module02'] 不能限制下面的导入方式
import package.module01
import package.module02

package.module01.hi()
package.module02.ok()
  • 包可以有多个层级

    • 包下还可以再创建包。

    • 在使用时,通过.来确定层级关系。

# 使用方式 1
import package.package2.module03

package.package2.module03.cal(10, 20)

# 使用方式 2
from package.package2.module03 import cal

cal(10, 20)

# 使用方式 3
from package.package2 import module03

module03.cal(10, 20)
  • 快捷导入

    • alt + enter:提示你选择解决方案。

    • alt + shift + enter:直接导入。

第三方库

基本介绍

  • 第三方库/包:就是非python官方提供的库,python语言有很多的第三方库,覆盖几乎所有领域,比如:网络爬虫、自动化、数据分析和可视化、web开发、机器学习等。

  • 使用第三方库,可以大大提高开发效率。

  • 常见的第三方库:网络爬虫、自动化、数据分析及可视化、web开发、机器学习等。

pip安装

  • 基本使用

    • pip介绍:pip 是python的包管理器,是一个工具,允许你安装和管理第三方库和依赖。

    • pip使用方式

      • 语法:pip install 库名/包名

      • 注意:pip是通过网络安装的,需要网络是连通的

  • 快速入门

# 使用 pip 安装 requests
pip list
pip install requests
  • 指定pip源

    • python pip常用源地址

      • 默认的pip源:https://pypy.python.org/simple,因为在国外,有时候速度比较慢。

      • 国内常用源:

        • 豆瓣:https://pypi.douban.com/simple

        • 清华:https://pypi.tuna.tsinghua.edu.cn/simple

        • 阿里云:https://mirrors.aliyun.com/pypi/simple

        • 中科大:https://pypi.mirrors.edu.cn/simple

      • 指定源,安装第三方库:pip install -i 指定源 库名/包名

# 使用 pip 指定源安装 requests
pip uninstall requests
pip install -i https://mirrors.aliyun.com/pypi/simple requests

面向对象编程(基础)

类与对象

类与实例的关系

  • 类与实例的关系示意图

 

9e6e1dea8123491847ff3dcb06edc42b.png

 

d3cdb5e049ad8105ab3050a3e0e499f9.png

快速入门—面向对象的方式解决养猫问题

# 定义一个猫类,age, name, color 是属性,或者称为成员变量
# Cat 类 就是你自己定义的一个新类型

# 定义 Cat 类
class Cat:
    # age, name, color 是属性
    age = None  # 年龄属性
    name = None  # 名字属性
    color = None  # 颜色属性


# 通过 Cat 类,创建实例(实例对象/对象)
cat1 = Cat()

# 通过对象名.属性名 可以给各个属性赋值
cat1.name = "小白"
cat1.age = 2
cat1.color = "白色"

# 通过对象名.属性名,可以访问到属性
print(f "cat1 的信息为:name: {cat1.name} age {cat1.age} color {cat1.color}")

# 小结:通过上面的 oop 解决问题,可以更好地管理小猫的属性

类和对象的区别和联系

  • 类是抽象的,概念的,代表一类事物,比如人类,猫类,...,即它是数据类型。

  • 对象是具体的,实际的,代表一个具体事物,即是实例。

  • 类是对象的模板,对象是类的一个个体,对应一个实例。

对象在内存中的存在形式

  • 内存分析图

 

258f9017269982e486b892af37223f52.png

属性/成员变量

  • 基本介绍

    • 类中定义的属性(变量),我们成为:成员变量。

    • 属性是类的一个组成部分,一般是字符串、数值,也可以是其它类型(list、dict等),比如前面定义Cat类的name、age就是属性。

  • 注意事项和细节说明

    • 属性的定义语法同变量,实例:属性名 = 值,如果没有值,可以赋值None。

      • None 是python的内置常量,通常被用来代表空值的对象。

    • 如果给属性指定的有值,那么创建的对象属性就有值。

# 定义 Cat 类
class Cat:
    # age, name, color 是属性
    age = 2  # 年龄属性
    name = "小白"  # 名字属性
    color = "白色"  # 颜色属性


# 创建对象
cat1 = Cat()

print(f "age:{cat1.age}, name:{cat1.name}, color:{cat1.color}")

类的定义和使用

  • 如何定义类

class 类名:
	属性...
    行为...
    
# class 是关键字,表示后面定义的是类。
# 属性:定义在类中的变量(成员变量)
# 行为:定义在类中的函数(成员方法)
  • 如何创建对象

对象名 = 类名()

# 举例
cat = Cat()
  • 如何访问属性

对象名.属性名

# 举例
cat.name

对象的传递机制

class Person:
    age = None
    name = None


p1 = Person()
p1.age = 10
p1.name = "小明"

p2 = p1
print(p2.age)
print(f "id(p1.name): {id(p1.name)}, id(p2.name): {id(p2.name)}")

 

86423e5b4755cffe5c9235bd7cbed83b.png

a = Person()
a.age = 10
a.name = "jack"

b = a
print(b.name)
b.age = 200
b = None
print(a.age)
print(b.age)  # AttributeError: 'NoneType' object has no attribute 'age'

 

23e7814830096595e552eba614f11d77.png

 

对象的布尔值

  • Python一切皆为西,所有对象都有一个布尔值,可以通过内置函数bool()可以获取对象的布尔值。

  • 下面对象的布尔值为False

    • False、数值0、None、空字符串、空列表、空字典、空元组、空集合。

print("---下面对象的布尔值为 False---")
print(bool(False))
print(bool(0))
print(bool(None))
print(bool(""))
print(bool([]))
print(bool(()))
print(bool({}))
print(bool(set()))
# 因为所有对象都有一个布尔值,所有有些代码直接使用对象的布尔值做判断
content = "hello"
if content:
    print(f "hi {content}")
else:
    print("空字符串")


lst = [1, 2]
if lst:
    print(f "lst {lst}")
else:
    print("空列表")

成员方法

基本介绍

  • 类出来有一些属性外,还会有一些行为,比如人类有年龄、姓名等属性,我们人类还有一些行为,比如:可以说话、跑步、...,通过学习,还可以做算术题。这时就要用成员方法才能完成。

class 类名:
    属性...
    行为...
    
# 类中定义的行为(函数),我们成为:成员方法/方法

成员方法的定义和使用

  • 成员方法的定义

    • 在类中定义成员方法和前面学习过的定义函数,基本是一样的(原理和运行机制是一样的),但是还是有点不同(形式上有不同)。

def 方法名(self, 形参列表):
    方法体

# 在方法定义的参数列表中,有一个 self
# self 是定义成员方法时,需要写上的
# self 表示当前对象本身
# 当我们通过对象调用方法时,self 会隐式的传入
# 在方法内部,需要使用 self,才能访问到成员变量
  • 案例演示

class Person:
    age = None
    name = None

    # 成员方法
    def hi(self):
        print("hi, python")

    def cal01(self):
        result = 0
        for i in range(1, 1001):
            result += i
        print(f "result: {result}")

    def cal02(self, n):
        result = 0
        for i in range(1, n + 1):
            result += i
        print(f "result: {result}")

    def get_sum(self, n1, n2):
        return n1 + n2


# 测试
p = Person()

p.hi()
p.cal01()
p.cal02(10)
print(p.get_sum(10, 20))

使用细节

  • python也支持对象动态的添加方法

# 函数
def hi():
    print("hi, python")


# 定义类
class Person:
    age = None
    name = None

    def ok(self):
        pass


# 创建对象 p、p2
p = Person()
p2 = Person()

# 动态地给 p 对象添加方法 m1,注意:只是针对 p 对象添加方法
# m1 是你新增加的方法的名称,由程序员指定名称
# 即 m1 方法和函数 hi 关联起来,当调用 m1 方法时,会执行 hi 函数
p.m1 = hi

# 调用 m1(即 hi)
p.m1()

print(type(p.m1), type(hi))  # <class 'function'> <class 'function'>
print(type(p.ok))  # <class 'method'>


# 因为没有动态的给 p2 添加方法,会报错
p2.m1()  # AttributeError: 'Person' object has no attribute 'm1'

self

访问对象的属性/成员变量

class Cat:
    name = "波斯猫"
    age = 2

    def info(self, name):
        print(f "name: {name}")  # 加菲猫
        # 通过 self.属性名 可以访问对象的属性/成员变量
        print(f "属性 name:{self.name}")  # 波斯猫


cat = Cat()
cat.info("加菲猫")

基本介绍

  • 基本语法

def 方法名(self, 形参列表):
    方法体

# 在方法定义的参数列表中,有一个 self
# self 是定义方法时,需要写上的,如果不写,则需要使用 @staticmethod 标注,否则会报错
# 将方法转换为静态方法:https://docs.python.org/zh-cn/3/library/functions.html#staticmethod
class Dog:
    name = "藏獒"
    age = 2

    def info(self, name):
        print(f "name: {name}")

    # @staticmethod 可以将普通方法转换为静态方法。
    # 如果是一个静态方法,可以不带 self
    # 静态方法的调用形式有两种:通过对象调用、通过类名调用
    @staticmethod
    def ok():
        print("ok()...")


dog = Dog()
dog.info("德牧")

# 通过对象调用
dog.ok()
# 通过类名调用
Dog.ok()
  • self 表示当前对象本身,简单的说,哪个对象调用,self 就代表哪个对象。

class Dog:
    name = "藏獒"
    age = 2

    def hi(self):
        print(f "hi self: {id(self)}")


dog2 = Dog()
print(f "dog2: {id(dog2)}")
dog2.hi()

dog3 = Dog()
print(f "dog3: {id(dog3)}")
dog3.hi()
  • 当我们通过对象调用方法时,self 会隐式的传入。

  • 在方法内部,要访问成员变量和成员方法,需要使用 self。

# 在方法内部,要访问成员变量和成员方法,需要使用 self。
class Dog:
    name = "藏獒"
    age = 2

    def eat(self):
        print(f "{self.name} 饿了...")

    def cry(self, name):
        print(f "{name} is crying")
        print(f "{self.name} is crying")
        self.eat()
        # 不能直接调用
        # eat()


dog = Dog()
# 修改 dog 对象的属性 name
dog.name = "中华田园犬"
dog.cry("金毛")
  • 练习题

class Person:
    name = None
    age = None

    def compare_to(self, other):
        # 名字和年龄都一样,就返回 True,否则返回 False
        return self.name == other.name and self.age == other.age


p1 = Person()
p1.name = "jack"
p1.age = 3
p2 = Person()
p2.name = "jack"
p2.age = 3

print(p1.compare_to(p2))

对象作为参数传递

  • 对象的传参机制

class Person:
    name = None
    age = None


# 分析对象作为参数传递到函数/方法的机制
def f1(person):
    print(f "②person 的地址:{id(person)}")
    person.name = "james"
    person.age += 1


# 创建对象 p1
p1 = Person()

p1.name = "jordan"
p1.age = 21

print(f "①p1 的地址:{id(p1)} p1.name: {p1.name} p1.age: {p1.age}")
f1(p1)

print(f "③p1 的地址:{id(p1)} p1.name: {p1.name} p1.age: {p1.age}")
  • 示意图

 

019b1cd4c36c86cd0e47228c386e744b.png

作用域

  • 在面向对象编程中,主要的变量就是成员变量(属性)和局部变量。

class Cat:
    # 属性(成员变量)
    name = None
    age = None
    
    # n1, n2, result 就是局部变量
    def cal(self, n1, n2):
        result = n1 + n2
        print(f "result = {result}")
  • 我们说的局部变量,一般指在成员方法中定义的变量。

  • 作用域的分类:属性作用域为整个类,比如 Cat类:cry eat 等方法使用属性。

class Cat:
    # 属性
    name = None
    age = None
    
    # n1, n2, result 就是局部变量
    def cal(self, n1, n2):
        result = n1 + n2
        print(f "cal() 使用属性 name {self.name}")
    
    def cry(self):
        print(f "cry() 使用属性 name {self.name}")
    
    def eat(self):
        print(f "eat() 使用属性 name {self.name}")

        
cat = Cat()
cat.cal(10, 20)
cat.cry()
cat.eat()
  • 局部变量:也就是方法中定义的变量,作用域是它在方法中。

  • 属性和局部变量可以重名,访问时带上 self,表示访问的属性,没有带 self,则是访问局部变量。

class Cat:
    # 属性
    name = None
    age = None
    
	def hi(self):
        name = "皮皮"
        print(f "name is {name}")
        print(f "self.name is {self.name}")


cat = Cat()
cat.name = "小咪"
cat.hi()

构造方法

基本介绍

def __init__(self, 参数列表):
    代码...
  • 在初始化对象时,会自动执行__init__方法

  • 在初始化对象时,传入的参数,自动传递给__init__方法。

  • 构造方法是python预定义的,名称是__init__,注意init前后都有两个_

# 在初始化对象时,会自动执行 __init__ 方法
class Person:
    name = None
    age = None

    # 构造方法/构造器
    # 构造方法是完成对象的初始化任务
    def __init__(self, name, age):
        print(f "__init__ 执行了 {name} {age}")
        # 把接收到的 name 和 age 赋给属性(name, age)
        # self 就是你当前创建的对象
        print(f "self id: {id(self)}")
        self.name = name
        self.age = age


# 创建对象
p1 = Person("kobe", 20)
print(f "p1 id: {id(p1)}")
print(f "p1 的信息: {p1.name} {p1.age}")

p2 = Person("tim", 30)
print(f "p2 id: {id(p2)}")
print(f "p2 的信息: {p2.name} {p2.age}")

注意事项和使用细节

  • 一个类只有一个__init__方法,即使你写了多个,也只有最后一个生效。

class Person:
    name = None
    age = None

    def __init__(self, name, age):
        print(f "__init__ 执行了... 得到了{name} {age}")
        self.name = name
        self.age = age

    def __init__(self, name):
        print(f " __init__ 执行了~~ 得到了{name}")
        self.name = name


# TypeError: Person.__init__() takes 2 positional arguments but 3 were given
# p1 = Person("tim", 20)

# 后面的 __init__()生效
p1 = Person("tim")
print(f "p1 的 name ={p1.name} age ={p1.age}")
# 为了代码简洁,我们也可以通过 __init__ 动态的生成对象属性
# python 可以动态的生成对象属性。
class Person:
    def __init__(self, name, age):
        print(f "__init__ 执行了... 得到了{name} {age}")
        # 将接收到的 name 和 age 赋给当前对象的 name 和 age 属性
        # python 支持动态生成对象属性
        self.name = name
        self.age = age


p1 = Person("tim", 30)
print(f "p1 的 name ={p1.name} age ={p1.age}")
  • 构造方法不能有返回值。

class Person:
    def __init__(self, name, age):
        print(f "__init__ 执行了... 得到了{name} {age}")
        self.name = name
        self.age = age
        return "hello"  # TypeError: __init__() should return None, not 'str'

练习

1、编写A01,定义方法max_lst,实现求某个float 列表list = [1.1, 2.9, -1.9, 67.9]的最大值,并返回。

# 1、编写 A01,定义方法 max_lst,实现求某个 float 列表 list = [1.1, 2.9, -1.9, 67.9] 的最大值,并返回。
"" "
    思路分析
    1. 类名:A01
    2. 方法:max_lst(self, lst), 功能:返回列表的最大值
"" "


class A01:
    def max_lst(self, lst):
        return max(lst)


# 测试
a = A01()
print("最大值:", a.max_lst([1.1, 2.9, -1.9, 67.9]))

2、编写类Book,定义方法update_price,实现更改某本书的价格,具体:如果价格>150,则更改为150,如果价格>100,更改为100,否则不变。

# 2、编写类 Book,定义方法 update_price,实现更改某本书的价格,具体:如果价格 > 150,则更改为 150,如果价格 > 100,更改为 100,否则不变。
"" "
    思路分析:
    类名:Book
    属性:name, price
    构造器:__init__(self, name, price)
    方法:update_price, 功能:如果价格 > 150,则更改为 150,如果价格 > 100,更改为 100,否则不变。
"" "


class Book:

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def update_price(self):
        # 如果价格 > 150,则更改为 150,如果价格 > 100,更改为 100,否则不变。
        if self.price > 150:
            self.price = 150
        elif self.price > 100:
            self.price = 100

    def info(self):
        # 输出书籍的信息
        print(f "书的信息:{self.name} {self.price}")


book = Book("天龙八部", 99)
book.info()
book.update_price()
book.info()

3、定义一个圆类Circle,定义属性:半径,提供显示圆周长功能的方法,提供显示圆面积的方法。

# 定义一个圆类 Circle,定义属性:半径,提供显示圆周长功能的方法,提供显示圆面积的方法。

"" "
    思路分析:
    类名:Circle
    属性:radius
    构造器:__init_(self, radius)
    方法:len(self) 显示圆周长
    方法:area(self) 显示圆面积
"" "
import math


class Circle:
    def __init__(self, radius):
        self.radius = radius

    def len(self):
        len = 2 * math.pi * self.radius
        print("周长:", round(len, 2))

    def area(self):
        area = math.pi * (self.radius ** 2)
        print("面积:", round(area, 2))


# 测试
circle = Circle(5)
circle.len()
circle.area()

4、编程创建一个Cal计算类,在其中定义2个成员变量表示两个操作数,定义四个方法实现求和、差、乘、商(要求除数为0的话,要提示)并创建对象,分别测试。

# 编程创建一个 Cal 计算类,在其中定义 2 个成员变量表示两个操作数,定义四个方法实现求和、差、乘、商(要求除数为 0 的话,要提示)并创建对象,分别测试
"" "
    思路分析:
    类名:Cal
    属性:num1, num2
    构造器/构造方法:__init__(self, num1, num2)
    定义四个方法实现求和 def sum(), 求差 def minus(), 求积 def mul(), 求商 def div()
    商(要求除数为 0 的话,要提示)
"" "


class Cal:
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2

    def sum(self):
        return self.num1 + self.num2

    def minus(self):
        return self.num1 - self.num2

    def mul(self):
        return self.num1 * self.num2

    def div(self):
        if self.num2 == 0:
            print("num2 不能为 0")
        else:
            return self.num1 / self.num2


cal = Cal(1, 0)
print("和 =", cal.sum())
print("差 =", cal.minus())
print("积 =", cal.mul())
print("商 =", cal.div())

5、定义Music类,里面有音乐名name,音乐时长times属性,并有播放play功能,和返回本身属性信息的方法get_info。

# 定义 Music 类,里面有音乐名 name,音乐时长 times 属性,并有播放 play 功能,和返回本身属性信息的方法 get_info
"" "
    思路分析:
    类名:Music
    属性:name, times
    构造器:__init__(self, name, times)
    方法:play, get_info
"" "


class Music:
    def __init__(self, name, times):
        self.name = name
        self.times = times

    def play(self):
        print(f "音乐名 {self.name} 正在播放中... 时长为 {self.times}")

    def get_info(self):
        return f "音乐的信息: name:{self.name} times:{self.times}"


# 测试
music = Music("月光曲", 300)
music.play()
print(music.get_info())

6、分析下列代码输出结果。

class Demo:
    i = 100

    def m(self):
        self.i += 1
        j = self.i
        print("i =", self.i)
        print("j =", j)


d1 = Demo()
d2 = d1
d2.m()

print(d1.i)
print(d2.i)

"" "
输出结果:
i = 101
j = 101
101
101
"" "

7、石头剪刀布游戏,0表示石头,1表示剪刀,2表示布。

import random


class Tom:
    def __init__(self):
        self.wins = 0
        self.losses = 0
        self.choices = ['石头', '剪刀', '布']

    def play(self):
        user_choice = int(input("请输入你的选择(0 = 石头,1 = 剪刀,2 = 布):"))
        if user_choice not in [0, 1, 2]:
            print("输入错误,请输入 0、1 或 2。")
            return

        computer_choice = random.randint(0, 2)
        print(f "Tom 的选择是:{self.choices [user_choice]},电脑的选择是:{self.choices [computer_choice]}")

        if user_choice == computer_choice:
            print("平局!")
        elif (user_choice == 0 and computer_choice == 1) or \
                (user_choice == 1 and computer_choice == 2) or \
                (user_choice == 2 and computer_choice == 0):
            print("Tom 赢了!")
            self.wins += 1
        else:
            print("Tom 输了!")
            self.losses += 1

    def show_scores(self):
        print(f "Tom 的赢的次数:{self.wins},输的次数:{self.losses}")


# 使用示例
tom = Tom()
tom.play()
tom.play()
tom.show_scores()

面向对象编程(进阶)

面向对象编程三大特征

  • 面向对象编程有三大特征:封装、继承、多态。

面向对象编程—封装

封装介绍

  • 封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部。

  • 程序只有通过被授权的操作,才能对数据进行访问。

  • 封装经典案例:电视机的操作就是典型封装。

封装的理解和好处

  • 隐藏实现细节:方法(绘制柱状图)<——调用(传入参数...)。

  • 可以对数据进行验证(比如:age: 1~120、password: 长度要求),保证安全合理。

  • 可以保护数据隐私(比如salary),要求授权才可以访问。

私有成员

  • 公共的变量和方法介绍。

    • 默认情况下,类中的变量和方法都是公有的,它们的名称前都没有下划线。

    • 公共的变量和方法,在类的外部、类的内部,都可以正常访问。

  • 如何将属性/方法进行私有化。

    • 类中的变量或方法以双下划线__开头命名,则该变量或方法为私有的,私有的变量或方法,只能在本类内部使用,类的外部无法使用。

  • 如何访问私有的属性/方法:提供公共的方法,用于对私有成员的操作。

快速入门

# 创建职员类(Clerk),属性:name, job, salary
# 1、不能随便查看职员 Clerk 的职位和工资等隐私,比如职员 ("tiger", "python 工程师", 20000)
# 2、提供公共方法,可以对职员和工资进行操作

class Clerk:
    # 公共属性
    name = None
    # 私有属性
    __job = None
    __salary = None

    # 构造方法
    def __init__(self, name, job, salary):
        self.name = name
        self.__job = job
        self.__salary = salary

    # 提供公共的方法,对私有属性操作(根据实际的业务编写即可)
    def set_job(self, job):
        self.__job = job

    def get_job(self):
        return self.__job

    # 私有方法
    def __hi(self):
        print("hi()")

    # 提供公共方法,操作私有方法
    def f1(self):
        self.__hi()


clerk = Clerk("tiger", "python 工程师", 20000)
print(clerk.name)

# print(clerk.__job)  # AttributeError: 'Clerk' object has no attribute '__ job'
print(clerk.get_job())
clerk.set_job("Java 工程师")
print(clerk.get_job())

# 私有方法不能在类的外部直接调用
# clerk.hi()  # AttributeError: 'Clerk' object has no attribute 'hi'

# 通过公共方法,调用私有方法
clerk.f1()

注意事项和细节

  • python语言的动态特性,会出现伪私有属性的情况

class Clerk:
    # 公共属性
    name = None
    # 私有属性
    __job = None
    __salary = None

    # 构造方法
    def __init__(self, name, job, salary):
        self.name = name
        self.__job = job
        self.__salary = salary

    # 提供公共的方法,对私有属性操作
    def get_job(self):
        return self.__job


clerk = Clerk("apple", "python 工程师", 20000)

# 如果这样使用,因为 python 语言的动态特性,会动态的创建属性 __job,但是这个属性和我们在类中定义的私有属性 __ job 不是同一个变量,我们在类中定义的__job 私有属性完整的名字是 _Clerk__job,而并不是
# 可以通过 debug 的“Evaluate Expression”来观察。
clerk.__job = "Go 工程师"
print(f " job = {clerk.__job}")  # job = Go 工程师

# 获取真正的私有属性__job
print(f "{clerk.get_job()}")
  • 练习

# Account 类要求具有属性:姓名(长度为 2-4 位)、余额(必须 > 20)、密码(必须是六位),如果不满足,则给出提示
# 通过 set_xx 的方法给 Account 的属性赋值
# 编写 query_info() 接收姓名和密码,如果姓名和密码正确,返回该账号信息

"" "
    思路分析:
    类名:Account
    私有属性:姓名(长度为 2-4 位)、余额(必须 > 20)、密码(必须是六位)
    构造器:无
    方法:set_xx(self, 属性名) 进行赋值,并且对各个接收到的数据进行校验
    方法:query_info(self, name, pwd) 而且需要验证,才返回响应信息
"" "


class Account:
    __name = None
    __balance = None
    __pwd = None

    def set_name(self, name):
        if 2 <= len(name) <= 4:
            self.__name = name
        else:
            print("名字的长度不在 2-4 位之间")

    def set_balance(self, balance):
        if balance > 20:
            self.__balance = balance
        else:
            print("余额(必须 > 20)")

    def set_pwd(self, pwd):
        if len(pwd) == 6:
            self.__pwd = pwd
        else:
            print("密码(必须是六位)")

    def query_info(self, name, pwd):
        if name == self.__name and pwd == self.__pwd:
            return f "账户信息:{self.__name} {self.__ balance}"
        else:
            return "请输入正确的名字和密码"


# 测试
account = Account()
account.set_name("tim")
account.set_pwd("000000")
account.set_balance(100)
print(account.query_info("tim", "000000"))

面向对象编程—继承

基本介绍

  • 继承基本介绍

    • 基础可以解决代码复用,让我们的编程更加靠近人类思维。

    • 当多个类存在相同的属性(成员变量)和方法时,可以从这些类中抽象出方法,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法。

  • 示意图

 

f66323ad33a8c94d56707c35eda71974.png

  • 基本语法

class DerivedClassName(BaseClassName):
    <statement-1>
    ...
    <statement-N>
    
# 派生类就会自动拥有基类定义的属性和方法
# 基类习惯上也叫父类
# 派生类习惯上也叫子类

快速入门

# 编写父类
class Student:
    name = None
    age = None
    __score = None

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def show_info(self):
        print(f " name ={self.name}, age ={self.age}, score ={self.__score}")

    def set_score(self, score):
        self.__score = score


# 小学生类,继承 Student
class Pupil(Student):
    def testing(self):
        print("小学生在考小学数学...")


# 大学生类,继承 Student
class Graduate(Student):
    def testing(self):
        print("大学生在考高等数学...")


# 测试
student1 = Pupil("apple", 10)
student1.testing()
student1.set_score(70)
student1.show_info()

student2 = Graduate("grape", 22)
student2.testing()
student2.set_score(80)
student2.show_info()
  • 继承给编程带来的便利

    • 代码的复用性提高了。

    • 代码的扩展性和维护性提高了。

继承的注意事项和细节

  • 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法在子类不能在子类直接访问,要通过父类提供公共的方法去访问。

# 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法在子类不能在子类直接访问,要通过父类提供公共的方法去访问。
class Base:
    # 公共属性
    n1 = 100
    # 私有属性
    __n2 = 200

    def __init__(self):
        print("Base 构造方法")

    def hi(self):
        print("hi() 公共方法")

    def __hello(self):
        print("__hello() 私有方法 ")

    # 提供公共方法,访问私有属性和私有方法
    def test(self):
        print("属性:", self.n1, self.__n2)
        self.__hello()


class Sub(Base):

    def __init__(self):
        print("Sub 构造方法")

    def say_ok(self):
        print("say_ok() ", self.n1)
        self.hi()

        # print(self.__n2)  # AttributeError: 'Sub' object has no attribute '_Sub__n2'
        # self.__hello()  # AttributeError: 'Sub' object has no attribute '_Sub__n2'


# 测试
sub = Sub()
sub.say_ok()

# 调用子类继承父类的公共方法,去实现访问父类的私有成员的效果
sub.test()
  • Python语言中,object是所有其它类的基类。Ctrl + H可以查看一个类的继承关系。

  • Python支持多继承。

class A:
    n1 = 100

    def sing(self):
        print("A sing()...", self.n1)


class B:
    n2 = 200

    def dance(self):
        print("B dance()...", self.n2)


class C(A, B):
    # Python pass 是空语句,是为了保持程序结构的完整;pass 不做任何事情,一般用作站位语句。
    pass


c = C()
# 继承的属性信息
print(f "属性信息:{c.n1} {c.n2}")
# 调用继承的方法
c.dance()
c.sing()
  • 在多重继承中,如果有同名的成员,遵守从左到右的继承优先级(即:写在左边的父类优先级高,写在右边的父类优先级低)。

class A:
    n1 = 100

    def sing(self):
        print("A sing()...", self.n1)


class B:
    n2 = 200

    def dance(self):
        print("B dance()...", self.n2)

    def sing(self):
        print("B sing()...", self.n2)


class C(A, B):
    # Python pass 是空语句,是为了保持程序结构的完整;pass 不做任何事情,一般用作站位语句。
    pass


c = C()
# 继承的属性信息
print(f "属性信息:{c.n1} {c.n2}")
# 调用继承的方法
c.dance()
c.sing()  # A sing()... 100

继承的练习题

class GrandPa:
    name = "大头爷爷"
    hobby = "旅游"


class Father(GrandPa):
    name = "大头爸爸"
    age = 39


class Son(Father):
    name = "大头儿子"


son = Son()
print(f "son.name is {son.name} and son.age is {son.age} and son.hobby is {son.hobby}")
class Computer:
    cpu = None
    memory = None
    disk = None

    def __init__(self, cpu, memory, disk):
        self.cpu = cpu
        self.memory = memory
        self.disk = disk

    def get_details(self):
        return f "CPU: {self.cpu}, Memory: {self.memory}, Disk: {self.disk}"


class PC(Computer):
    brand = None

    def __init__(self, cpu, memory, disk, brand):
        # 初始化子类的属性——方式 1
        # self.cpu = cpu
        # self.memory = memory
        # self.disk = disk
        # self.brand = brand

        # 初始化子类的属性——方式 2
        # 通过 super().xx 方式可以去调用父类方法,这里通过 super().__init__(cpu, memory, disk, brand) 去调用父类的构造器
        super().__init__(cpu, memory, disk)
        self.brand = brand

    def print_info(self):
        print(f "品牌:{self.brand}\t{self.get_details()}")


pc = PC("inter", 32, 1000, "戴尔")
pc.print_info()

调用父类成员

基本介绍&实例

  • 基本介绍

    • 如果子类和父类出现同名的成员,可以通过父类名super() 访问父类的成员。

  • 基本语法

    • 访问父类成员方式1

      • 访问成员变量:父类名.成员变量

      • 访问成员方法:父类名.成员方法(self)

    • 访问父类成员方式2

      • 访问成员变量:super().成员变量

      • 访问成员方法:super().成员方法()

  • 案例演示

class A:
    n1 = 100

    def run(self):
        print("A-run()...")


class B(A):
    n1 = 200

    def run(self):
        print("B-run()...")

    # 通过父类名去访问父类的成员
    def say(self):
        print(f "父类的 n1 ={A.n1} 本类的 n1 ={self.n1}")
        # 调用父类的 run
        A.run(self)

        # 调用本类的 run()
        self.run()

    # 通过 super()方式去访问父类对象
    def hi(self):
        print(f "父类的 n1 ={super().n1}")


b = B()
b.say()
b.hi()

注意事项和使用细节

  • 子类不能直接访问父类的私有成员

class A:
    n1 = 100
    __n2 = 600

    def run(self):
        print("A-run()...")

    def __jump(self):
        print("A-jump()...")


class B(A):
    # 子类不能直接访问父类的私有成员
    def say(self):
        # print(A.__n2)  # AttributeError: type object 'A' has no attribute '_B_ _n2'. Did you mean: '_A__n2'?
        # print(super().__n2)  # AttributeError: 'super' object has no attribute '_B__n2'

        # A.__jump(self)
        # super().jump()
        print("B-say()...")


b = B()
b.say()
  • 访问不限于直接父类,而是建立从子类向上级父类的查找关系 A->B->C...

class Base:
    n3 = 800

    def fly(self):
        print("Base-fly()...")


class A(Base):
    n1 = 100
    __n2 = 600

    def run(self):
        print("A-run()...")

    def __jump(self):
        print("A-jump()...")


class B(A):
    # 访问不限于直接父类,而是建立从子类向上级父类的查找关系 B-> A-> Base...
    def say(self):
        print(Base.n3, A.n3, super().n3)

        Base.fly(self)
        A.fly(self)
        super().fly()
        self.fly()


b = B()
b.say()
  • 建议使用super()的方式,因为如果使用父类名方式,一旦父类变化,类名统一需要修改,比较麻烦。

练习题

  • 分析下面的代码,看看输出什么内容?

class A:
    n1 = 300
    n2 = 500
    n3 = 600

    def fly(self):
        print("A-fly()...")


class B(A):
    n1 = 200
    n2 = 400

    def fly(self):
        print("B-fly()...")


class C(B):
    n1 = 100

    def fly(self):
        print("C-fly()...")

    def say(self):
        print(self.n1)  # 100
        print(self.n2)  # 400
        print(self.n3)  # 600
        print(super().n1)  # 200
        print(B.n1)  # 200
        print(C.n1)  # 100


c = C()
c.say()
  • 针对上面的程序,想在C的say()中,调用C的fly()和A的fly(),应该如何调用?

self.fly()  # C-fly()...
A.fly(self)  # A-fly()...

方法重写

基本介绍&实例

  • 基本介绍

    • 重写又称覆盖(override),即子类继承父类的属性和方法后,根据业务需要,再重新定义同名的属性和方法。

  • 案例演示

 

c2995a3cf2b33fba1fd1d0f9aee6852c.png

练习题

class Person:
    name = None
    age = None

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        return f "名字:{self.name} 年龄:{self.age}"


class Student(Person):
    id = None
    score = None

    def __init__(self, id, score, name, age):
        # 调用父类的构造器完成继承父类的属性和属性的初始化
        super().__init__(name, age)
        # 子类的特有的属性,我们自己完成初始化
        self.id = id
        self.score = score

    def say(self):
        return f "id:{self.id} score:{self.score} {super().say()}"


person = Person("tom", 12)
print(person.say())

student = Student("tom", 14, "tom", 18)
print(student.say())

类型注解-type hint

基本介绍

  • 为什么需要类型注解

    • 随着项目越来越大,代码也就会越来越多,在这种情况下,如通过没有类型注解,很容易不记得某一个方法的参数类型是什么。

    • 一旦传入了错误类型的参数,python是解释性语言,只有运行时候才能发现问题,这对大项目来说是一个巨大的灾难。

# 对字符串进行遍历
# a: str 给形参 a 进行类型注解,标注 a 的类型是 str。
def fun1(a: str):
    for ele in a:
        print(ele)


# Ctrl + P 提示参数时,没有类型提示
# 如果类型传错了,就会出现异常
fun1(100)  # TypeError: 'int' object is not iterable
  • 类型注解作用和说明

    • 从Python3.5开始,引入了类型注解机制,作用和说明如下:

      • 类型提示,防止运行时出现参数类型、返回值类型、变量类型不符合。

      • 作为开发文档附加说明,方便使用者调用时传入和返回类型参数。

      • 加入后并不会影响程序的运行,不会报正式的错误,只有提示。

      • Pycharm支持类型注解,参数类型错误会黄色提示。

变量的类型注释

  • 基本语法:变量: 类型

  • 基础数据类型注解

n1: int = 10  # 对n1进行类型注解,标注n1的类型为int;如果给出的类型与标注的类型类型不一致,则Pycharm会给出黄色警告
n2: float = 10.1
is_pass: bool = True
name: str = "Tom"
  • 实例对象类型注解

class Cat:
    pass


cat: Cat = Cat()  # 对cat进行类型注解,标注cat类型是Cat
  • 容器类型注解

my_list: list = [1, 2, 3]  # 对my_list进行类型注解,标注my_list类型为list。
my_tuple: tuple = ("run", "sing", "fly")
my_set: set = {1, 2, 3}
my_dict: dict = {"name": "Tom", "age": 22}
  • 容器详细类型注解

my_list1: list[int] = [1, 2, 3]  # 对my_list1进行类型注解,标注my_list1类型是list,而且该list元素是int。
# 元组类型设置详细类型注解,需要把每个元素类型都标注一下
my_tuple1: tuple[str, str, str, float] = ("run", "sing", "fly", 1.1)
my_set1: set[int] = {1, 2, 3}
# 字典类型设置详细类型注解,需要设置两个类型,即[key类型, value类型]
# 对my_dict1进行类型注解,标注my_dict1类型是dict,而且key的类型是str,values的类型是int。
my_dict1: dict[str, int] = {"score": 100, "score2": 80}
  • 在注释中使用注解

    • 基本语法:# type: 类型

# # type: float 用于标注变量n3的类型是float。
n3 = 89.9  # type: float
my_list3 = [100, 200, 300]  # type: list[int]
email = "[email protected]"  # type: str

函数(方法)的类型注解

  • 基本语法

def 函数/方法名(形参名: 类型, 形参名: 类型 ...) -> 返回值类型:
	函数/方法体
  • 实例演示

# 对字符串进行遍历
# name: str 对形参name进行类型注解:标注name类型是str;在调用方法/函数时,传入的实参类型不是一样的,则给出黄色的警告。
def fun1(name: str):
    for ele in name:
        print(ele)


fun1("hsp")


# 接收两个整数,返回整数
# a: int, b: int 对形参a、b进行类型注解,标注a、b的类型为int; -> int 对返回值进行类型注解,标注返回值的类型为int。
def fun2(a: int, b: int) -> int:
    return a + b


print(f"结果是:{fun2(1, 2)}")

类型注解是提示性的,并不是强制性的,如果你给的类型和指定/标注的类型不一致,Pycharm检测到会给出黄色警告,但是仍然可以运行。

Union类型

 

d69486c24a5a951f1e046d131c2fc10c.png

  • 基本介绍

    • Union类型可以定义联合类型注解。

    • 在变量、函数(方法)都可以使用Union联合类型注解。

    • 使用的时候,需要先导入Union:from typing import Union

  • 基本语法

Union[类型, 类型...]
比如:联合类型:Union[X, Y] 等价于 X|Y,意味着满足X或Y之一。
  • 实例演示

from typing import Union

# 联合类型注解, a可以是int或str
a: Union[int, str] = 100

# my_list是str类型,元素可以是int或str
my_list: list[Union[int, str]] = [1, 2, 3, "tom"]


# 函数/方法使用联合函数注解
# 接收两个数(可以是int/float),返回数(int/float)
def cal(num1: Union[int, float], num2: Union[int, float]) -> Union[int, float]:
    return num1 + num2


print(cal(1, 2.1))

面向对象编程—多态

问题引入

  1. Master类中有一个feed(喂食)方法,可以完成主人给动物喂食物的信息。

 

733fb18ba52acded219c7bbe3087a599.png

# 使用传统的方式
class Food:
    name = None
​
    def __init__(self, name):
        self.name = name
​
​
class Fish(Food):
    pass
​
​
class Bone(Food):
    pass
​
​
class Animal:
    name = None
​
    def __init__(self, name):
        self.name = name
​
​
class Dog(Animal):
    pass
​
​
class Cat(Animal):
    pass
​
​
class Master:
    name = None
​
    def __init__(self, name):
        self.name = name
​
    # 给猫喂鱼
    def feed_cat(self, cat: Cat, fish: Fish):
        print(f"主任{self.name} 给动物{cat.name} 喂{fish.name}")
​
    # 给狗喂骨头
    def feed_dog(self, dog: Dog, bone: Bone):
        print(f"主人{self.name} 给动物{dog.name} 喂{bone.name}")
​
​
master = Master("老韩")
cat = Cat("小花猫")
fish = Fish("黄花鱼")
dog = Dog("大黄狗")
bone = Bone("大棒骨")
​
master.feed_cat(cat, fish)
master.feed_dog(dog, bone)
  1. 传统方法带来的问题:代码的复用性不高,而且不利于代码维护和功能扩展。解决方法是使用多态。

持续更新中ing,关注公众号`软工星球`,获取最新更新状态!


欢迎关注我的博客,如有疑问或建议,请随时留言讨论。

 

 

标签:__,name,Python,self,list,2024,print,def,顺平
From: https://blog.csdn.net/qingxuly/article/details/143198558

相关文章

  • wrf-python安装教程
    记录一下wrf-python安装过程安装过程中出现了许多报错,这里记录了成功安装的完整流程。1.创建新环境#创建一个新环境推荐使用3.11版本condacreate-nWRFpython=3.11condaactivateWRF2.搭建Fortran环境1.安装visualstudio(自行前往官网下载)2.安装OneAPI(Base......
  • Python数据分析结合OpenAI:智能数据摘要和洞察
    Python数据分析结合OpenAI:智能数据摘要和洞察Python数据分析结合OpenAI:智能数据摘要和洞察在数据分析领域,随着数据集规模的日益庞大,如何从复杂的数据集中提取有价值的信息成为关键挑战。借助OpenAI的自然语言处理(NLP)能力,分析师可以自动生成摘要、报告,并获得有洞察力的结论......
  • 华为OD机试真题-比赛-2024年OD统一考试(E卷)
    最新华为OD机试考点合集:华为OD机试2024年真题题库(E卷+D卷+C卷)_华为od机试题库-CSDN博客     每一题都含有详细的解题思路和代码注释,精编c++、JAVA、Python三种语言解法。帮助每一位考生轻松、高效刷题。订阅后永久可看,发现新题及时跟新。题目描述一个有N个选手参加比......
  • 2024年spring6下载
    1.进入官网https://spring.io/2.projects选3.点击github图标4.往下滑到readme,点击 SpringFrameworkArtifactsAccesstoBinaries下的SpringFrameworkArtifacts 5.往下滑到SpringRepositories,点击https://repo.spring.io 6.点击Artifacts7.在搜索栏搜索libs-miles......
  • Origin 2024 中文版 下载及安装教程
    安装包下载Origin2024中文版安装包点击下载安装和使用教程:1.通过上方链接下载软件后,选中下载的【Origin2024】压缩包,右击选择解压到【Origin2024】。 2.进入解压后的文件夹,双击打开【Setup】文件夹。 3.右键Setup.exe文件,选择以管理员身份运行。 4.点击“下一步”。 5.选择......
  • IDEA 2024.2.2 最新安装教程(附激活-2099年~)
    访问IDEA官网下载IDEA2024.2.2版本的安装包。下载补丁https://pan.quark.cn/s/fcc23ab8cadf检查进入IDEA中后,点击菜单Help|Register,即可查看IDEA的激活到期时间:免责声明:本文中的资源均来自互联网,仅供个人学习和交流使用,严禁用于商业行为,下载后请在24小......
  • 2024.10.23
      今天有些小忙。  中午和陈处去吃了一直久闻大名的铁锅炖,遗憾地发现其实也不过如此。吃饭时路上偶然谈到冬旭,算起来他已经走了1年半有余,而离我收到讣告几乎刚好一年。中间相差的半年,陈处一直不告诉我,这一点我至今不太能理解。可能那时候他真的,一想起此事就控制不住自......
  • 【2024-10-23】生活主干道
    20:00梧叶新黄柿叶红,更兼乌桕与丹枫。只言山色秋萧索,绣出西湖三四峰。                                                 ——《秋山秋雨蚤作有叹》 宋·杨万里昨天下......
  • ChatGPT国内中文版镜像网站整理合集(2024/10/24)
    ​一、GPT中文镜像站① yixiaai.com 支持4o以及o1,支持MJ绘画② chat.lify.vip 支持通用全模型,支持文件读取、插件、绘画、AIPPT③ AIChat 支持GPT3.5/4,4o以及MJ绘画1.什么是镜像站镜像站(MirrorSite)是指通过复制原始网站内容和结构,创建的备用网站。其主要目的是在原......
  • 【高届数,往届EI检索】第十届能源资源与环境工程研究进展国际学术会议(ICAESEE 2024)
    第十届能源资源与环境工程研究进展国际学术会议(ICAESEE2024)定于2024年12月20-22日在湖南长沙举办。会议主要围绕能源资源与环境工程等研究领域展开讨论。会议旨在为从事能源材料与电力电气研究的专家学者、工程技术人员、技术研发人员提供一个共享科研成果和前沿技术,了解学术......