韩顺平老师Python学习笔记【持续更新...】

1、Python语言概述

1.1、转义字符

1.1.1、常用的转义字符

常用转义字符列表
\t一个制表位,实现对齐的功能
\n换行符
\\一个\
\'一个'
\"一个"
\r一个回车

1.2、注释

1.2.1、单行注释

# + 其他

1.2.2、多行注释

三个单引号 ''' + xxx + '''   或   三个双引号 """ + xxx + """

1.2.3、文件编码声明注释

# coding:编码
# coding:utf-8

## 用途:用于指定文件的编码格式(放在文件开头)

2、变量

2.1、变量基本原理

变量三要素 = 类型 + 名称 + 值

2.2、格式化输出

2.2.1、%操作

age = 80
score = 70.5
gender = 'male'
name = 'John'

print("info: %s %d %s %.1f" % (name, age, gender, score))

2.2.2、format()函数

print("info: {} {} {} {} ".format(name, age, gender, score))

2.2.3、f-strings

print(f"info: {name} {age} {score} {gender}")

2.3、加法运算

  • 左右两边都是数值型时,则做加法运算。
  • 左右两边都是字符串时,则做拼接运算。

2.4、数据类型

2.4.1、通过使用 type() 来查看数据的数据类型

type(object)

2.4.2、整数类型

2.4.2.1、进制

2.4.2.2、Python中的整型占多少个字节

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

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

1byte = 8 bit

  • 字节数随着数字的增大而增大(Python整型是变长的)。
  • 每次的增量是4个字节。

2.5、浮点类型

  • 其实也就是小数。
  • 科学计数法表示:
    # 5.12乘以10的2次方
    5.12e2
    # 5.12除以10的2次方
    5.12E-2
    
  • 浮点类型计算后,存在精度的损失,可以使用 Decimal 类进行精确计算。
    b = 8.1 / 3 # 2.7
    
    # 解决方法
    from decimal import Decimal
    b = Decimal("8.1") / Decimal("3")
    

2.6、布尔类型

2.6.1、知识点

  • 布尔类型也叫bool类型,取值为 True 和 False。
  • bool类型适用于逻辑运算,一般用于流程控制。
  • bool在运算中,True为1,False为0。
  • 在Python中,非0值被视为真值,0值被视为假值。

2.7、字符串类型

2.7.1、知识点

  • 使用引号(单双引号都可以)包括起来,创建字符串。
  • 在字符串前面加 'r' 可以使整个字符串不会被转义。

2.7.2、字符串的驻留机制

  • 相同值的变量指向同一个内存地址。

2.8、数据类型转换

2.8.1、隐式类型转换

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

2.8.2、显式类型转换

  • 借助一些函数来做转换。


3、运算符

3.1、运算符介绍

  • 算术运算符
  • 赋值运算符
  • 比较运算符
  • 逻辑运算符
  • 赋值运算符
  • 位运算符

3.2、算术运算符

3.2.1、知识点

3.3、比较运算符

3.3.1、知识点

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

3.4、逻辑运算符

3.4.1、知识点

假设:a=10,b=20

3.5、赋值运算符

3.5.1、知识点

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

3.6、三元运算符

3.6.1、知识点

  • 语法是:max = a if a > b else b

3.7、运算符的优先级

3.8、标识符的命名规则和命名规范

  • 凡是可以自己起名字的地方都叫标识符。

3.9、关键字

  • 被Python语言赋予了特殊含义,用作专门用途的字符串(单词)。这些关键字不要用做标识符了。

3.10、键盘输入

  • 需要接收用户输入的数据,可以使用键盘输入语句来获取。
  • 一般使用:input()函数。
  • 从控制台接收到的数据类型是 字符串类型。

4、进制(先跳过,后续补充!)


5、程序控制结构

三大流程控制语句:

  • 顺序控制。
  • 分支控制。
  • 循环控制。

5.1、顺序控制

5.1.1、知识点

  • 程序从上到下逐行地执行,中间没有任何判断和跳转。

5.2、分支控制if-else

5.2.1、单分支

if 条件表达式:
    代码块(可以有多条语句)

5.2.2、双分支

if 条件表达式:
    执行代码块1
else:
    执行代码块2

5.2.3、多分支

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

5.3、嵌套分支

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

5.4、for循环控制

5.4.1、知识点

for <变量> in <范围/序列>:
    <循环操作语句>

5.5、while循环控制

while 判断条件:
    循环操作语句

5.6、多重循环

思路如下:

  • 1、矩形:
    # i控制层数
    for i in range(1, 6):
        # j控制每层输出的*的个数
        for j in range(1, 6):
            # 这里的end=''表示输出不换行
            print("*", end='')
        # 表示每层输出后换行
        print('')
  • 直角三角形:
    # i控制层数
    for i in range(1, 6):
        # j控制每层输出的*的个数
        for j in range(i):
            # 这里的end=''表示输出不换行
            print("*", end='')
        # 表示每层输出后换行
        print('')
  • 金字塔:
    # i控制层数
    for i in range(1, 6):
        for k in range(5-i):
            print(' ', end='')
        # j控制每层输出的*的个数
        for j in range(2*i-1):
            # 这里的end=''表示输出不换行
            print("*", end='')
        # 表示每层输出后换行
        print('')
  • 空心金字塔:
    # i控制层数
    for i in range(1, 6):
        for k in range(5-i):
            print(' ', end='')
        # j控制每层输出的*的个数
        for j in range(2*i-1):
            # 这里的end=''表示输出不换行
            if j == 0 or j == 2*i-1-1 or i==5:
                print("*", end='')
            else:
                print(' ', end='')
        # 表示每层输出后换行
        print('')

