Python 笔记 — 单例模式和魔术方法

本文详细介绍了Python中的单例模式,包括概念、优点、使用场景以及多种实现方法如__new__、classmethod、装饰器等,并探讨了魔术方法如init、new、del等在类中的特殊作用。

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

一、单例模式

1、概念

让所有类在实例化时,指向同一个内存地址,称之为单例模式

是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。

单例模式保证了在程序的不同位置都可以且仅可以取到同一个对象实例

如果实例不存在,会创建一个实例,如果已存在,就会返回这个实例。

因为单例是一个类,所以也可以为其提供相应的操作方法,以便于对这个实例进行管理。

2、优点

节省内存空间。因为产生不同的对象,会产生不同的内存地址,造成资源的浪费。

单例模式和全局变量

全局变量可能会有名称空间的干扰,如果有重名的可能会被覆盖。

3、使用

使用场景:当类只要求一个对象的时候,就用单例模式。

如何实现:使构造函数私有化,并创建一个静态方法来完成对象的初始化。

4、实现方法

  • 通过 __new__ 实现
  • 通过@classmethod
  • 通过装饰器实现
  • 通过导入模块时实现
  • 通过元类实现

5、运用

  • 任务管理器
  • 回收站
  • 项目日志
  • 多线程的线程池的设计

6、内存地址

在计算机中,通常使用十六进制表示内存地址。

获取数据引用地址的语法格式:

id(数值) 
id(变量名)

id 函数:Python 的内置函数,用来查看对象的身份,也就是内存地址

引用是指变量指向数据存储空间的现象。

引用的特点

相同的数据在内存空间中仅占用一个存储空间,不同的变量使用相同的数据则指向相同的存储空间。

变量赋值修改的不是变量值,而是变量的引用地址。

每个数字在内存中都要占用一个存储空间,而变量存储的是这个空间的地址。

7、通过 __new__ 实现

class People(object):
    def __init__(self):
        '''初始化方法 初始化参数'''
        '''new方法不去开辟空间,不去引用,init 就找不到地方'''
        print('这是init方法')

    def __new__(cls, *args, **kwargs):
        '''一般不用,会用到单例模式中'''
        # print('这是new方法')  # 这是自己的 new 方法
        # 重写了没有这个功能,调用父类的方法来使用功能(开辟空间)
        # 重写了就需要返回父类的 new 方法程序才能继续往下走
        # return super().__new__(cls)  # 父类的 new 方法
        return object.__new__(cls)

a = People()
b = People()
# id 指向内存空间的地址,一样表示同一个空间,不同表示不同空间
print(id(a))
print(id(b))

# 运行结果:
# 这是init方法
# 这是init方法
# 2225781388432
# 2225781389536
# new 使用空间 引用
class People(object):
    _a = None  # 第二次实例化的时候就不为空,为super().__new__(cls)
    def __new__(cls, *args, **kwargs):
        if not cls._a:  # 不是空的就会执行,只会执行一次,只会引用一次空间
            # _a 等于父类的 new 方法
            cls._a = super().__new__(cls)  # 执行这一句之后 _a 就不为空了
        # 返回父类的 new 方法,和返回 _a 都可以
        # cls._a 只会执行一次,只会引用一次空间
        return cls._a

    def __init__(self,name):
        # print(name)
        self.name = name
    def eat(self):
        print(self.name)

a = People('张三')
a.eat()
b = People('李四')
print(id(a))
print(id(b))
a.eat()
b.eat()

# 运行结果:
# 张三
# 2506976886208
# 2506976886208
# 李四
# 李四

8、通过 @classmethod

class Singleton:
    _instance = None

    def __new__(cls, data):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

    def __init__(self, data):
        self.data = data

    @classmethod
    def get_instance(cls, data):
        if cls._instance is None:
            cls._instance = cls(data)
        return cls._instance

# 创建两个实例
singleton1 = Singleton.get_instance("Instance 1")
singleton2 = Singleton.get_instance("Instance 2")

# 检查两个实例是否相等
print(singleton1 is singleton2)  # 输出: True

