OOP 面向对象
__new__ vs __init__
__new__ | __init__ | |
|---|---|---|
| 作用 | 创建对象(分配内存) | 初始化对象(设置属性) |
| 参数 | cls(类本身) | self(实例) |
| 返回值 | 必须返回实例 | 无返回值(None) |
| 调用顺序 | 先 | 后 |
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, value):
self.value = value
a = Singleton(1)
b = Singleton(2)
print(a is b) # True(同一对象)
print(a.value) # 2(__init__ 被调用了两次)魔术方法(Magic Methods)
常用魔术方法
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __len__(self):
return 2
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
def __getitem__(self, index):
return (self.x, self.y)[index]
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # (4, 6)
print(len(v1)) # 2
print(v1[0]) # 1__repr__ vs __str__
__repr__:开发者视角,repr(obj),交互式解释器显示,应能重建对象__str__:用户视角,str(obj),print()调用- 只定义
__repr__时,str()也会用它;反之不行
继承与 MRO
MRO(方法解析顺序)
Python 3 使用 C3 线性化算法确定多继承时方法查找顺序。
class A:
def method(self): print("A")
class B(A):
def method(self): print("B")
class C(A):
def method(self): print("C")
class D(B, C):
pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
D().method() # "B"(按 MRO 顺序,B 在 C 前)super() 的正确用法
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类 __init__
self.breed = breed
class GuideDog(Dog):
def __init__(self, name, breed, owner):
super().__init__(name, breed)
self.owner = owner@staticmethod vs @classmethod vs 实例方法
class MyClass:
class_var = "I'm a class variable"
def instance_method(self):
# 访问实例和类
print(f"Instance: {self}, Class: {self.__class__}")
@classmethod
def class_method(cls):
# 只能访问类,不能访问实例
# 常用于工厂方法、替代构造器
print(f"Class: {cls}")
return cls() # 创建实例
@staticmethod
def static_method(x, y):
# 既不能访问实例也不能访问类
# 相当于放在类里的普通函数
return x + y
# 工厂方法是 classmethod 最典型的用途
class Date:
def __init__(self, year, month, day):
self.year, self.month, self.day = year, month, day
@classmethod
def from_string(cls, date_string):
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day)
d = Date.from_string("2024-01-15")Metaclass(元类)
类也是对象,元类是创建类的类。
# 默认元类是 type
print(type(int)) # <class 'type'>
print(type(str)) # <class 'type'>
class MyClass:
pass
print(type(MyClass)) # <class 'type'>
# 用 type 动态创建类
Dog = type("Dog", (object,), {"bark": lambda self: "Woof!"})
dog = Dog()
print(dog.bark()) # "Woof!"自定义元类:
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self):
self.connection = "connected"
db1 = Database()
db2 = Database()
print(db1 is db2) # True数据类(dataclass)
Python 3.7+ 简化 OOP 样板代码:
from dataclasses import dataclass, field
@dataclass(frozen=True) # frozen=True 使其不可变(可哈希)
class Point:
x: float
y: float
z: float = 0.0
tags: list = field(default_factory=list) # 可变默认值必须用 field
def distance(self):
return (self.x**2 + self.y**2 + self.z**2) ** 0.5
p = Point(1.0, 2.0)
print(p) # Point(x=1.0, y=2.0, z=0.0, tags=[])
print(p.distance()) # 2.23...面试高频题
Q: 如何实现单例模式?(有多种方式,面试官可能追问)
# 方式1:__new__
# 方式2:模块级变量(Python 模块天然是单例)
# 方式3:元类(见上)
# 方式4:装饰器
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 Config:
passQ: __slots__ 的作用?
class WithSlots:
__slots__ = ['x', 'y'] # 限制实例属性,用 tuple 而非 dict 存储
def __init__(self, x, y):
self.x = x
self.y = y
# 优点:内存占用更小(无 __dict__),属性访问更快
# 缺点:不能动态添加属性,不能有默认值(除非用描述符)
w = WithSlots(1, 2)
w.z = 3 # AttributeError!