最终代码如下:

total_level = int(input())

# i控制层数
for i in range(1, total_level+1):
    for k in range(total_level-i):
        print(' ', end='')
    # j控制每层输出的*的个数
    for j in range(2*i-1):
        # 这里的end=''表示输出不换行
        if j == 0 or j == 2*i-1-1 or i==total_level:
            print("*", end='')
        else:
            print(' ', end='')
    # 表示每层输出后换行
    print('')

5.7、break语句

  • 终止循环用的语句。

5.8、continue语句

  • continue语句用于for或while循环所嵌套的代码中。
  • continue语句用于结束本次循环,继续执行循环的下一个轮次。

5.9、return语句

  • return使用在函数,表示跳出所在的函数。
  • 注意,break语句只是结束当前循环,而return语句直接跳出了这个函数。

6、函数

6.1、知识点

  • 普通传参:
    def xxx(args):
        xxx
  • 传入多个的位置参数:
    def xxx(*args):
        xxx

  • 传入关键字可变参数:
    def xxx(**args):
        xxx

6.2、函数的传参机制

6.3、函数的递归调用

  • 简单来讲,递归就是函数自己调用自己,每次调用时传入不同的值。
  • 递归有助于编程者解决复杂问题,同时可以让代码变得简洁。

6.3.1、递归练习1

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

test(4)

# 输出如下:
n = 2
n = 3
n = 4

6.3.2、递归练习2

# 例子:
def test(n):
    if n > 2:
        test(n - 1)
    else:
        print("n=", n)

test(4)

# 输出如下:
n = 2

6.3.3、递归练习3(阶乘问题)

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

print(factorial(4))

# 结果:

-----------
n = 4

return f(3)*4

n = 3

return f(2)*3

n = 2

return f(1)*2

n = 1

return 1
-----------

24

6.3.4、递归的重要规则

6.3.5、递归练习

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