# 输出实例数据
print(singleton1.data)  # 输出: Instance 1
print(singleton2.data)  # 输出: Instance 1

9、通过装饰器实现

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 Singleton:
    def __init__(self, data):
        self.data = data

# 创建两个实例
singleton1 = Singleton("Instance 1")
singleton2 = Singleton("Instance 2")

# 检查两个实例是否相等
print(singleton1 is singleton2)  # 输出: True

# 输出实例数据
print(singleton1.data)  # 输出: Instance 1
print(singleton2.data)  # 输出: Instance 1

10、通过导入模块时实现

# singleton_module.py
class Singleton:
    def __init__(self, data):
        self.data = data

# 创建单一实例并导出
singleton_instance = Singleton("Instance 1")
# main.py
import singleton_module

# 导入模块时,会创建单一实例
singleton1 = singleton_module.singleton_instance
singleton2 = singleton_module.singleton_instance

# 检查两个实例是否相等
print(singleton1 is singleton2)  # 输出: True

# 输出实例数据
print(singleton1.data)  # 输出: Instance 1
print(singleton2.data)  # 输出: Instance 1

11、hasattr() 函数

用于检查对象是否具有指定的属性。如果对象拥有该属性,它返回 True,否则返回 False。

语法:

hasattr(object, name)

object:要检查属性的对象。

name:要检查的属性名(字符串)。

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

person = Person("Alice", 30)

# 检查对象是否有 'name' 属性
has_name = hasattr(person, 'name')

# 检查对象是否有 'address' 属性
has_address = hasattr(person, 'address')

print(has_name)  # 输出:True
print(has_address)  # 输出:False

12、getattr() 函数

用于获取对象的属性的值。如果对象具有指定的属性,它返回该属性的值。如果属性不存在,则可以提供一个默认值,如果未提供默认值,则会引发 AttributeError 异常。

语法:

getattr(object, name, default)

object:要获取属性的对象。

name:要获取的属性名(字符串)。

default(可选):如果属性不存在时返回的默认值。

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

person = Person("Bob", 25)

# 获取 'name' 属性的值
name = getattr(person, 'name')

# 获取 'address' 属性的值,如果不存在返回默认值 'Unknown'
address = getattr(person, 'address', 'Unknown')

print(name)  # 输出:Bob
print(address)  # 输出:Unknown

二、魔术方法

也称为特殊方法双下划线方法,是在类中以双下划线开头和结尾的方法,用于定义类的行为和操作,例如初始化、字符串表示、比较、迭代等。这些方法在 Python 中具有特殊用途,可以自定义类的行为。

1、init

用于初始化对象的属性。

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.mileage = 0

    def start(self):
        print(f"{self.year}{self.make}{self.model}启动了。")

    def drive(self, miles):
        print(f"行驶了{miles}英里。")
        self.mileage += miles

    def stop(self):
        print(f"{self.year}{self.make}{self.model}停止了。")

# 创建汽车对象并使用中文字符串
car1 = Car("奥迪", "A6", 2022)
car2 = Car("宝马", "X5", 2023)

car1.start()  # 输出: 2022年奥迪A6启动了。
car2.drive(50)  # 输出: 行驶了50英里。
car1.stop()  # 输出: 2022年奥迪A6停止了。

2、new

用于创建一个实例对象

class ImmutableClass:
    def __new__(cls, value):
        instance = super(cls, cls).__new__(cls)
        instance.value = value
        return instance

    def __init__(self, value):
        # 这里的初始化方法可以省略,因为属性已经在 __new__ 中设置
        pass

    def __setattr__(self, name, value):
        raise AttributeError("Attributes of ImmutableClass are immutable")

obj = ImmutableClass(42)
print(obj.value)  # 输出: 42

# 尝试修改属性会引发异常
obj.value = 100  # 报错,AttributeError: Attributes of ImmutableClass are immutable
  • 创建不可变对象:可以使用 __new__ 方法来确保对象的属性不能被修改。
  • 实现单例模式:__new__ 方法可以用于确保只创建一个类的实例。
  • 定制对象创建行为:可以在 __new__ 中添加额外的逻辑,以根据特定条件选择性地创建对象。

