19.python中函数和面向对象基础

本篇内容

函数部分
    – 不定长参数的扩展补充
    – 匿名函数 lambda

面向对象

函数

不定长参数补充-函数调用时的拆包

# 定义一个名为my_sum的函数,可以接收任意数量的位置参数和关键字参数
def my_sum(*args, **kwargs):
    num = 0  # 初始化变量num,用于保存求和的结果
    
    # 遍历所有位置参数,并累加到num中
    for i in args:
        num += i
    
    # 遍历所有关键字参数的值,并累加到num中
    # kwargs是一个字典,所以使用.values()来获取所有的值
    for j in kwargs.values():
        num += j
    
    print(num)  # 打印出求和的结果

'''
需求说明:
我们有一个列表my_list和一个字典my_dict,需要使用my_sum函数来计算它们中所有数字的和。
'''

# 定义一个列表my_list,包含四个整数
my_list = [1, 2, 3, 4]

# 定义一个字典my_dict,键是字母,值是对应的整数
my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

# 使用my_sum函数求和
# 下面的调用展示了如何将列表和字典中的数据传递给my_sum函数

# 直接传递位置参数(这里为了演示,没有使用my_list,而是直接写了数字)
# my_sum(1, 2, 3, 4)  # 输出: 10

# 直接传递关键字参数(这里为了演示,没有使用my_dict,而是直接写了关键字参数)
# my_sum(a=1, b=2, c=3, d=4)  # 输出: 10

# 如果想将列表(或元组)中的数据分别作为位置参数传递给函数,需要使用*进行拆包操作
# *my_list会将my_list中的每个元素分别作为位置参数传递给my_sum函数
# my_sum(*my_list)  # 输出: 10,等同于my_sum(1, 2, 3, 4)

# 如果想将字典中的数据作为关键字参数传递给函数,需要使用**进行拆包操作
# **my_dict会将my_dict中的每个键值对作为关键字参数传递给my_sum函数
my_sum(**my_dict)  # 输出: 10,等同于my_sum(a=1, b=2, c=3, d=4)

匿名函数

匿名函数:
就是使用 lambda 关键字定义的函数,一般称为使用def 关键字定义的函数为,标准函数

匿名函数只能书写一行代码,匿名函数返回值不需要return,一行代码(表达式)的结果就是返回值。

使用场景:
作为函数的参数,这个函数比较简单,只使用一次,没有必要使用def

语法:

lambda 参数: 一行代码   # 这一行代码,称为是表达式
​
# 匿名函数一般不需要我们主动的调用, 一般作为函数的参数使用的
# 我们在学习阶段为了查看匿名函数定义的是否正确,可以调用
# 1, 在定义的时候 ,将匿名函数的引用保存到一个变量中
变量 = lambda 参数: 一行代码
# 2. 使用变量进行调用
变量()


# 简单的加法函数
add = lambda x, y: x + y
print(add(2, 3))  # 输出: 5


# 无参数的匿名函数
greet = lambda: "Hello, World!"
print(greet())  # 输出: Hello, World!

20250227195610878-image

案例

# 1. 无参数返回值的函数
def func1():
    # 打印字符串 'hello world'
    print('hello world')

# 调用func1函数,将会打印 'hello world'
func1()

# 使用lambda表达式定义一个匿名函数,该函数没有参数,执行时打印 'hello world'
func11 = lambda: print('hello world')

# 调用匿名函数func11,将会打印 'hello world'
func11()

# 2. 无参数有返回值的函数
def func2():
    # 返回整数 10
    return 10

# 调用func2函数,并打印返回值,将会打印 10
print(func2())

# 使用lambda表达式定义一个匿名函数,该函数没有参数,返回整数 10
func22 = lambda: 10

# 调用匿名函数func22,并打印返回值,将会打印 10
print(func22())

# 3. 有参数无返回值的函数
def my_sum(a, b):
    # 打印参数a和b的和
    print(a + b)

# 调用my_sum函数,传入参数1和2,将会打印它们的和 3
my_sum(1, 2)

# 使用lambda表达式定义一个匿名函数,该函数接受两个参数a和b,打印它们的和
my_sum11 = lambda a, b: print(a + b)

