python学习day29
知识点回顾
- 类的装饰器
- 装饰器思想的进一步理解:外部修改、动态
- 类方法的定义:内部定义和外部定义
作业:复习类和函数的知识点,写下自己过去29天的学习心得,如对函数和类的理解,对python这门工具的理解等,未来再过几个专题部分我们即将开启深度学习部分。
1.函数的基本写法:
def function_name(parameter1, parameter2, ...):
"""
Docstring: 描述函数的功能、参数和返回值 (可选但强烈推荐)
"""
# 函数体: 实现功能的代码
# ...
return value # 可选,用于返回结果
2.函数的参数类型(位置参数、默认参数、可变数量参数):
参数有以下类型:
- 位置参数 (Positional Arguments): 调用时按顺序匹配。比如def describe_pet(animal_type, pet_name):中的animal_type, pet_name就是位置参数
- 默认参数值 (Default Parameter Values): 定义函数时给参数指定默认值,调用时如果未提供该参数,则使用默认值。def describe_pet_default(pet_name, animal_type="狗"):这其中的animal_type="狗"就是默认参数。
- 可变数量参数 (*args 和 **kwargs):
- *args: 将多余的位置参数收集为一个元组。def make_pizza(size, *toppings):中的*toppings就是一个*args
- **kwargs: 将多余的关键字参数收集为一个字典。def build_profile(first_name, last_name, **user_info):中的**user_info就是一个**kwargs
*args 和 **kwargs 的核心目的是让函数能够接收不定数量的参数,并以元组和字典的形式在函数内部进行处理。也就是说 当位置参数用完了 就自动变成*args,当关键词参数用完了 就自动变成**kwarges
process_data(101, "Alice", "vip", "new_user", location="USA", age=30)
# ID: 101
# Name: Alice
# Tags (*args): ('vip', 'new_user') <-- "vip", "new_user" 是多余的位置参数,被 *tags 收集
# Status: pending <-- status 使用默认值,因为调用中没有 status=...
# Details (**kwargs): {'location': 'USA', 'age': 30} <-- location 和 age 是多余的关键字参数,被 **details 收集
3.装饰器:
装饰器的本质是一个高阶函数,它接收一个函数作为参数,并返回一个新函数来替代原函数。这个新函数需要:
1. 保留原函数的调用方式(参数和返回值)。
2. 在原函数执行前后添加额外逻辑(如计时、日志等)。
因此,我们需要在装饰器内部定义一个新函数来实现这些功能。
# 继续定义判断质数的函数
def is_prime(num):"""判断一个数是否为素数"""if num < 2:return Falseelif num == 2:return Trueelse:for i in range(2, num):if num % i == 0:return Falsereturn True# 装饰器的标准写法
@display_time
def prime_nums(): # 这2行是一个整体"""找出2到10000之间的所有素数并打印"""for i in range(2, 10000):if is_prime(i):print(i)prime_nums()
# 执行时间每次都会变,但是变动不大,一般计算稳定的执行时间我们都是重复1000遍,然后取平均
其中display_time是一个记录时间的装饰器
import time# 定义一个装饰器
def display_time(func):def wrapper(): # 定义一个内部函数,在装饰器中wrapper函数是一个常用的函数名,并非强制,约定俗成的start_time = time.time()func() # 直接调用原函数(无参数),这里的func()是指装饰器需要修饰的函数,在这里是prime_nums()end_time = time.time()print(f"执行时间: {end_time - start_time} 秒")return wrapper # return wrapper是返回函数对象,如果是return wrapper()则是立即执行wrapper函数
4.类的定义:
一个常见的类的定义包括了:
1. 关键字class
2. 类名
3. 语法固定符号冒号(:)
4. 一个初始化函数__init__(self)
注意:注意:init左右各有两个下划线__,需要传入self这个特殊的参数。
比如:
class Teacher:
def __init__(self):
5.类的初始化方法:
初始化方法又叫构造方法、特殊方法
类有2种方法
1. 初始化方法
2. 普通方法
初始化方法:
class Teacher: def __init__(self): self.name = "Susan" self.subject = "English"self.age = 33
普通方法:
def function_name(parameter1, parameter2, ...):"""Docstring: 描述函数的功能、参数和返回值 (可选但强烈推荐)"""# 函数体: 实现功能的代码# ...return value # 可选,用于返回结果
6.类的继承:
在面向对象编程中,继承允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码复用和功能扩展。子类可以:
1. 复用父类的代码(无需重新实现)。
2. 重写父类的方法(修改或增强功能)。
3. 添加新的方法和属性(扩展功能)。
# 先沿用之前定义的teacher类
class Teacher:def __init__(self, name, subject, age):self.name = nameself.subject = subjectself.age = agedef teach_lesson(self):print(f"{self.name}正在教{self.subject}。")def criticize(self, student_name):print(f"{self.name}正在批评{student_name}。")# 继承 Teacher 类,起名特级教师
class MasterTeacher(Teacher): # 1. 继承需要在括号中指定父类def __init__(self, name, subject, age, experience_years):# 2. 继承的时候需要调用父类的构造方法,所以需要传入父类的参数,同时也可以传入自己的参数# 调用父类的构造方法初始化基本属性super().__init__(name, subject, age) # 3. 调用父类的构造方法,这里的super()是一个内置函数,返回父类的实例# 4. 此时子类自动拥有了父类的属性和方法# 添加子类特有的属性self.experience_years = experience_years # 5. 子类特有的属性可以在这里定义# 重写父类方法,增强功能-----如果子类定义了与父类同名的方法,子类实例会优先调用子类的方法。def teach_lesson(self): # 6. 重写父类的方法print(f"{self.name}(特级教师)正在用高级方法教授{self.subject}。")# 新增子类特有的方法def give_lecture(self, topic): print(f"{self.name}正在举办关于{topic}的讲座。")# 创建子类实例
master = MasterTeacher("王教授", "数学", 45, 20)# 调用继承的方法
master.teach_lesson() # 调用重写的父类的方法
master.criticize("李同学") # 调用父类的方法,如果不修改方法,则可以直接继承父类的方法# 调用子类特有的方法
master.give_lecture("微积分") # 调用子类新增的方法
7.类的装饰器:
类也有修饰器,他的逻辑类似:接收一个类,返回一个修改后的类。例如
1. 添加新的方法或属性(如示例中的 log 方法)。
2. 修改原有方法(如替换 __init__ 方法,添加日志)。
3. 甚至可以返回一个全新的类(继承或组合原类)。
通过类装饰器,可以在不修改类内部代码的情况下,为多个类统一添加功能(如日志、统计)
同样是语法糖的写法:第一步先定义装饰器,第二步应用装饰器
函数装饰器写法:
#函数装饰器的写法
# 定义一个装饰器
def display_time(func):def wrapper(): # 定义一个内部函数,在装饰器中wrapper函数是一个常用的函数名,并非强制,约定俗成的start_time = time.time()func() # 直接调用原函数(无参数),这里的func()是指装饰器需要修饰的函数,在这里是prime_nums()end_time = time.time()print(f"执行时间: {end_time - start_time} 秒")return wrapper# 继续定义判断质数的函数
def is_prime(num):"""判断一个数是否为素数"""if num < 2:return Falseelif num == 2:return Trueelse:for i in range(2, num):if num % i == 0:return Falsereturn True# 装饰器的标准写法
@display_time
def prime_nums(): # 这2行是一个整体"""找出2到10000之间的所有素数并打印"""for i in range(2, 10000):if is_prime(i):print(i)prime_nums()
类装饰器写法:
# 定义类装饰器:为类添加日志功能
def class_logger(cls):# 保存原始的 __init__ 方法original_init = cls.__init__def new_init(self, *args, **kwargs):# 新增实例化日志print(f"[LOG] 实例化对象: {cls.__name__}")original_init(self, *args, **kwargs) # 调用原始构造方法# 将类的 __init__ 方法替换为新方法cls.__init__ = new_init# 为类添加一个日志方法(示例)def log_message(self, message):print(f"[LOG] {message}")cls.log = log_message # 将方法绑定到类,这是一种将外部函数添加为类的属性的方法return cls# 定义简单打印类,应用装饰器
# 同样是语法糖的写法
@class_logger
class SimplePrinter:def __init__(self, name):self.name = name # 构造方法:初始化名称def print_text(self, text):"""简单打印方法"""print(f"{self.name}: {text}")# 使用示例
printer = SimplePrinter("Alice") # 实例化时触发装饰器的日志
printer.print_text("Hello, World!") # 调用普通方法
printer.log("这是装饰器添加的日志方法") # 调用装饰器新增的方法
实际上,定义类的方法,有2类写法
1. 在类定义内部直接写方法,这是静态方法,一般定义类都这么完成。
2. 在类定义外部定义方法,然后把方法赋值给类的属性---这是一种动态方法,常在装饰器中使用,可以再外部修改类的方法。
注意到其中的cls.log = log_message 这行代码,他把外部的函数赋值给了类的新定义的属性,这里我们介绍这种写法,这就是外部赋值。
@浙大疏锦行