3、del

用于在对象被销毁(垃圾回收)之前执行一些清理工作。也被称为析构方法

当对象不再被引用,或引用计数降为零时,Python 的垃圾回收机制会自动销毁对象。

在对象销毁之前,如果存在 __del__ 方法,Python 会调用这个方法。

class MyObject:
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print(f"对象 {self.name} 即将被销毁")

# 创建对象
obj1 = MyObject("Object 1")
obj2 = MyObject("Object 2")

# 删除对象引用
del obj1
del obj2

# 输出:
# 对象 Object 1 即将被销毁
# 对象 Object 2 即将被销毁

4、call

允许将对象当作函数来调用。

class Counter:
    def __init__(self):
        self.count = 0

    def __call__(self):
        self.count += 1
        return self.count

# 创建可调用的对象
counter = Counter()

# 调用对象,增加计数并返回新值
print(counter())  # 输出: 1
print(counter())  # 输出: 2
print(counter())  # 输出: 3

5、str

用于定义对象的 “字符串表示”。
通过实现此方法,可以返回一个描述对象的字符串。这个字符串可以包括对象的属性值、状态信息或其它有关对象的重要信息。

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

    def __str__(self):
        return f"姓名:{self.name}, 年龄:{self.age}"

# 创建 Person 对象
person = Person("Alice", 30)

# 打印对象,会自动调用 __str__ 方法
print(person)  # 输出: 姓名:Alice, 年龄:30

6、repr

用于定义对象的 “官方字符串表示”。
通过实现此字符串,该字符串是一个合法的 Python 表达式,用于创建与原对象相同状态的新对象。通常情况下,这个字符串应该包括初始化对象所需的信息,以便精确地重建对象。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

# 创建 Point 对象
point = Point(2, 3)

# 显示对象时,会自动调用 __repr__ 方法
print(point)  # 输出: Point(2, 3)

7、doc

是一个包含对象文档字符串的特殊属性。
文档字符串是对象的描述性文本,通常用于解释对象的目的、用法和其它相关信息。

class Rectangle:
    """
    这是一个矩形类的文档字符串。

    该类表示一个矩形,包括宽度和高度属性。
    """

    def __init__(self, width, height):
        """
        初始化矩形对象。

        Args:
            width (float): 矩形的宽度。
            height (float): 矩形的高度。
        """
        self.width = width
        self.height = height

    def area(self):
        """
        计算矩形的面积。

        Returns:
            float: 矩形的面积。
        """
        return self.width * self.height


# 获取类的文档字符串
class_docstring = Rectangle.__doc__

# 获取方法的文档字符串
init_docstring = Rectangle.__init__.__doc__
area_docstring = Rectangle.area.__doc__

# 打印文档字符串
print("类的文档字符串:")
print(class_docstring)

print("\n__init__ 方法的文档字符串:")
print(init_docstring)

print("\narea 方法的文档字符串:")
print(area_docstring)

8、module

是一个包含模块名称的特殊属性,用于获取包含定义对象的模块的名称

# my_module.py
def my_function():
    pass

class MyClass:
    def __init__(self):
        pass
# main.py
import my_module

# 获取函数的模块名称
function_module = my_module.my_function.__module__

# 获取类的模块名称
class_module = my_module.MyClass.__module__

# 打印模块名称
print("函数所属的模块:", function_module)
print("类所属的模块:", class_module)

# 运行结果:
# 函数所属的模块: my_module
# 类所属的模块: my_module

9、class

用于获取对象所属的。通过访问对象的此所属的类的引用。

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

class Cat:
    def __init__(self, name):
        self.name = name

# 创建 Dog 和 Cat 对象
dog = Dog("Buddy")
cat = Cat("Whiskers")

# 使用 __class__ 属性检查对象的类
if dog.__class__ == Cat:
    print("这是一只猫")
elif dog.__class__ == Dog:
    print("这是一只狗")

# 动态修改对象的类
dog.__class__ = Cat

# 现在 dog 对象的类变成了 Cat
if dog.__class__ == Cat:
    print("现在,这是一只猫")

# 运行结果:
# 这是一只狗
# 现在,这是一只猫