def fbn(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fbn(n-1) + fbn(n-2)

print(fbn(3))
# 结果:
2

(2)猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,并再多吃一个。当到第十天时,想再吃时(即还没吃),发现只有一个桃子了。问:最初有几个桃子?

# day == 10, 有桃子数1
# day == 9,  day9的桃子数 - (day9的桃子数/2 + 1)= day10的桃子数

--> day == 9, 有桃子数:(day10桃子数+1)*2

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

print(peach(1))
# 结果
# 1534

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

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

print(f(10))
# 2047

6.4、函数作为参数传递

在编写函数中,参数可以传一个函数。

需注意:

  • 函数作为参数传递,传递的不是数据,而是业务处理逻辑。
  • 一个函数,可以接收多个函数作为参数传入。

6.5、lambda匿名函数(临时函数)

6.5.1、函数的定义

  • def 关键字,可以定义带有名称的函数,可以重复使用。
  • lambda关键字,可以定义匿名函数(无名称),匿名函数只能使用一次。
  • 匿名函数用于临时创建一个函数,只使用一次的场景。

6.5.2、匿名函数基本语法

  • lambda 形参列表函数体(一行代码)。
  • lambda关键字,表示定义匿名函数。
  • 形参列表:比如 num1, num2 表示接收两个参数。
  • 函数体:完成的功能,只能写一行,不能写多行代码。

6.5.3、应用实例

编写一个函数,可以接收一个匿名函数和两个数,通过匿名函数计算,返回两个数的最大值。

def f1(fun, num1, num2):
    '''
    功能:调用fun,返回num1和num2的最大值
    : fun: 接收函数(匿名的)
    :num1:
    : num2:
    : return:
    '''
    return fun(num1, num2)

max_val = f1(lambda a,b: a if a > b else b, 12, 10)
print(max_val)

# 12

6.6、全局变量和局部变量

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

注意:

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

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

(3)在函数内部使用global 关键字,可以标明指定使用全局变量。

7、数据容器

7.1、为什么需要数据容器

便于添加删减数据、便于代码的维护。

7.2、什么是数据容器

7.2.1、基本介绍

  • 数据容器是一种数据类型,有些地方也简称为容器。
  • 数据容器可以存放多个数据,每一个数据也被称为一个元素。
  • 存放的数据 / 元素可以是任意类型。
  • 简单地说,数据容器就是一种可以存放多个数据 / 元素的数据类型。

7.2.2、分类

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

7.3、列表 - List

7.3.1、基本介绍

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

7.3.2、列表的定义

7.3.3、列表的使用

7.3.4、列表的遍历

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

7.3.5、注意事项和使用细节

  • 如果我们需要一个空列表,可以通过 [] ,或者 list() 方式来定义。
  • 列表的元素可以有多个,而且数据类型没有限制,允许有重复元素,并且是有序的。
  • 列表的索引 / 下标是从0开始的。
  • 列表索引必须在指定范围内使用,否则会报错。
  • 索引也可以从尾部开始,最后一个元素的索引为 -1,往前一位为 -2,以此类推。
  • 通过 列表[索引]=新值 对数据进行更新,使用 列表.append(值) 方法来添加元素,使用 del 语句来删除列表的元素,注意不能超过有效索引范围。
  • 列表是可变序列,即:列表的元素是可以修改的,修改后,列表变量指向地址不变,只是数据内容变化。简单来讲,整个列表的地址没变,但是值变更后的list[index]的地址变了。
  • list1 = [1,2,3]
    list2 = list1
    list2[0] = 10
    
    print(list1)
    print(list2)
    
    # 结果
    [10,2,3]
    [10,2,3]

7.3.6、列表的常用操作

7.3.7、列表生成式

  • 列表生成式就是“生成列表的公式”。
  • 基本语法:

7.4、元素 - tuple

7.4.1、基本介绍

  • 元组(tuple)可以存放多个不同类型数据,元组是不可变序列(不能用append()insert()这样的方法,有获取某个索引值的方法,但不可以重新赋值)。
  • 元组也是一种数据类型。

7.4.2、元组的定义

7.4.3、元组的使用

7.4.4、元组的遍历

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

7.4.5、注意事项和使用细节

  • 如果我们需要一个空元组,可以通过 () ,或者 tuple() 方式来定义。
  • 元组的元素可以有多个,而且数据类型没有限制(甚至可以嵌套元组),允许有重复元素,并且是有序的。
  • 元组的 索引/下标 是从0开始的。
  • 元组索引必须在指定范围内使用,否则报错。
  • 元组是不可变序列
  • 可以修改元组内 list的内容(修改增加删除等)。
  • 索引也可以从尾部开始。
  • 定义只有一个元素的元组,需要带上逗号,否则就不是元组类型。
    tuple_h = (100,)

7.4.6、元组常用操作

7.5、字符串 - str

7.5.1、基本介绍

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

7.5.2、字符串支持索引

7.5.3、字符串的遍历

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

7.5.4、注意事项和使用细节

  • 字符串索引必须在指定范围内使用,否则报错。索引可以从前开始,也可以从后开始。
  • 字符串是不可变序列,不能修改。
  • 在Python中,字符串长度没有固定限制,取决于计算机内存大小。

7.5.5、字符串常用操作

7.5.6、字符串比较

7.6、切片 - slice

7.6.1、基本介绍

7.6.2、基本语法

7.6.3、注意事项和使用细节

  • 切片语法:序列[起始索引:结束索引:步长],起始索引如果不写,默认为0;结束索引如果不写,默认为截取到结尾;步长如果不写,默认为1。
  • 切片语法:序列[起始索引:结束索引:步长],步长为负数,表示反向取,同时注意起始索引和结束索引也要反向标记。
  • 切片操作并不会影响原序列,而是返回了一个序列。

7.7、集合 - set

7.7.1、基本介绍

7.7.2、集合的定义

7.7.3、注意事项和使用细节

  • 集合是由不重复元素组成的无序容器(自动去重)。
  • 集合不支持索引。
  • 既然集合不支持索引,所以对集合进行遍历不支持while,只支持for。
  • 创建空集合只能用 set() ,不能用 {},{} 创建的是空字典。

7.7.4、集合常用操作

7.7.5、集合生成式

答案:

韩韩、顺顺、平平(乱序)

7.8、字典 - dict

7.8.1、基本介绍

7.8.2、字典的定义

7.8.3、注意事项和使用细节

  • 字典的Key(关键字)通常是字符串或数字,Value可以是任意数据类型。
  • 字典不支持索引,会报 keyError。
  • 既然字典不支持索引,所以对字典遍历不支持 while,只支持 for,注意直接对字典进行遍历,遍历得到的是key。三种遍历方式如下:
  • 创建空字典可以通过 {} ,或者 dict()。
  • 字典的key必须是唯一的,如果你指定了多个相同的key,后面的键值对会覆盖前面的。

7.8.4、字典常用操作

7.8.5、字典生成式

7.9、数据容器 - 小结

7.10、list、tuple、set和dict的传参机制

8、排序和查找

8.1、排序的介绍

  • 排序是将多个数据,按照指定的顺序进行排列的过程。
  • 排序的分类:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序。

8.2、冒泡排序法

8.2.1、基本介绍

8.2.2、冒泡排序法范例

def bubble_sort(my_list)
    for i in range(0, len(my_list)):
        for j in range(0, 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]

8.3、查找

8.3.1、基本介绍

在Python中,我们应当掌握两种常见的查找方法:

  • 顺序查找;
  • 二分查找;
  • 插值查找;
  • 斐波那契查找;
  • 树表查找;
  • 分块查找;
  • 哈希查找

8.3.2、顺序查找

def seq_search(my_list, find_val):
    find_index = []
    for i in range(len(my_list)):
        if my_list[i] == find_val:
            find_index.append(i)
    return find_index

8.3.3、二分查找

思路分析:

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_value:
            right_index = mid_index - 1
        elif my_list[mid_index] < find_value:
            left_index = mid_index + 1
        else:
            find_index = mid_index
            break
    return find_index

9、断点调试(Debug)

9.1、基本介绍

10、模块和包

10.1、模块的基本介绍

模块(Module)是什么?

  • 模块是一个py文件,后缀名 .py。
  • 模块可以定义函数、类和变量,模块里也可能包含可执行的代码。

模块的作用有哪些?

  • 当函数、类和变量很多时,可以很好的进行管理。
  • 开发中,程序员可以根据业务需要,把同一类型的功能代码,写到一个模块文件中,即方便管理,也方便调用。
  • 一个模块就是一个工具包,供程序员开发使用,提高开发效率。
  • Python自带标准模块库。

10.2、导入模块 - import

10.2.1、基本语法

10.2.2、实例演示

(1)导入一个或多个模块

(2)导入模块的指定功能

(3)导入模块的全部功能

(4)给导入的模块或者功能取别名

10.3、自定义模块

10.3.1、基本说明

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

10.3.2、注意事项

  • 使用 __name__ 可以避免模块中测试代码的执行。
    def hi():
        print("1")
    
    if __name__ == "__main__":
        hi()
  • 使用 __all__ 可以控制 import* 时,哪些功能被导入,注意:import 模块 方式,不受 __all__的限制。
    __all__ = ['ok']

    表示如果其他文件使用的是 from xxx import *,则只能导入ok函数。如果你用的是 import xxx ,则限制不了。

10.4、包

10.4.1、应用场景

  • 一个实际的项目,可能需要很多的模块,当模块文件越来越多,如果我们将所有的模块文件都放在同一个文件夹,就会带来很多问题,不利于管理和调用。这个时候我们就会用 包。

10.4.2、基本介绍

10.4.3、注意事项和使用细节

(1)导入包基本语法

(2)导入包的模块的指定函数、类、变量

(3)__init__.py 通过 __all__ 控制允许导入的模块

(4)包可以有多个层级

有三种导入形式:

# 方式1:
import hsp_package.hsp_package2.module03

hsp_package.hsp_package2.module03.cal(10, 30)


# 方式2:
from hsp_package.hsp_package2.module03 import cal

cal(10, 30)


# 方式3:
from hsp_package.hsp_package2 import module03

module03.cal(10, 30)

(5)快捷键:alt + enter / shift + alt + enter 可以快捷的导入(PyCharm):

10.5、第三方库

10.5.1、基本介绍

10.5.2、基本使用

10.5.3、指定源

11、面向对象编程(基础部分)

11.1、类与对象

11.1.1、类与实例的关系

11.1.2、快速入门 - 面向对象的方式解决养猫问题

# feed_cat_oop.py

# 定义一个毛类:age、name、color是属性,或者称为成员变量;
# Cat类 就是你自己定义的一个新类型;

# 定义Cat类
class Cat:
    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方式解决问题,可以更好的管理小猫的属性。

11.1.3、类和对象的区别和联系

  • 是抽象的、概念的,代表一类事物,比如人类、猫类...,即 它是数据类型
  • 对象是具体的,实际的,代表一个具体事物,即 是实例
  • 类是对象的模板,对象是类的一个个体,对应一个实例。

11.1.4、对象在内存中存在形式

11.1.5、属性 / 成员变量

基本介绍:

  • 类中定义的属性(变量),我们也称为:成员变量。
  • 属性是类的一个组成部分,一般是字符串、数值,也可是其他类型(list、dict等),比如前面定义 Cat类 的 name、age 就是属性。

注意事项和细节:

(1)属性的定义语法同变量,实例:属性名 = 值,如果没有值,可以赋值 None。

(2)如果给属性指定的有值,那么创建的对象,属性就有值。

11.1.6、类的定义和使用

如何定义类:

如何创建对象:

如何访问属性:

练习:

a = Person()
a.age = 10
a.name = "jack"

b = a
print(b.name) # jack

b.age = 100
b = None

print(a.age) # 100
print(b.age) # 报错

11.2、对象的布尔值

11.3、成员方法

11.3.1、基本介绍

11.3.2、成员方法的定义和使用

成员方法的定义:

11.3.3、案例演示

class Person:
    name = None
    age = None
    
    def hi(self):
        print("hi!")
    
    def cal01(self):
        result1 = 0
        for i in range(1, 1001):
            result1 += i
        print(result1)
    
    def cal02(self, n):
        result2 = 0
        for i in range(1, n+1):
            result2 += i
        print(result2)
    
    def get_sum(self, a, b):
        result3 = a + b
        print(result3)



p = Person()
p.hi()
p.cal01()
p.cal02(10)
p.get_sum(1,3)

# hi!
# 500500
# 55
# 4

11.3.4、使用细节

  • Python也支持对象动态的添加方法。
def hi():
    print("hi!!!")

class Person:
    name = None
    age = None

p = Person()
p2 = Person()

# 动态的给p对象添加方法m1,注意,只是针对p对象添加方法
# m1是你新增加的方法的名称,由程序员指定名字
# 即m1方法和函数hi关联起来,当调用m1方法时,会执行hi函数
p.m1 = hi
p.m1()


# hi!!!

11.4、self

11.4.1、先看一段代码,并分析输出的信息是什么?

输出加菲猫。

问题分析:如果我们希望在成员方法内,访问对象的属性 / 成员变量,怎么办? >>> self

输出波斯猫。

11.4.2、二说self

  • 通过@staticmethod,可以将方法转为静态方法。
  • 如果是一个静态方法,可以不带self。
  • 静态方法的调用形式有变化。

3)self表示当前对象本身,简单地说,哪个对象调用,self就代表哪个对象。