# 调用匿名函数my_sum11,传入参数10和20,将会打印它们的和 30
my_sum11(10, 20)

# 4. 有参数有返回值的函数
def func4(a, b):
    # 返回参数a和b的和
    return a + b

# 调用func4函数,传入参数1和2,并打印返回值,将会打印它们的和 3
print(func4(1, 2))

# 使用lambda表达式定义一个匿名函数,该函数接受两个参数a和b,返回它们的和
func44 = lambda a, b: a + b

# 调用匿名函数func44,传入参数10和20,并打印返回值,将会打印它们的和 30
print(func44(10, 20))

练习

  1. 定义一个匿名函数可以求两个数的乘积
  2. 定义一个匿名函数,参数为字典,返回字典中键为 age 的值
# 1. 定义一个匿名函数,可以求两个数的乘积(需要两个参数)
# lambda a, b: a * b 表示定义一个匿名函数,接受两个参数a和b,返回它们的乘积
func1 = lambda a, b: a * b

# 调用func1函数,传入参数1和2,打印它们的乘积,将会打印 2
print(func1(1, 2))

# 再次调用func1函数,传入参数3和2,打印它们的乘积,将会打印 6
print(func1(3, 2))

# 2. 定义一个匿名函数,参数为一个字典,返回字典中键为'age'的值
# lambda x: x.get('age') 表示定义一个匿名函数,接受一个参数x(预期为字典),返回x中键为'age'的值
func2 = lambda x: x.get('age')

# 另一个匿名函数,功能与func2相同,但是使用了字典的索引操作来获取'age'的值
# lambda x: x['age'] 表示定义一个匿名函数,接受一个参数x(预期为字典),返回x中键为'age'的值
func3 = lambda x: x['age']

# 定义一个字典my_dict,包含'name'和'age'两个键值对
my_dict = {'name': '张三', 'age': 18}

# 调用func2函数,传入my_dict作为参数,打印返回的'age'的值,将会打印 18
print(func2(my_dict))

# 调用func3函数,传入my_dict作为参数,打印返回的'age'的值,将会打印 18
print(func3(my_dict))

匿名函数作为函数的参数-列表中的字典排序

# 定义一个包含多个字典的列表,每个字典代表一个用户,包含用户的姓名和年龄
user_list = [
    {"name": "zhangsan", "age": 18},  # 用户张三,年龄18
    {"name": "lisi", "age": 19},      # 用户李四,年龄19
    {"name": "wangwu", "age": 17},    # 用户王五,年龄17
]

# 列表排序示例:

# 如果列表中的元素都是数字,可以直接使用 sort() 方法进行排序
# 例如:
# numbers = [3, 1, 4, 1, 5, 9]
# numbers.sort()  # 升序排序,结果为 [1, 1, 3, 4, 5, 9]
# numbers.sort(reverse=True)  # 降序排序,结果为 [9, 5, 4, 3, 1, 1]

# 但是对于列表中的元素都是字典的情况,想要根据字典中的某个键的值进行排序,需要提供一个排序的关键字参数 key
# 例如,如果我们想要根据用户的年龄进行排序:

# 按年龄升序排序
user_list.sort(key=lambda user: user['age'])  # 升序排序,结果为 [{'name': 'wangwu', 'age': 17}, {'name': 'zhangsan', 'age': 18}, {'name': 'lisi', 'age': 19}]

# 如果想要按照年龄降序排序,可以设置 sort 方法的 reverse 参数为 True
user_list.sort(key=lambda user: user['age'], reverse=True)  # 降序排序,结果为 [{'name': 'lisi', 'age': 19}, {'name': 'zhangsan', 'age': 18}, {'name': 'wangwu', 'age': 17}]

# 打印排序后的列表
print(user_list)


# 定义一个函数,用于获取字典中的'age'值,这个函数将作为排序的关键字参数
def get_value(x):
    return x['age']  # 返回传入字典x的'age'键对应的值

# 使用sort方法对user_list进行排序
# key参数指定了一个函数,这个函数会被应用到列表的每个元素上
# 在这里,我们传入了get_value函数,所以列表将根据每个字典的'age'值进行排序
user_list.sort(key=get_value)

# 打印排序后的用户列表
print(user_list)

字符串比大小

