Skip to main content

Python 面向对象

Python 定制类

在Python中,通过定义特殊方法(以双下划线 __ 开头和结尾的方法,也称为魔术方法),我们可以定制类的行为,使其支持内置操作,如初始化、打印、迭代、比较等。

一、对象生命周期相关

这些方法控制对象的创建、初始化和销毁,是定制类的起点。

1. __init__ - 构造器

__init__ 方法在创建类实例时自动调用,用于初始化对象的属性。

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

person = Person("Alice", 30)
print(person.name)  # 输出: Alice
print(person.age)   # 输出: 30

2. __del__ - 析构器

__del__ 方法在对象被销毁(垃圾回收)时调用,可以用来清理资源。

class Person:
    def __del__(self):
        print("Person object is being destroyed")

person = Person()
del person  # 输出: Person object is being destroyed

二、对象显示相关方法

这些方法定义对象如何被表示为字符串,通常用于调试或用户交互。

1. __str__ - 字符串显示

__str__ 方法定义了 print() 函数调用时对象的字符串表示。

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

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

person = Person("Alice", 30)
print(person)  # 输出: Person(name=Alice, age=30)

2. __repr__ - 字符串显示

__repr__ 方法返回对象的“官方”字符串表示,通常用于调试,目标是可以通过 eval() 重建对象。

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

person = Person("Alice", 30)
print(repr(person))
# 输出: Person('Alice', 30) 这个字符串后期可以用 eval 创建实例

比如在控制台中默认显示的就是 __repr__

>>> 
>>> class Person:
...     def __init__(self, name, age):
...         self.name = name
        self.age = age
...         self.age = age
...     def __str__(self):
...         return f"name:{self.name}, age:{self.age})"
...     def __repr__(self):
...         return f"Person('{self.name}', {self.age})"
... 
>>> 
>>> Person("Alice", 30)
Person('Alice', 30)
>>> 
>>> 
>>> print(Person("Alice", 30))
name:Alice, age:30)
>>> 
>>> 

三、容器操作相关方法

这些方法使类像内置容器(如列表、字典)一样支持长度计算、索引和迭代。

1. __len__ - 返回对象的长度

__len__ 方法允许使用 len() 函数获取对象的长度。

class MyList:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)

my_list = MyList([1, 2, 3, 4])
print(len(my_list))  # 输出: 4

2. __getitem____setitem__

__getitem__ 用于访问元素,__setitem__ 用于设置元素,支持 obj[key] 语法。

class MyList:
    def __init__(self, items):
        self.items = items

    def __getitem__(self, index):
        return self.items[index]

    def __setitem__(self, index, value):
        self.items[index] = value

my_list = MyList([1, 2, 3, 4])
print(my_list[2])  # 输出: 3
my_list[2] = 10
print(my_list[2])  # 输出: 10

3. __iter____next__ - 迭代

__iter__ 返回迭代器,__next__ 定义迭代行为,使对象支持 for 循环。

class MyList:
    def __init__(self, items):
        self.items = items
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < len(self.items):
            item = self.items[self.index]
            self.index += 1
            return item
        else:
            raise StopIteration

my_list = MyList([1, 2, 3, 4])
for item in my_list:
    print(item)  # 输出: 1 2 3 4

四、比较与哈希相关方法

这些方法定义对象的相等性比较和哈希行为,适用于集合或字典键。

1. __eq__ - 相等性比较

__eq__ 方法定义两个对象是否相等,使用 == 运算符时调用。

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

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

person1 = Person("Alice", 30)
person2 = Person("Alice", 30)
print(person1 == person2)  # 输出: True

2. __hash__ - 哈希值

__hash__ 方法返回对象的哈希值,使对象可用作字典键或集合元素。需与 __eq__ 一致。

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

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

    def __hash__(self):
        return hash((self.name, self.age))

person = Person("Alice", 30)
my_dict = {person: "data"}
print(my_dict[person])  # 输出: data

五、其他高级定制方法

这些方法提供更多功能,如上下文管理、可调用对象等。

1. __call__ - 对象可调用

__call__ 方法使对象可以像函数一样被调用。

class MyCallable:
    def __call__(self, *args, **kwargs):
        print("Called with:", args, kwargs)

callable_obj = MyCallable()
callable_obj(1, 2, a=3, b=4)  # 输出: Called with: (1, 2) {'a': 3, 'b': 4}

对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,使用 callable方法可以判断。

class MyCallable:
    def __call__(self, *args, **kwargs):
        print("Called with:", args, kwargs)


print(callable(MyCallable))    # True
print(callable([1, 2, 3, 4]))  # False

2. __enter____exit__

__enter____exit__ 方法支持 with 语句,用于上下文资源管理。

class MyContext:
    def __enter__(self):
        print("Entering the context")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")

with MyContext() as ctx:
    print("Inside the context")

# 输出:
# Entering the context
# Inside the context
# Exiting the context

六、性能优化相关属性

1. __slots__ - 限制属性

__slots__ 属性限制实例的动态属性,减少内存使用并提高访问速度。

class Person:
    __slots__ = ['name', 'age']

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

person = Person("Alice", 30)
print(person.name)  # 输出: Alice
person.address = "Unknown"  # 这会引发 AttributeError

__slots__ 的作用

  1. 节省内存
    1. 默认情况下,Python 类的实例使用 __dict__ 存储属性,这是一个字典,占用较多内存。
    2. 使用 __slots__ 后,Python 不会为实例创建 __dict__,而是使用固定大小的内存空间来存储属性,从而显著减少内存占用。
    3. 尤其在需要创建大量实例时,内存节省效果明显。
  2. 限制属性动态添加
    1. __slots__ 防止实例动态添加不在 __slots__ 定义中的属性,有助于避免因拼写错误或意外添加属性导致的 bug。
    2. 提高代码的健壮性和可维护性。
  3. 提高属性访问速度
    1. 由于 __slots__ 消除了 __dict__ 的开销,属性访问不再需要通过字典查找,而是直接通过偏移量访问,速度略有提升。

总结

通过以上特殊方法和属性,我们可以全面定制类的行为:

  • 生命周期__init____del__ 控制对象的创建和销毁。
  • 表示__str____repr__ 定义对象的字符串形式。
  • 容器操作__len____getitem____setitem____iter____next__ 模拟容器行为。
  • 比较与哈希__eq____hash__ 支持比较和哈希操作。
  • 高级功能__call____enter__/__exit__ 提供额外的灵活性。
  • 性能优化__slots__ 提高效率。