4)当我们通过对象调用方法时,self会隐式的传入。

5)在方法内部,要访问成员变量和成员方法,需要使用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()

dog = Dog()
dog.cry("金毛")

# 金毛 is crying
# 藏獒 is crying
# 藏獒 饿了。。。


-----------------
dog = Dog()
dog.name = "狮子"
dog.cry("金毛")

# 金毛 is crying
# 狮子 is crying
# 狮子 饿了。。。

11.4.3、课堂练习题

class Person:
    name = None
    age = None

    def compare_to(self, other):
        return self.name == other.name and self.age == other.age

# 测试
p1 = Person()
p1.name = "tom"
p1.age = 18

p2 = Person()
p2.name = "tim"
p2.age = 3

print(p1.compare_to(p2))

11.5、对象作为参数传递

对象的传参机制:

1)这里我们讨论的对象,是通过我们自定义的类,创建的对象,比如 Cat类 -> cat对象。

2)示例如下:

地址不变,值改变。

11.6、作用域

面向对象中,变量的作用域是需要掌握的知识点。

1、在面向对象编程中,主要的变量就是成员变量(属性)和局部变量。

2、我们说的局部变量,一般是指在成员方法中定义的变量。

3、作用域的分类:属性作用域为整个类,比如Cat类:cry eat等方法使用属性。

4、局部变量:也就是方法中定义的变量,作用域是在它的方法中。