大于号(>)​​ 和 ​小于号(<

ASCII 码​(American Standard Code for Information Interchange,美国信息交换标准代码)是一种基于拉丁字母的字符编码标准,用于在计算机和其他设备中表示文本。ASCII 码使用 ​7 位二进制数​(即 0 到 127 的十进制数)来表示字符,包括:

  • 大小写英文字母(A-Z, a-z)
  • 数字(0-9)
  • 标点符号(如 !,. 等)
  • 控制字符(如换行符、退格符等)
字符比大小,是比较字符对应的 ASCII 码值
A < Z < a < z
ord(字符)  # 获取字符对应的 ASCII 的值
chr(ASCII 值)  # 获取对应的 字符
​
字符串比大小: 
对应下标位置字符的大小, 直到比出大小, 如果全部比完了, 还没有比出大小,就是相等

20250227203252127-image

20250227203513455-image

面向对象

基本的介绍

面向对象是一个编成思想(写代码的套路)

编程思想:
1. 面向过程:面向过程是一种以 ​过程(函数)​​ 为核心的编程思想。它将程序看作一系列步骤的集合,通过调用函数来完成任务。
2. 面向对象:面向对象是一种以 ​对象​ 为核心的编程思想。它将程序看作一系列对象的集合,每个对象包含数据(属性)和操作数据的方法。

以上两种都属于写代码的方法,最终目的都是为了将代码写出来,只不过过程和思考方式不太一样

  • 面向过程
    – 关注的是 具体步骤的实现,所有功能都自己书写
    – 亲历亲为
    – 定义一个个函数,最终按照顺序调用函数
  • 面向对象
    – 关注的是结果,谁(哪个对象)能帮我做这件事
    – 偷懒
    – 找一个对象(),让对象去做这件事

类和对象

面向对象的核心思想是 找一个对象去帮忙处理事情,在程序代码中,对象是由 类 创建的

类和对象,是面向对象编程思想中重要的两个概念


  • – 抽象的概念,对多个特征和行为相同或者相似事物的统称
    – 泛指的(指代多个,而不是具体的一个)
  • 对象
    – 具体存在的一个事物,看得见摸得着
    – 特指的(指代一个)

苹果 —— 》类

红苹果——》类

张三嘴里正在吃的那个苹果——》对象

类的组成

  1. 类名(给这多个事物起一个名字,在代码中,满足大驼峰命名法(每个单词的首字母大写))
  2. 属性(事物的特征,即有什么,一般书写中的名词)
  3. 方法(事物的行为,即做什么事,一般是动词)

类的抽象(类的设计)

类的抽象,其实就是找到类的类名,属性 和 方法

例如:

20250227204549662-image

类名:  人类(Person, People)
属性:  名字(name), 年龄(age), 身高(height)
方法:  跑步(run)  吃(eat)

20250227204621125-image

类名: 狗类(Dog)
属性: 颜色(color) , 名字(name)
方法: 汪汪叫 (bark),  摇尾巴(shake)

面向代码的步骤

  1. 定义类,在定义类之前先设计类
  2. 创建对象,使用第一步定义的类创建对象
  3. 通过对象调用方法

面向对象基本代码的书写

1.定义类

萧定一简单的类,不包含属性,在python中定义类需要使用关键字 class

方法:
方法的本质是在类中定义的函数,只不过第一个参数是 self

class 类型:

    # 在缩进中书写的内容,都是类中的代码

    def 方法名(self): # 就是一个方法

        pass

2.创建对象

创建对象是使用 类名()进行创建

即:
类名()
# 创建一个对象,这个对象在后续不能使用
# 创建的对象想要在后续的代码中继续使用,需要使用一个变量,将这个对象保存起来

变量 = 类名()
# 这个变量中保存的是对象的地址,一般可以成为这个变量的对象

# 一个类可以创建对各对象,只要出现 类名() 就是创建了一个对象,每个对象的地址是不一样的

3.调用方法

对象.方法名()

列表.sort()
列表.append()

4.案例实现

需求:小猫爱吃鱼,小猫要喝水

类名:猫类 cat

属性:暂无

方法:吃鱼(eat) 喝水(drink)

'''
需求:小猫爱吃鱼,小猫要喝水,定义不带属性的类
'''

# 定义一个名为cat的类,不带任何属性
class cat:
    # 定义一个名为eat的方法,表示小猫吃鱼的行为
    def eat(self): 
        print('小猫爱吃鱼')  # 运行此方法会打印出'小猫爱吃鱼'
        
    # 定义一个名为drink的方法,表示小猫喝水的行为
    def drink(self):
        print('小猫要喝水')  # 运行此方法会打印出'小猫要喝水'
        
# 创建一个cat类的实例对象,名为blue_cat
blue_cat = cat()

# 通过blue_cat对象调用eat方法
blue_cat.eat()  # 运行结果:小猫爱吃鱼

# 通过blue_cat对象调用drink方法
blue_cat.drink()  # 运行结果:小猫要喝水

# 创建另一个cat类的实例,名为black_cat
black_cat = cat()

# 通过black_cat对象调用eat方法 
black_cat.eat()  # 运行结果:小猫爱吃鱼
 
# 通过black_cat对象调用drink方法
black_cat.drink()  # 运行结果:小猫要喝水

# 创建一个cat类的实例,但是没有给它赋值给任何变量
cat()  # 是,创建了一个临时对象

a = black_cat # 不是,并没有创建对象
b = cat # 不是,没有创建对象

self的说明

# 定义一个名为cat的类,表示小猫
class cat:
    # 定义一个名为eat的方法,表示小猫吃鱼的行为
    def eat(self): 
        print('小猫爱吃鱼')  # 当调用此方法时,会打印出'小猫爱吃鱼'

# 创建一个cat类的实例,名为black_cat
black_cat = cat()

# 通过black_cat对象调用eat方法
black_cat.eat()  # 运行结果:小猫爱吃鱼
  1.  从函数的语法上讲,self 是形参,就可以是任意的变量名,只不过我们习惯将这个形参写作self
  2. self 虽然看起来是类方法中的一个普通形参,但在调用方法时,Python 解释器会自动将调用该方法的对象作为实参传递给 self,因此 self 的本质就是调用方法的对象本身。
  3. 当我们通过一个对象调用类的方法时,Python 会自动将这个对象的引用传递给方法的第一个参数(通常是 self),因此 self 就是这个对象的引用。换句话说,self 和调用方法的对象是同一个东西。
  4. ​在 Python 中,self 是类方法中的一个局部变量,而直接创建的对象(比如 obj = MyClass())通常是全局变量。
"""
需求:小猫爱吃鱼,小猫要喝水, 定义不带属性的类
"""
class cat():
    # 定义一个名为eat的方法,表示小猫吃鱼的行为
    def eat(self):
        # 打印当前对象的内存地址和字符串'self'
        print(f'{id(self)},self')  # 运行结果示例: 140289177749280,self
        # 打印'小猫爱吃鱼'
        print('小猫爱吃鱼')  # 运行结果: 小猫爱吃鱼
        
# 创建一个cat类的实例,名为blue_cat
blue_cat = cat()
# 打印blue_cat对象的内存地址和字符串'blue_cat'
print(f'{id(blue_cat)},blue_cat')  # 运行结果示例: 140289177749280,blue_cat

# 通过blue_cat对象调用eat方法
# 解释器会将blue_cat对象作为参数传递给eat方法中的self
blue_cat.eat()
# 运行结果:
# 140289177749280,self
# 小猫爱吃鱼

print('-' * 30)

# 创建另一个cat类的实例,名为black_cat
black_cat = cat()
# 打印black_cat对象的内存地址和字符串'black_cat'
print(f'{id(black_cat)},black_cat')  # 注意这里打印的是'balck_cat',应该是'black_cat'
# 运行结果示例: 140289177749312,black_cat

# 通过black_cat对象调用eat方法
# 解释器会将black_cat对象作为参数传递给eat方法中的self
black_cat.eat()
# 运行结果:
# 140289177749312,self
# 小猫爱吃鱼

在上面的输出中,你可以看到 blue_cat 和 black_cat 的内存地址是不同的,但是当你调用它们的 eat 方法时,self 参数引用的是各自的对象,所以打印出的内存地址也是各自对象的地址。

对象的属性操作

1.添加属性

对象.属性名 = 属性值

  1. 类内部添加
    ·在内部方法中,self是对象,
    ·self.属性名 = 属性值
    ·# 在类中添加属性一般写在__init__方法中
    ·在 Python 中,__init__ 是一个特殊的方法,被称为 ​构造函数​(constructor)。它的主要作用是在创建类的对象时,自动执行一些初始化操作,比如设置对象的属性或执行一些必要的准备工作。
  2. 类外部添加
    对象.属性名 = 属性值 
    # 一般不适用

获取属性

对象.属性名

  • 类内部
    在内部方法中,self是对象
    self.属性名
  • 类外部
    对象.属性名 
    # 一般很少使用
# 定义一个名为cat的类
class cat():
    # 定义一个名为eat的方法,表示小猫吃鱼的行为
    def eat(self):
        # 打印当前对象的内存地址和字符串'self'
        print(f'{id(self)},self')  # 打印对象的内存地址和'self'
        # 打印小猫的名字和爱吃鱼的信息
        print(f'小猫{self.name}爱吃鱼...')  # 需要对象有name属性
        
# 创建一个cat类的实例,名为blue_cat
blue_cat = cat()
# 打印blue_cat对象的内存地址和字符串'blue_cat'
print(f'{id(blue_cat)},blue_cat')  # 打印对象的内存地址和'blue_cat'

# 给blue_cat对象添加name属性,并赋值为'蓝猫'
blue_cat.name = '蓝猫'

# 通过blue_cat对象调用eat方法
# 解释器会将blue_cat对象作为参数传递给eat方法中的self
blue_cat.eat()  # 运行结果:
# 对象blue_cat的内存地址,self
# 小猫蓝猫爱吃鱼...

print('_*_' * 30)  # 打印分隔线

# 创建另一个cat类的实例,名为black_cat
black_cat = cat() 
# 给black_cat对象添加name属性,并赋值为'黑猫'
black_cat.name = '黑猫'
# 打印black_cat对象的内存地址和字符串'black_cat'
print(f'{id(black_cat)},black_cat')  # 打印对象的内存地址和'black_cat'

# 通过black_cat对象调用eat方法
# 解释器会将black_cat对象作为参数传递给eat方法中的self
black_cat.eat()  # 运行结果:
# 对象black_cat的内存地址,self
# 小猫黑猫爱吃鱼...

魔法方法

python中有一类方法,以两个下划线开头,两个下划线结尾,并且在满足某个条件的情况下,会自动调用,这类方法称位魔法方法。

学习内容

  1. 什么情况下自动调用
  2. 有什么用,用在哪
  3. 书写的注意事项

__init__方法(重要)

  1. 什么情况下自动调用
    创建对象之后会自动调用
  2. 有什么用,用在哪
    1. 给对象添加属性(初始化方法,构造方法)
    2. 某些代码,在每次创建对象之后,都要执行,就可以将这行代码写在 __init__ 方法中
  3. 书写的注意事项
    1. 不要写错了
    2. 如果 init 方法中,存在除了self 之外的参数,在创建对象的时候必须传参
"""
猫类, 属性 name, age , show_info(输出属性信息)
"""

# 定义一个名为cat的类,包含name和age属性,以及一个show_info方法用于输出属性信息
class cat():
    # 定义初始化方法,当创建对象时会自动调用此方法
    def __init__(self, name, age):  # 初始化方法,接收name和age两个参数
        self.name = name  # 给对象添加name属性,并赋值为传入的name参数
        self.age = age    # 给对象添加age属性,并赋值为传入的age参数
        
    # 定义一个名为show_info的方法,用于输出对象的name和age属性
    def show_info(self):
        print(f'小猫的名字是:{self.name}, 年龄是:{self.age}')  # 输出对象的name和age属性

# 创建一个cat类的实例,名为blue_cat,传入'name'为'蓝猫','age'为2
# 此时会自动调用__init__方法来初始化blue_cat对象
blue_cat = cat('蓝猫', 2)

# 创建一个对blue_cat对象的引用,名为blue
blue = blue_cat

# 通过blue引用调用show_info方法,输出blue_cat对象的属性信息
# show_info方法不会自动调用,需要显式调用
blue.show_info()  # 运行结果: 小猫的名字是:蓝猫, 年龄是:2

# 创建另一个cat类的实例,名为black_cat,传入'name'为'黑猫','age'为3
# 此时会自动调用__init__方法来初始化black_cat对象
black_cat = cat('黑猫', 3)

# 通过black_cat对象调用show_info方法,输出black_cat对象的属性信息
# show_info方法不会自动调用,需要显式调用
black_cat.show_info()  # 运行结果: 小猫的名字是:黑猫, 年龄是:3

__str__方法 *

  1. 什么情况下自动调用
    1. 使用print(对象)打印对象的时候,会自动调用
  2. 有什么用,用在哪?
    1. 在这个方法中一般书写对象的 属性信息的,即打印对象的时候想要查看什么信息,在这个方法中进行定义的
    2. 如果类中没有 __str__方法,print(对象),默认输出对象的引用地址
  3. 书写的注意事项
    1. 这个方法必须返回一个字符串
# 定义一个名为cat的类
class cat():
    # 定义初始化方法,当创建对象时会自动调用此方法
    def __init__(self, name, age):
        self.name = name  # 给对象添加name属性,并赋值为传入的name参数
        self.age = age    # 给对象添加age属性,并赋值为传入的age参数
        
    # 定义一个名为__str__的方法,用于定义对象的字符串表示形式
    # 当使用print函数打印对象时,会自动调用此方法
    def __str__(self):
        # 方法必须返回一个字符串,这里返回包含name和age信息的字符串
        return f'小猫的名字:{self.name},年龄是:{self.age}'
    
# 创建一个cat类的实例,名为blue_cat,传入'name'为'蓝猫','age'为2
# 此时会自动调用__init__方法来初始化blue_cat对象
blue_cat = cat('蓝猫', 2)
# 使用print函数打印blue_cat对象
# 由于定义了__str__方法,print函数会调用此方法并输出返回的字符串
print(blue_cat)  # 运行结果: 小猫的名字:蓝猫,年龄是:2

# 创建另一个cat类的实例,名为black_cat,传入'name'为'黑猫','age'为3
# 此时会自动调用__init__方法来初始化black_cat对象
black_cat = cat('黑猫', 3)
# 使用print函数打印black_cat对象
# 由于定义了__str__方法,print函数会调用此方法并输出返回的字符串
print(black_cat)  # 运行结果: 小猫的名字:黑猫,年龄是:3

__del__方法

  • __init__方法:
    创建对象之后,会自动调用(构造方法)
  • __del__方法:
    对象被删除销毁时,自动调用的(遗言,处理后事)(析构方法)
  1. 调用场景:程序代码运行结束后,所有对象都被销毁
  2. 调用场景:直接使用del删除对象(如果对象有多个名字或者多个对象引用一个对象),需要把所有的对象都删除才行
# 定义一个名为Demo的类
class Demo:
    # 定义初始化方法,当创建对象时会自动调用此方法
    def __init__(self, name):
        print('我是 __init__, 我被调用了 ')  # 打印初始化方法被调用的提示信息
        self.name = name  # 给对象添加name属性,并赋值为传入的name参数

    # 定义析构方法,当对象被销毁时会自动调用此方法
    def __del__(self):
        print(f'{self.name} 没了, 给他处理后事...')  # 打印对象被销毁的提示信息,包含对象的name属性

# Demo('a')  # 这行代码如果取消注释,会创建一个Demo类的实例,并调用__init__方法

# 创建一个Demo类的实例,名为a,传入'name'为'a'
a = Demo('a')  # 运行结果: 我是 __init__, 我被调用了 

# 创建另一个Demo类的实例,名为b,传入'name'为'b'
b = Demo('b')  # 运行结果: 我是 __init__, 我被调用了 

# 删除销毁对象a
del a  # 运行结果: a 没了, 给他处理后事...

# 打印代码运行结束的提示信息
print('代码运行结束')  # 运行结果: 代码运行结束

# 当程序运行到此结束,Python解释器会自动销毁所有未引用的对象
# 因此,b对象会被销毁,调用其__del__方法
# 运行结果: b 没了, 给他处理后事...
THE END
喜欢就支持一下吧
赞赏 分享