系统学习Python——装饰器:类装饰器-[单例类:基础知识]

本文介绍了如何在Python中使用类装饰器,特别是在类上进行实例创建的管理,如实现单例模式,确保每个类只有一个实例。作者通过实例展示了如何使用类装饰器包装类的构造方法以达到此目的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分类目录:《系统学习Python》总目录


在前面的文章中,我们已经编写了函数装饰器来管理函数调用,但是正如我们已经见到的,从Python2.6和Python3.0起装饰器已被扩展,因此也能在类上使用。如同前面所提到的,尽管类装饰器与函数装饰器在概念上很相像,但类装饰器是在类上使用的一一它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例。和函数装饰器一样,类装饰器其实只是可选的语法糖,尽管很多人相信,它们使得程序员的意图更为明显并且能使不正确的调用或错失的调用最小化。

由于类装饰器可以拦截实例创建调用,因此它们可以用来管理一个类的所有实例,或者扩展这些实例的接口。为了说明这点,这里的第一个类装饰器示例做了前一项工作一一一管理一个类的所有实例。这段代码实现了传统的单例编程模式,其中每个类最多只有一个实例存在。它的singleton函数定义并返回一个用于管理实例的函数,同时@语法在这个函数中自动包装了一个主体类:

instances = {}

def singleton(aClass):
    def onCall(*args, **kwargs):
        if aClass not in instances:
            instances[aClass] = aClass(*args, **kwargs)
        return instances[aClass]
    return onCall

为了使用它,对施行单一实例模型的类进行装饰:

@singleton
class Person:
    def __init__(self, name, hours, rate):
        self.name = name
        self.hours = hours
        self.rate = rate
        
    def pay(self):
        return self.hours * self.rate
    
@singleton
class Spam:
    def __init__(self, val):
        self.attr = val
        
bob = Person('Bob', 40, 10)
print(bob.name, bob.pay())

sue = Person('Sue', 50, 15)
print(sue.name, sue.pay())

X = Spam(1)
Y = Spam(2)
print(X.attr, Y.attr)

现在,当稍后使用PersonSpam类来创建一个实例的时候,装饰器提供的包装逻辑层把实例构建调用指向了onCall,它反过来保证不管进行了多少次构造调用,每个类只有单独一个实例。这段代码的输出如下:

Bob 400
Bob 400
1 1

参考文献:
[1] Mark Lutz. Python学习手册[M]. 机械工业出版社, 2018.