5、属性和局部变量可以重命名,访问时带上self,表示访问的属性,没有带self,则是访问局部变量。

11.7、构造方法

11.7.1、看一个需求

11.7.2、基本介绍

基本介绍:

- 构造方法(构造器)基本语法:
def __init__(self, 参数列表):
    代码...

(1)在初始化对象时,会自动执行 __init__ 方法。

class Person:
    # 构造方法
    def __init__(self):
        print("__init__ 执行了")

p1 = Person()
# __init__ 执行了

(2)在初始化对象时,将传入的参数,自动传递给 __init__ 方法。

class Person:
    name = None
    age = None
    
    # 构造方法
    # 构造方法是完成对象的初始化任务
    def __init__(self, name, age):
        self.name = name
        self.age = age

p1 = Person("james", 20)

(3)构造方法是py预定义的,名称是 __init__ ,注意 init 前后都有两个 _ 。

11.7.3、注意事项和使用细节

# 为了代码简介,我们也可以通过 __init__ 动态的生成对象属性

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)

11.8、本章作业

class A01:
    def max(self, lst):
        return max(lst)

lst = [-1.1, 2.9, -1.9, 67.9]

a = A01()
print(a.max(lst))

class Book:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    
    def update_price(self):
        if self.price > 150:
            self.price = 150
        elif 100 < self.price <= 150:
            self.price = 100
    
    def info(self):
        print(f"书的信息 {self.name} {self.price}")

book = Book("xiaohu", 1900)
book.info()
book.update_price()
book.info()

class Circle:
    def __init__(self, r):
        self.r = r
    
    def zhouchang(self):
        length = 2 * 3.14 * self.r
        print(f"周长是{length}")
    
    def mianji(self):
        area = 3.14 * self.r * self.r
        print(f"面积是{area}")

circle = Circle(10)
circle.zhouchang()
circle.mianji()

class Cal:
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
    
    def jia(self):
        print(f"这两个数的和是{self.num1 + self.num2}")

    def cha(self):
        print(f"这两个数的差是{self.num1 - self.num2}")

    def cheng(self):
        print(f"这两个数的积是{self.num1 * self.num2}")

    def chu(self):
        if self.num2 != 0:
            print(f"这两个数的商是{self.num1 / self.num2}")
        else:
            print("除数为0!!!")

cal = Cal(5, 9)
cal.jia()
cal.cha()
cal.cheng()
cal.chu()

12、面向对象编程(进阶部分)

12.1、面向对象编程三大特征

封装、继承和多态。

12.2、面向对象编程 - 封装

12.2.1、封装介绍

  • 封装(encapsulation)就是把抽象出的数据 [属性] 和对数据的操作 [方法] 封装在一起,数据被保护在内部。
  • 程序只有通过被授权的操作,才能对数据进行访问。

12.2.2、封装的理解和好处

  1. 隐藏实现细节:方法(绘制柱状图)<--- 调用(传入参数...)
  2. 可以对数据进行验证(比如age:1~120、password长度要求等),保证安全合理
  3. 可以保护数据隐私(比如salary),要求授权才可以访问

12.2.3、私有成员

12.2.4、如何将 属性/方法 进行私有化

类中的变量或方法以双下划线 __ 开头命名,则该变量或方法为私有的,私有的变量或方法,只能在本类内部使用,类的外部无法使用。

12.2.5、如何访问私有的属性/方法:提供公共的方法,用于对私有成员的操作

12.2.6、快速入门

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)

print(clerk.get_job())
clerk.set_job("Java工程师")
print(clerk.get_job())

# 私有方法不能再类的外部直接调用
# clerk.__hi()

# 通过公共方法,调用私有方法
clerk.f1()

12.2.7、注意事项和使用细节

  1. Python语言的动态特征,会出现 伪私有属性的情况

言外之意就是,不可以直接用clerk.__job = xxx来访问,而是要用公有方法来私有访问。

12.2.8、封装的练习题

class Account:
    __name = None
    __balance = None
    __pwd = None

    def set_name(self, name):
        # 长度为2-4位
        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("密码必须是6位!")
    
    def query_info(self, name, pwd):
        if name == self.__name  and pwd == self.__pwd:
            return f"账户信息 {self.__name} {self.__balance}"
        else:
            print("请输入正确的名字和密码!")

account1 = Account()
account1.set_pwd("123456")
account1.set_name("Alex")
account1.set_balance(10000)

account1.query_info("Alex", "123456")
print(account1.query_info("Alex", "123456"))

12.3、面向对象编程 - 继承

12.3.1、为什么需要继承

分析问题:

  • Pupil 和 Graduate 有很多相同的属性和方法。
  • 目前如果单独分开写,代码复用性差。
  • 也不利于管理。

12.3.2、继承基本介绍