10、dict

是一个字典,它包含了一个对象的命名空间中的所有属性和方法。每个对象都有一个 __dict__ 属性,可以用来查看和修改对象的属性。

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

    def greet(self):
        print(f"你好,我的名字是 {self.name} ,我今年 {self.age} 岁了.")

# 创建一个 Person 对象
person = Person("Alice", 18)

# 使用 __dict__ 查看对象的属性和方法
attributes_and_methods = person.__dict__

# 打印属性和方法
print(attributes_and_methods)  # 输出:{'name': 'Alice', 'age': 18}

11、getattr

用于自定义对象的属性访问行为。
当访问一个对象的属性,但该属性不存在时,Python 会调用该对象的 __getattr__ 方法,允许在属性不存在时执行自定义操作,例如返回计算后的值或引发异常。

使用场景

  • 惰性加载属性:可以在访问属性时根据需要加载它们的值,而不是在对象初始化时就加载所有属性。

  • 动态属性:根据运行时条件计算属性的值,而不是事先定义所有可能的属性。

  • 属性的默认值:可以定义一个通用的 __getattr__ 方法来返回默认值,以便在访问不存在的属性时提供合理的默认值。

class MyObject:
    def __init__(self):
        self.data = {'name': 'Alice', 'age': 30}

    def __getattr__(self, name):
        if name in self.data:
            return self.data[name]
        else:
            raise AttributeError(f"'MyObject' 对象没有属性 '{name}'")

# 创建一个 MyObject 对象
obj = MyObject()

# 访问已存在的属性
print(obj.name)  # 输出: Alice
print(obj.age)   # 输出: 30

# 访问不存在的属性,将调用 __getattr__ 方法
try:
    print(obj.country)  # 输出: AttributeError: 'MyObject' 对象没有属性 'country'
except AttributeError as e:
    print(e)  # 输出: 'MyObject' 对象没有属性 'country'

12、setattr

用于自定义对象属性的赋值行为。
当为对象的属性赋值时,Python 会调用对象的 __setattr__ 方法,允许在属性被赋值时执行自定义操作。

使用场景

  • 属性验证:可以在属性赋值前执行验证,以确保属性的值符合某些条件或规则。

  • 计算属性:可以在设置属性时执行计算,例如,将两个属性的值相加并将结果赋给一个新属性。

  • 记录属性更改:可以在属性被赋值时记录属性的更改,以便跟踪对象的状态。

class MyObject:
    def __init__(self):
        self.data = {}  # 创建一个空字典来存储属性

    def __setattr__(self, name, value):
        # 使用 __setattr__ 方法自定义属性设置行为
        if name == "age":
            if value < 0:
                raise ValueError("年龄不能为负数")
        # 调用父类的 __setattr__ 方法来设置属性
        super().__setattr__(name, value)

# 创建一个 MyObject 对象
obj = MyObject()

# 设置属性
obj.name = "Alice"
obj.age = 30

# 试图设置无效的属性值将引发异常
try:
    obj.age = -5
except ValueError as e:
    print(e)

# 访问属性
print(obj.name)  # 输出: Alice
print(obj.age)   # 输出: 30

13、delattr

用于自定义对象属性的删除行为。
当删除对象的属性时,Python 会调用对象的 __delattr__ 方法,允许在属性被删除时执行自定义操作。

使用场景

  • 属性删除验证:可以在属性被删除之前执行验证,以确保属性可以被删除。

  • 记录属性删除:可以在属性被删除时记录删除操作,以便跟踪对象的状态变化。

class MyObject:
    def __init__(self):
        self.data = {'name': 'Alice', 'age': 30}

    def __delattr__(self, name):
        if name in self.data:
            del self.data[name]
        else:
            raise AttributeError(f"'MyObject' 对象没有属性 '{name}'")

# 创建一个 MyObject 对象
obj = MyObject()

# 删除属性
del obj.name

# 删除不存在的属性,将调用 __delattr__ 方法
try:
    del obj.country
except AttributeError as e:
    print(e)

# 打印剩余属性
print(obj.data)  # 输出: {'age': 30}

记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值