<think>我们正在寻找关于Python面向对象高阶编程的练习题目,重点包括:继承、多态、封装、访问控制、装饰器。 根据用户提供的引用,我们了解到: 引用[1]:面向对象三要素——封装、继承与多态。 引用[2]和[3]:装饰器的概念、实现原理(函数嵌套、闭包)以及应用场景(日志、性能测试、事务处理等)。 因此,我们将设计一些练习题目,涵盖这些主题,并按照从基础到高级的顺序排列。 注意:由于是练习题目,每个题目应包含问题描述和参考代码(或要求用户实现的部分),以及必要的解释。 设计思路: 1. 封装与访问控制:设计一个,展示私有属性、保护属性和公有属性,以及使用@property装饰器。 2. 继承与多态:设计一个继承体系,要求子重写父方法,展示多态行为。 3. 装饰器:设计一个自定义装饰器,用于增强方法函数,例如日志记录、性能测试等。 4. 综合应用:设计一个稍复杂的系统,整合封装、继承、多态和装饰器。 下面给出具体的练习题目:</think>### Python面向对象高阶编程练习题目 以下练习涵盖**继承、多态、封装、访问控制、装饰器**等核心概念,每个题目均包含需求描述和参考实现框架。 --- #### **练习1:银行账户系统(封装与访问控制)** **需求**: 1. 实现`Account`基,包含私有属性`_balance`(余额)和`account_id`(账户ID) 2. 使用`@property`和`@setter`装饰器控制余额访问 3. 子`SavingsAccount`(储蓄账户)需保留最低余额100元 4. 子`CheckingAccount`(支票账户)允许透支 ```python class Account: def __init__(self, account_id, balance=0): self.account_id = account_id self._balance = balance # 私有属性 @property def balance(self): return self._balance def deposit(self, amount): if amount > 0: self._balance += amount return True return False def withdraw(self, amount): raise NotImplementedError("子需实现此方法") class SavingsAccount(Account): def withdraw(self, amount): if 0 < amount <= self._balance - 100: self._balance -= amount return True return False class CheckingAccount(Account): def withdraw(self, amount): if amount > 0: self._balance -= amount return True return False # 测试代码 savings = SavingsAccount("SA001", 500) savings.withdraw(450) # 失败(需保留100元) print(savings.balance) # 输出: 500 ``` --- #### **练习2:形状计算系统(继承与多态)** **需求**: 1. 基`Shape`定义抽象方法`area()`和`perimeter()` 2. 子`Circle`、`Rectangle`实现具体计算逻辑 3. 通过多态统一调用不同子方法 ```python from abc import ABC, abstractmethod import math class Shape(ABC): @abstractmethod def area(self): pass @abstractmethod def perimeter(self): pass class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return math.pi * self.radius ** 2 def perimeter(self): return 2 * math.pi * self.radius class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height def perimeter(self): return 2 * (self.width + self.height) # 多态演示 shapes = [Circle(5), Rectangle(4, 6)] for shape in shapes: print(f"面积: {shape.area():.2f}, 周长: {shape.perimeter():.2f}") # 输出: # 面积: 78.54, 周长: 31.42 # 面积: 24.00, 周长: 20.00 ``` --- #### **练习3:日志记录装饰器装饰器应用)** **需求**: 1. 创建装饰器`log_execution`,记录方法的执行时间和参数 2. 将装饰器应用于银行账户的`deposit`和`withdraw`方法 3. 使用闭包捕获函数执行状态 ```python import time def log_execution(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"[LOG] 执行 {func.__name__},耗时: {end-start:.4f}秒,参数: {args[1:]}") return result return wrapper class Account: # ... (同练习1的Account) @log_execution def deposit(self, amount): # ... (同练习1) @log_execution def withdraw(self, amount): # ... (同练习1) # 测试 acc = Account("ACC001", 1000) acc.deposit(500) # 输出: [LOG] 执行 deposit,耗时: 0.0001秒,参数: (500,) ``` --- #### **练习4:访问控制强化(私有属性与异常处理)** **需求**: 1. 在`Account`中添加私有属性`_transaction_history`(交易历史) 2. 通过方法`add_transaction()`添加记录(禁止直接访问) 3. 使用`@property`提供只读访问接口 ```python class Account: def __init__(self, account_id): self.account_id = account_id self._balance = 0 self._transaction_history = [] # 私有属性 def add_transaction(self, action, amount): if amount <= 0: raise ValueError("金额必须为正数") self._transaction_history.append((action, amount)) @property def transaction_history(self): return self._transaction_history.copy() # 返回副本防止外部修改 # 测试 acc = Account("ACC002") acc.add_transaction("存款", 500) print(acc.transaction_history) # 输出: [('存款', 500)] acc.transaction_history.clear() # 失败(返回的是副本) ``` --- #### **练习5:装饰器实现单例模式** **需求**: 1. 创建装饰器`singleton`,确保被装饰的只有一个实例 2. 测试`DatabaseConnection`在不同位置调用时返回相同实例 ```python def singleton(cls): instances = {} def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class DatabaseConnection: def __init__(self, db_url): self.db_url = db_url # 测试 conn1 = DatabaseConnection("mysql://localhost") conn2 = DatabaseConnection("postgres://remote") print(conn1 is conn2) # 输出: True(相同实例) print(conn1.db_url) # 输出: mysql://localhost ``` --- ### 关键知识点总结 1. **封装**:通过私有属性(`_balance`)和访问控制器(`@property`)保护数据完整性[^1] 2. **多态**:子重写父抽象方法(`Shape`的`area()`),实现统一接口不同行为[^1] 3. **装饰器**:基于闭包实现横切关注点(如日志、性能监控)与业务逻辑分离[^2][^3] 4. **访问控制**:利用私有属性和异常处理强制数据操作规则 5. **设计模式**:装饰器实现单例模式,控制对象创建过程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

von Neumann

您的赞赏是我创作最大的动力~

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

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

打赏作者

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

抵扣说明:

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

余额充值