12.3.2.1、继承基本介绍
  1. 继承可以解决代码复用,让我们的编程更加靠近人类思维。
  2. 当多个类存在相同的属性(成员变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法。
12.3.2.2、继承的示意图
12.3.2.3、继承的基本语法
class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>
  • 派生类就会自动拥有基类定义的属性和方法。
  • 基类习惯上也叫父类。
  • 派生类习惯上也叫子类。

12.3.3、快速入门

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


class Pupil(Student):
    def testing(self):
        print("..小学生正在考数学..")

class Graduate(Student):
    def testing(self):
        print("..大学生正在考高数..")


student1 = Pupil("Alex", 10)
student1.testing()
student1.set_score(80)
student1.show_info()

student2 = Graduate("Bob", 25)
student2.testing()
student2.set_score(90)
student2.show_info()

12.3.4、继承的深入讨论 / 细节问题

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有的属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。
  2. Python编程语言中,”object“是所有其它类的基类,通过ctrl+h,可以查看类的继承关系。
  3. Python支持多重继承。
  4. 在多重继承中,如果有同名的成员,遵守从左到右的继承优先原则(即:写左边的父类优先级高,写在右边的父类优先级低)。

12.3.5、继承的练习题

son.name=大头儿子 son.age=39 son.hobby=旅游

class Computer:
    def __init__(self, CPU, Memory, disk):
        self.CPU = CPU
        self.Memoery = Memory
        self.disk = disk
    
    def get_details(self):
        return f"CPU={self.CPU}, Memory={self.Memoery}, disk={self.disk}"
    

class PC(Computer):
    
    def __init__(self, CPU, Memory, disk, brand):
        # 通过 super().xx 方式可以去调用父类的方法
        super().__init__(CPU, Memory, disk)

        self.brand = brand
    
    def gen_info(self):
        # print(f"CPU={self.CPU}, Memory={self.Memoery}, disk={self.disk}, brand={self.brand}")
        print(f"{self.get_details()}, brand={self.brand}")

class NotePad(Computer):
    def __init__(self, CPU, Memory, disk, color):
        super().__init__(CPU, Memory, disk)
    
        self.color = color

    def gen_info(self):
        print(f"CPU={self.CPU}, Memory={self.Memoery}, disk={self.disk}, color={self.color}")

pc1 = PC("intel", "1024MiB", "2T", "Thinkpad")
pc1.gen_info()

notepad1 = NotePad("intel", "1024MiB", "2T", "black")
notepad1.gen_info()

12.4、调用父类成员

12.4.1、基本介绍 & 实例

12.4.1.1、基本介绍

如果子类和父类出现同名的成员,可以通过父类名、super()访问父类的成员

12.4.1.2、基本语法

1)访问父类成员方式1

2)访问父类成员方式2

12.4.2、注意事项和使用细节

  1. 子类不能直接访问父类的私有成员。
  2. 访问不限于直接父类,而是建立 从子类向上级父类的查找关系 A->B->C...
  3. 建议使用 super() 的方式,因为如果使用 父类名 方式,一旦父类变化,类名同意需要修改,比较麻烦。

12.5、重写

12.5.1、基本介绍 & 实例

12.5.1.1、基本介绍

重写又称覆盖(override),即子类继承父类的属性和方法后,根据业务需要,再 重新定义同名的属性或方法。

12.5.2、课堂练习题

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def say(self):
        return f"名字{self.name} 年龄{self.age}"

class Student(Person):
    def __init__(self, name, age, id, score):
        super().__init__(name, age)

        self.id = id
        self.score = score
    
    def say(self):
        return f"{super().say()} 编号{self.id} 分数{self.score}"

person1 = Person("Alex", 19)
student1 = Student("Mike", 25, 7, 100)

print(person1.say())
print(student1.say())

12.6、类型注解 - type hint

12.6.1、基本介绍

12.6.1.1、为什么需要类型注解:
  • 随着项目越来越大,代码也就会越来越多,在这种情况下,如果没有类型注解,很容易不记得某一个方法的参数类型是什么。
  • 一旦传入了错误类型的参数,Python是解释性语言,只有运行时候才能发现问题,这对大型项目来说是一个巨大的灾难。
12.6.1.2、类型注解作用和说明

def fun1(a:str):
    for ele in a:
        print(ele)

fun1("100")

12.6.2、变量的类型注解

12.6.2.1、基本语法

变量:类型

12.6.2.2、基础数据类型注解

12.6.2.3、实例对象类型注解

12.6.2.4、容器类型注解

12.6.2.5、容器详细类型注解

12.6.2.6、在注释中使用注解

12.6.3、函数(方法)的类型注解

12.6.3.1、基本语法

12.6.3.2、示例

12.6.3.3、说明

12.6.4、Union类型

12.6.4.1、基本语法

12.6.4.2、示例

使用Union,需要导入:from typng import Union

12.7、面向对象编程 - 多态

12.7.1、先看一个问题

12.7.1.1、使用传统方法来解决
# 先使用传统的方式完成

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("Jack")
cat = Cat("alex")
fish = Fish("黄花鱼")
dog = Dog("bob")
bone = Bone("鸡脆骨")

master.feed_dog(dog, bone)
master.feed_cat(cat, fish)

问题分析:如果 动物/食物 的种类很多,怎么办?

12.7.1.2、传统方法带来的问题是什么?如何解决?
  • 问题是:代码的复用性不高,而且不利于代码的维护和功能扩展。
  • 解决方案:引出我们要讲的多态。

12.7.2、多态介绍 & 特别说明

12.7.2.1、怎么理解多态

12.7.2.2、实例演示
class Animal:
    def cry(self):
        pass

class Cat(Animal):
    def cry(self):
        print("小猫喵喵叫")

class Dog(Animal):
    def cry(self):
        print("小狗汪汪叫")

class Pig(Animal):
    def cry(self):
        print("小猪噜噜叫")

# 注意,在Python面向对象编程中,子类对象可以传递给父类类型
def func(animal: Animal):
    animal.cry()

# 创建3个对象
cat = Cat()
dog = Dog()
pig = Pig()

# 调用函数
func(cat)
func(dog)
func(pig)
12.7.2.3、多态的好处

12.7.2.4、特别说明Python的多态特点

12.7.3、二说主人喂动物问题

# 使用多态特性来优化

class Food:
    name = None
    def __init__(self, name):
        self.name = name

class Fish(Food):
    # 特有的属性和方法
    pass

class Bone(Food):
    # 特有的属性和方法
    pass

class Grass(Food):
    pass

