Python的面相对象

Class

用class关键字声明类;用Animal()生成一个对象。

class Animal:
    pass

cat: Animal = Animal()

Attribute和Property

Python动态语言特性,设计的attribute不一定要在class中定义的,比如下面的代码,可以有了cat这个对象,再给cat添加name属性。

cat.name = "Foo"
print(cat.name)

也可以在class中定义attribute,下面的sel.name就定义了name这个属性。

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

可以通过__slots__方法,限定类的属性,这样就不能随意添加新属性了,能节省内存。

class Point:
    __slots__ = ['x', 'y']

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

p = Point(1, 2)
p.z = 3  # ❌ 抛出 AttributeError

Python attribute的没有真正的访问限制,是通过命名规则来实现。一个下划线是protected,但是IDE只是通过命名规则来检测并提醒;两个下划线是private,直接调用编译就会出错了,但是仍可以通过._ClassName__attribute来获取。

class Animal:
    def __init__(self, name, age):
        self._hidden_name = name
        self.__age = age

cat: Animal = Animal("Foo", 16)
cat._hidden_name # ⚠️ IDE Warning, _hidden_name是protected
cat.__age        # ❌ Error, __age是private
cat._Animal__age # 仍能通过这种方式获取private的属性

为了给private attribute提供外部读写的途径,Python有Property的概念。通过@property声明

class Animal:
    def __init__(self, hidden_name):
        self.__hidden_name = hidden_name

    @property
    def name(self):
        return self.hidden_name

    @name.setter
    def name(self, input_name):
        self.hidden_name = input_name

cat: Animal = Animal("Foo")
print(cat.name)  # 会调用property的name函数
cat.name = "Bar" # 会调用setter的name函数

Method

Python中实例方法第一个参数为self,即使没有需要传递的参数,也得搞个self。

class Animal:
    def __init__(self, hidden_name):
        self.__hidden_name = hidden_name

    def eat(self):
        pass

静态方法通过添加@staticmethod实现,静态方法不需要加self

class MathUtils:
    @staticmethod
    def add(a: int, b: int) -> int:
        return a + b

MathUtils.add(1 + 2)

方法的访问权限和属性类似,通过添加下划线来实现private,但仍可通过._ClassName__method()访问。

Instance成员 vs Class成员

下面代码中,total_users是Class attribute,username是Instance attribute。Class属性的引用可以通过类名,也可以通过实例;但是修改必须通过类名引用。

class User:
    # 类属性:记录创建了多少个用户
    total_users = 0

    def __init__(self, username):
        self.username = username         # 实例属性
        User.total_users += 1            # 修改类属性

    def show_info(self):
        print(f"Username: {self.username}")

# 创建几个用户
u1 = User("Alice")
u2 = User("Bob")
u3 = User("Charlie")

# 查看类属性
print(User.total_users)      # 输出: 3

# 也可以通过实例访问类属性
print(u1.total_users)        # 输出: 3
print(u2.total_users)        # 输出: 3

下面代码中,添加了@classmethod的from_json是Class方法,第一个参数习惯用cls,代表类。Class方法一般用于创建、操作类本身的逻辑,比如工厂方法。

class User:
    def __init__(self, username, email, created_at=None):
        self.username = username
        self.email = email
        self.created_at = created_at if created_at else datetime.now()

    @classmethod
    def from_json(cls, data):
        """
        工厂方法:从字典数据(如 JSON)构建 User 实例
        """
        return cls(
            username=data.get('username'),
            email=data.get('email'),
            created_at=datetime.fromisoformat(data.get('created_at'))
        )

Magic Method

Python中定义了一些两个下划线开始和结尾的类方法,他们有着自己特殊的能力。

initnew

init: 初始化器,在对象被创建后,对对象进行初始化。

new: 创建一个对象,注意和__init__的区别。它是类的方法,第一个参数是cls,控制对象的创建过程,必须返回一个对象实例。最常用的地方是创建单例。

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("__new__ 被调用")
        instance = super().__new__(cls)  # 创建实例
        return instance

    def __init__(self, value):
        print("__init__ 被调用")
        self.value = value

strrepr

这两个方法的返回值都是str

str: 类似于Java中的toString()方法,一般给用户看

repr: 是representation,literally representation,一般给程序员看,比如显示所有的属性

其它Magic Method

其它的Magic Method包括算术相关的、上下文管理相关、容器相关和属性访问等。完整的列表见 Python Special Methods

__add__(self, other)     # ➕的运算
__lt__(self, other)      # < 的运算
__getattr__(self, name)  # 访问不存在属性时调用
__len__(self)            # len(obj)
__enter__(self)          # 在 with 块开始时调用
__bool__(self)           # 返回布尔值(用于 if obj:)

Inheritance

Python的继承语法比较特别。Python可以继承多个父类。方法的重写是自动识别的,没有override关键字,直接定义同名方法即可。

class Pet:
    pass

class Animal:
	pass

class Cat(Animal, Pet):
	pass

Abstract Class

Python的抽象类有点抽象,需要引入额外的包。ABC是抽象类的基类,所有的抽象类都是继承ABC或者其后代类。@abstractmethod标记抽象方法。

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

Protocal

Protocol是一个特殊的类,定义一个Protocol需要继承Protocol这个类。一个类“实现”这个Protocol是完全隐式的,没有implement关键字,也不需要继承,只需要默默地实现Protocol中的方法和属性。

from typing import Protocol

class Printable(Protocol):
		pages: int
		
		def print(self):
				pass
				
		def recycle(self):
				pass
				
class Book:
		pages: int
		
		def __init__(self, pages:str):
				self.pages = pages
		
		def print(self):
				print("Printing book:", self.title)
				
		def recycle(self):
				print("Recycling book:", self.title)

book: Pritable = Book('Python')

Data Class

数据类,帮我们实现了init,eq,repr这些方法。

from dataclasses import dataclass

@dataclass
class Fruit:
		name: str
		colaries: int