class Animal:
    name = None
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    pass

class Cat(Animal):
    pass

class Horse(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}")
    # 主人给动物喂食物
    def feed(self, animal: Animal, food: Food):
        print(f"主人{self.name}给动物:{animal.name}喂的食物是{food.name}")

# 测试
master = Master("Jack")
cat = Cat("alex")
fish = Fish("黄花鱼")
dog = Dog("bob")
bone = Bone("鸡脆骨")
horse = Horse("Mike")
grass = Grass("青草")

master.feed(cat, fish)
master.feed(dog, bone)
master.feed(horse, grass)

12.7.4、isinstance函数

12.7.4.1、基本说明

12.7.5、练习题

注意,当调用对象成员的时候,会和对象本身动态关联。一句话就是,自己有的,就用自己的。

class Employee:
    __name = None
    __salary = None

    def __init__(self, name, salary):
        self.__name = name
        self.__salary = salary
    
    def set_name(self, name):
        self.__name = name
    
    def set_salary(self, salary):
        self.__salary = salary
    
    def get_name(self):
        return self.__name
    
    def get_salary(self):
        return self.__salary
    
    def get_annual(self):
        return self.__salary * 12

class Worker(Employee):

    def __init__(self, name, salary):
        super().__init__(name, salary)
    
    def work(self):
        # print(f"工人:{self.get_name()}正在工作。。。")
        print(f"工人: {super().get_name()}正在工作。。。")
    
class Manager(Employee):
    __bonus = None

    def __init__(self, name, salary, bonus):
        super().__init__(name, salary)

        self.__bonus = bonus
    
    def set_bonus(self, bonus):
        self.__bonus = bonus
    
    def get_bonus(self):
        return self.__bonus
    
    def get_annual(self):
        # return self.__salary * 12  + self.__bonus
        return super().get_annual() + self.__bonus
    
    def manage(self):
        print(f"经理: {super().get_name()}正在管理。。。")


# 编写函数 show_emp_annual(e: Employee),实现获取任何员工对象的年工资。 --> 多态
def show_emp_annual(e: Employee):
    print(f"{e.get_name()}的年工资是{e.get_annual()}")



# 编写函数 working(e: Employee),如果是普通员工,则调用work方法,如果是经理,则调用manage方法。
def working(e: Employee):
    # 如果是普通员工
    if isinstance(e, Worker):
        e.work()
    elif isinstance(e, Manager):
        e.manage()
    else:
        print(f"无法确定工作状态...")


worker1 = Worker("king", 10000)
manager1 = Manager("James", 18000, 30000)
show_emp_annual(worker1)
show_emp_annual(manager1)
working(worker1)
working(manager1)

12.8、魔术方法

12.8.1、基本介绍

12.8.1.1、什么是魔术方法

12.8.1.2、常见的魔术方法

12.8.2、__str__

12.8.2.1、基本介绍

12.8.2.2、应用实例
class Monster:

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # 请输出 Monster[name, age, gender]对象的属性信息
    # 可以根据需要重写 __str__
    '''
        1. 在默认情况下,调用的是父类object的__str__
        2. 父类object的__str__返回的是:类型+地址
    '''

    def __str__(self):
        return f"{self.name}, {self.age}, {self.gender}"

m = Monster("jack", 18, "male")

print(m) # 默认输出类型 + 对象的地址

12.8.3、__eq__

12.8.3.1、基本介绍

12.8.3.2、应用实例
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # 重写
    
    def __eq__(self, other):
        # 判断 other 是不是 Person
        if isinstance(other, Person):
            return self.name == other.name and self.age == other.age and self.gender == other.gender
        return False

# 没有重写 __eq__ 前, == 比较的是内存地址

class Dog:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

p1 = Person("smith", 20, "male")
p2 = Person("smith", 20, "male")
d1 = Dog("smith", 20, "male")
print(f"p1==p2: {p1 == p2}")
print(f"p1==d1: {p1 == d1}")

12.8.4、其他几个魔术方法

12.9、Class对象和静态方法

12.9.1、Class对象

类本身也是对象,即:Class对象

class Monster:
    name = "蝎子精"
    age = 300

    def hi(self):
        print(f'hi() {self.name} - {self.age}')

print(Monster)

# 通过Class对象,可以引用属性(没有创建实例对象也可以引用 / 访问)
print(f"Monster.name: {Monster.name} Monster.age {Monster.age}")

# 通过类名如何调用非静态成员方法
Monster.hi(Monster)

12.9.2、静态方法

  • @staticmethod 将方法转换为静态方法
  •  静态方法不会接收隐式的第一个参数,要声明一个静态方法,语法:

    class C:
        @staticmethod
        def f(arg1, arg2, argN): ...
  • 静态方法既可以由类调用(如: C.f()),也可以由实例中调用(如 C().f())

12.10、抽象类

需求:

  • 当父类的某些方法,需要声明,但是又不确定如何实现时,怎么办?
  • 不需要实例化父类对象,父类主要的是用于设计和指定规范,让其他类来继承并实现,怎么办?
  • 解决方案 -> 抽象类

12.10.1、抽象类的介绍和快速入门

  • 默认情况下,Python不提供抽象类,Python附带一个模块,该模块为定义抽象基类提供了基础,该模块名称为abc。
  • 当我们需要抽象基类时,让类集成ABC(abc模块的ABC类),使用 @abstractmethod 生命抽象方法(@abstractmethod 用于声明抽象方法的装饰器,在 abc 模块中),那么这个类就是抽象类。
  • 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类的抽象方法。
from abc import ABC, abstractmethod

# Animal就是抽象类
class Animal(ABC):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    # 这时,cry就是一个抽象方法
    @abstractmethod
    def cry(self):
        # print("......")
        pass


# 注意,抽象类(含有抽象方法),不能实例化
# animal = Animal("apple", 3)

# 编写子类Tiger,集成Animal,并实现抽象方法
class Tiger(Animal):
    def cry(self):
        print("老虎嗷嗷叫!")

tiger = Tiger("alex", 9)
tiger.cry()

12.10.2、注意事项和使用细节

  • 抽象类不能被实例化。
  • 抽象类需要继承ABC,并且需要至少一个抽象方法。
  • 抽象类中可以有普通方法。
  • 如果一个类继承了抽象类,则它必须实现抽象方法类的所有抽象方法,否则它仍然是一个抽象类。

案例练习:

from abc import ABC, abstractmethod

class Employ(ABC):
    def __init__(self, name, id, salary):
        self.name = name
        self.id = id
        self.salary = salary
    
    @abstractmethod
    def work(self):
        pass

class CommonEmployee(Employ):
    def __init__(self, name, id, salary):
        super().__init__(name, id, salary)
    
    def work(self):
        print(f"普通员工 {self.name} 工作中。。。")

class Manager(Employ):
    def __init__(self, name, id, salary, bonus):
        super().__init__(name, id, salary)
        self.bonus = bonus
    def work(self):
        print(f"经理 {self.name} 工作中。。。")


ce = CommonEmployee("alex", 190, 20000)
m1 = Manager("bob", 10, 30000, 5000)
ce.work()
m1.work()

12.11、模板设计模式

12.11.1、什么是设计模式

  • 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
  • 设计模式就像是经典的棋谱,不同得棋局,我们用不同的棋谱,免去我们自己再思考和摸索。

12.11.2、模板设计模式 - 介绍

  • 基本介绍:
    抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
  • 模板设计模式能解决的问题:
    (1)当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
    (2)编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。

12.11.3、模板设计模式 - 最佳实践

开发需求:

1)有多个类,完成不同的任务job。

2)要求统计得到各自完成任务的时间。

3)请编程实现。

from abc import ABC, abstractmethod
import time

class Template(ABC):
    
    @abstractmethod
    def job(self):
        pass

    def cal_time(self):
        start= time.time()
        self.job()
        end = time.time()
        print(f"工作用时:{end - start}")

class AA(Template):
    def job(self):
        for i in range(10000):
            if i != 9999:
                print("挖矿中。。。")
            else:
                print("完成挖矿")
    
class BB(Template):
    def job(self):
        for i in range(100):
            if i != 99:
                print("钓鱼中。。。")
            else:
                print("完成钓鱼")

a = AA()
b = BB()
a.cal_time()
b.cal_time()

12.12、本章练习

12.12.1、练习1

定义一个Person类,属性:name, age, job,创建Person列表,有三个person对象,并按照 age 从大到小 的顺序进行排序。

# class Person:
#     def __init__(self, name, age, job):
#         self.name = name
#         self.age = age
#         self.job = job

# p1 = Person("alex", 19, "engineer")
# p2 = Person("bob", 23, "teacher")
# p3 = Person("cathy", 34, "worker")

# PERSON = [p1, p2, p3]

# length = len(PERSON)

# for i in range(length - 1):
#     for j in range(length - 1 - i):
#         if PERSON[j].age < PERSON[j+1].age:
#             temp_name = PERSON[j].name
#             temp_age  = PERSON[j].age
#             temp_job  = PERSON[j].job
#             PERSON[j].name = PERSON[j+1].name
#             PERSON[j].age  = PERSON[j+1].age
#             PERSON[j].job  = PERSON[j+1].job
#             PERSON[j+1].name = temp_name
#             PERSON[j+1].age  = temp_age
#             PERSON[j+1].job  = temp_job

# for j in range(length):
#     print(PERSON[j].name)


class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job

p1 = Person("alex", 19, "engineer")
p2 = Person("bob", 23, "teacher")
p3 = Person("cathy", 34, "worker")

PERSON = [p1, p2, p3]

length = len(PERSON)
for i in range(length - 1):
    for j in range(length - 1 - i):
        if PERSON[j].age < PERSON[j+1].age:
            # 交换整个对象,更简洁
            PERSON[j], PERSON[j+1] = PERSON[j+1], PERSON[j]

for person in PERSON:
    print(f"{person.name} - {person.age} - {person.job}")


#############################################################

class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job
    
    def __str__(self):
        return f"{self.name} - {self.age} - {self.job}"

p1 = Person("alex", 19, "engineer")
p2 = Person("bob", 23, "teacher")
p3 = Person("cathy", 34, "worker")

PERSON = [p1, p2, p3]

# 方法2:使用内置排序(推荐)
PERSON.sort(key=lambda person: person.age, reverse=True)

# 输出结果
for person in PERSON:
    print(person)

12.12.2、练习2

编写Doctor类,属性:name,age,job,gender,sal,重写__eq__()方法,并判断测试两个对象是否相等。

class Doctor:
    def __init__(self, name, age, job, gender, sal):
        self.name = name
        self.age = age
        self.job = job
        self.gender = gender
        self.sal = sal
    
    def __eq__(self, other):
        if isinstance(other, Doctor):
            if self.name == other.name and self.age == other.age and self.job == other.job and self.gender == other.gender \
                and self.sal == other.sal:
                print("相等")
            else:
                print("不等")
        else:
            print("没意义")

d1 = Doctor("alex", 18, "doctor", "male", 18000)
d2 = Doctor("alex", 18, "doctor", "male", 18000)

d1.__eq__(d2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱摸鱼的经济er

感谢支持,会继续努力!谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值