Python 类全面总结

0 阅读19分钟

本文面向已有前端开发基础、正在学习 Python 的开发者。

从前端程序员更熟悉的使用场景开始理解 Python 的类。

如果你写过 JavaScript / TypeScript,可以先用这张表建立直觉:

Python 知识点前端脑内映射先记住
classclass定义一类对象的模板
实例new Person() 创建出来的对象类只是模板,实例才是真正拿来用的对象
__init__constructor初始化实例属性
selfthisPython 必须把当前实例显式写出来
实例属性this.name = name每个实例自己一份
实例方法类里的普通方法依赖某个具体对象,默认接收 self
类属性静态共享字段放在类身上,实例也能读到
类方法操作类本身的方法默认接收 cls
静态方法类命名空间里的工具函数不自动接收 selfcls
继承class Student extends Person子类复用、扩展父类能力
多态多个对象实现同一个方法调用方只管调同名方法
抽象类TS 抽象基类规定子类必须实现哪些方法
魔法方法特殊协议不手动调用,由 Python 在特定场景自动触发

这张表不用一次背完,先抓住 4 条主线:

  • 对象怎么创建:class、实例、__init__self
  • 数据放在哪里:实例属性、类属性、property
  • 行为怎么复用:实例方法、类方法、静态方法、继承
  • 规则怎么接入:多态、抽象类、魔法方法、object
Python 类系统

  class 类
     |
     +-- 创建实例
     |      |
     |      +-- __init__(self, ...)
     |      +-- 实例属性 self.name / self.age
     |
     +-- 方法
     |      |
     |      +-- 实例方法 self
     |      +-- 类方法 cls
     |      +-- 静态方法 无 self / cls
     |
     +-- 属性
     |      |
     |      +-- 实例属性:每个对象自己一份
     |      +-- 类属性:类身上一份,实例可以查到
     |
     +-- 继承
     |      |
     |      +-- 子类复用父类
     |      +-- super() 调父类
     |      +-- 方法重写
     |
     +-- 多态和抽象
     |      |
     |      +-- 同名方法,不同表现
     |      +-- 抽象类规定子类必须实现的方法
     |
     +-- 特殊协议
            |
            +-- __str__ / __len__ / __eq__
            +-- __lt__ / __gt__ / __getattr__
            +-- property
            +-- object

一、先用前端心智理解类

在前端里,我们经常会遇到这种对象:

const user = {
  name: "张三",
  age: 18,
  gender: "男",
  speak(msg) {
    console.log(`我叫${this.name},我想说:${msg}`);
  },
};

这个对象能跑,但是如果你要创建很多个用户,就会开始重复:

const user1 = {
  name: "张三",
  age: 18,
  gender: "男",
};

const user2 = {
  name: "李四",
  age: 22,
  gender: "女",
};

这时候你会想到用 class

class Person {
  constructor(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }

  speak(msg) {
    console.log(`我叫${this.name},年龄是${this.age},我想说:${msg}`);
  }
}

const p1 = new Person("张三", 18, "男");
p1.speak("好好学习");

Python 里的类也是同一个思路,只是写法不同。

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

    def speak(self, msg):
        print(f'我叫{self.name},年龄是{self.age},性别是{self.gender},我想说:{msg}')


p1 = Person('张三', 18, '男')
p1.speak('好好学习')

一句话记住:

class 负责描述“这一类对象长什么样、能做什么”
实例负责表示“一个具体对象”

二、class:定义一类对象的模板

1. 基本写法

class Person:
    pass


p1 = Person()
print(p1)

class Person: 表示定义一个 Person 类。

pass 表示暂时什么都不写,只是占位,避免语法报错。

2. 类名通常使用大驼峰

class UserProfile:
    pass


class OrderItem:
    pass

这点和 TS 的类名习惯很像。

三、__init__:初始化实例属性

__init__ 是 Python 类里最常见的魔法方法。

它的作用不是“创建对象本身”,而是在对象创建好之后,给这个对象补充初始化数据。

你可以把它类比成 JS / TS 的 constructor

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


p1 = Person('张三', 18, '男')

print(p1.name)
print(p1.age)
print(p1.gender)

前端对照:

class Person {
  name: string;
  age: number;
  gender: string;

  constructor(name: string, age: number, gender: string) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
}

重点理解

def __init__(self, name, age, gender):

这里有 4 个参数,但调用的时候只传 3 个:

p1 = Person('张三', 18, '男')

因为第一个参数 self 不需要手动传,Python 会自动把当前创建出来的实例传进去。

所以可以这样理解:

Person('张三', 18, '男')
  -> Python 创建一个实例对象
  -> 自动调用 __init__
  -> 自动把实例对象传给 self
  -> name = '张三'
  -> age = 18
  -> gender = '男'

四、self:Python 里的 this

self 就是当前实例对象。

在 JS / TS 里,你会写:

this.name = name;

在 Python 里,你会写:

self.name = name

区别是:

  • JS / TS 的 this 是隐式存在的
  • Python 的 self 必须显式写在方法第一个参数上
class Person:
    def __init__(self, name):
        self.name = name

    def say_name(self):
        print(self.name)


p1 = Person('张三')
p2 = Person('李四')

p1.say_name()
p2.say_name()

当调用 p1.say_name() 时:

p1.say_name()
  -> Python 找到 Person 类上的 say_name 方法
  -> 自动把 p1 传给 self
  -> self.name 就是 p1.name

当调用 p2.say_name() 时:

p2.say_name()
  -> self 变成 p2
  -> self.name 就是 p2.name

所以 self 不是关键字,只是约定俗成的名字。

你理论上可以写成这样:

class Person:
    def __init__(abc, name):
        abc.name = name

但不要这么写。Python 社区默认都写 self,这属于可读性规范。

五、实例属性:每个对象自己一份

通过 self.xxx = value 添加到实例上的属性,叫实例属性。

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


p1 = Person('张三', 18, '男')
p2 = Person('李四', 22, '女')

print(p1.name)
print(p2.name)

p1.namep2.name 是两份不同的数据,互不影响。

p1.name = '张三丰'

print(p1.name)
print(p2.name)

创建实例后,也可以临时新增属性

p1.address = '杭州'

print(p1.address)
print(p1.__dict__)

__dict__ 可以查看当前实例身上实际保存了哪些属性。

print(p1.__dict__)

可能输出:

{'name': '张三丰', 'age': 18, 'gender': '男', 'address': '杭州'}

从工程习惯来说,不建议到处临时给实例补属性。

更推荐把对象需要的属性统一写在 __init__ 里,这样更像 TS 里提前声明字段,后续维护时不会乱。

六、类属性:类身上共享的一份数据

类属性定义在类里面,但不写在某个方法里面。

class Person:
    planet = '地球'
    max_age = 120

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


p1 = Person('张三', 18)
p2 = Person('李四', 22)

print(Person.planet)
print(p1.planet)
print(p2.planet)

类属性保存在类身上,但实例访问时也能查到。

查找过程可以先这样理解:

p1.planet
  -> 先找 p1 自己身上有没有 planet
  -> 没有
  -> 再去 Person 类身上找 planet
  -> 找到 '地球'

实例属性会遮住同名类属性

p1.planet = '火星'

print(p1.planet)
print(p2.planet)
print(Person.planet)

这里不是把类属性改掉了,而是在 p1 自己身上新增了一个同名实例属性。

print(p1.__dict__)
print(Person.__dict__['planet'])

类属性适合放公共数据

class Order:
    status_pending = 'pending'
    status_paid = 'paid'
    status_cancelled = 'cancelled'

    def __init__(self, order_id, status):
        self.order_id = order_id
        self.status = status

它有点像 TS 里挂在类上的静态字段,适合表示同一类对象共享的常量、默认配置、状态枚举。

七、实例方法:默认给实例使用的方法

定义在类里面,并且第一个参数是 self 的方法,通常叫实例方法。

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

    def speak(self, msg):
        print(f'我叫{self.name},年龄是{self.age},性别是{self.gender},我想说:{msg}')


p1 = Person('张三', 18, '男')
p1.speak('好好学习')

虽然 speak 方法定义在类身上,但它主要给实例使用。

可以通过 Person.__dict__ 验证方法确实在类身上:

print(Person.__dict__)

但是调用时更推荐:

p1.speak('好好学习')

不推荐这样写:

Person.speak(p1, '好好学习')

这也能跑,因为你手动把 p1 传给了 self,但正常业务代码里没有必要这么写。

八、类方法:操作类本身的逻辑

类方法用 @classmethod 装饰。

它的第一个参数通常叫 cls,代表当前类本身。

class Person:
    planet = '地球'

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

    @classmethod
    def create_baby(cls, name):
        return cls(name, 0)

    @classmethod
    def show_planet(cls):
        print(cls.planet)


baby = Person.create_baby('小明')
print(baby.name)
print(baby.age)
Person.show_planet()

类方法常见用途:

  • 根据另一种参数形式创建实例
  • 操作类属性
  • 写和类强相关的工厂方法

前端类比:

class Person {
  static createBaby(name: string) {
    return new Person(name, 0);
  }
}

Python 里不用 static 关键字,而是用 @classmethod

九、静态方法:放在类命名空间里的工具函数

静态方法用 @staticmethod 装饰。

它不会自动接收 self,也不会自动接收 cls

class Person:
    @staticmethod
    def is_adult(age):
        return age >= 18


print(Person.is_adult(20))
print(Person.is_adult(12))

静态方法适合放“和这个类主题相关,但不需要访问实例和类本身”的工具逻辑。

比如:

class Order:
    @staticmethod
    def format_order_no(order_id):
        return f'ORDER-{order_id}'


print(Order.format_order_no(1001))

如果一个函数完全和类没有关系,不要硬塞进类里,直接写普通函数就行。

十、三种方法怎么选

方法类型第一个参数能访问实例属性能访问类属性典型场景
实例方法self可以可以通过 self 或类名访问依赖某个具体对象的行为
类方法cls不可以直接访问某个实例可以工厂方法、操作类级别数据
静态方法无自动参数不可以不可以自动访问与类主题相关的工具函数
class User:
    role = 'member'

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

    def say_name(self):
        print(self.name)

    @classmethod
    def get_role(cls):
        return cls.role

    @staticmethod
    def normalize_name(name):
        return name.strip()

可以先用这个判断:

需要访问当前对象的数据 -> 实例方法
需要访问当前类的数据 -> 类方法
什么都不需要,只是工具逻辑 -> 静态方法或普通函数

十一、继承:复用父类能力

继承表示一个类基于另一个类扩展。

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

    def speak(self, msg):
        print(f'我叫{self.name},年龄是{self.age},性别是{self.gender},我想说:{msg}')


class Student(Person):
    pass


stu = Student('张三', 18, '男')
stu.speak('我正在学习 Python')

前端对照:

class Person {}

class Student extends Person {}

Python 里写成:

class Student(Person):
    pass

子类扩展自己的属性

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


class Student(Person):
    def __init__(self, name, age, gender, stu_id, grade):
        super().__init__(name, age, gender)
        self.stu_id = stu_id
        self.grade = grade


stu = Student('张三', 18, '男', 'S001', '高一')

print(stu.name)
print(stu.stu_id)

super().__init__(name, age, gender) 表示调用父类的初始化逻辑。

你可以这样理解:

Student 需要 name / age / gender
但这些属性已经由 Person 管理
所以 Student 先调用 Person 的 __init__
再补充自己的 stu_id / grade

十二、方法重写:子类覆盖父类方法

当子类定义了和父类同名的方法,子类的方法会覆盖父类的方法。

class Person:
    def speak(self, msg):
        print(f'普通人说:{msg}')


class Student(Person):
    def speak(self, msg):
        print(f'学生说:{msg}')


stu = Student()
stu.speak('我要学习')

如果子类想保留父类逻辑,再追加自己的逻辑,可以使用 super()

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

    def speak(self, msg):
        print(f'我叫{self.name},我想说:{msg}')


class Student(Person):
    def __init__(self, name, stu_id):
        super().__init__(name)
        self.stu_id = stu_id

    def speak(self, msg):
        super().speak(msg)
        print(f'我的学号是{self.stu_id}')


stu = Student('张三', 'S001')
stu.speak('好好学习')

这个思路和前端里重写父类方法后再调用 super.xxx() 很像。

十三、多态:同一个方法名,不同对象有不同表现

多态的核心不是语法,而是一种调用方式:

调用方只关心对象有没有某个方法
不关心这个对象具体是什么类

前端里你可能写过这种代码:

interface PayChannel {
  pay(amount: number): void;
}

function submitPay(channel: PayChannel, amount: number) {
  channel.pay(amount);
}

submitPay 不需要知道传进来的是支付宝、微信还是银行卡,只要它有 pay 方法就行。

Python 里也可以这样理解。

class PayChannel:
    def pay(self, amount):
        print(f'支付 {amount} 元')


class WechatPay(PayChannel):
    def pay(self, amount):
        print(f'微信支付 {amount} 元')


class AliPay(PayChannel):
    def pay(self, amount):
        print(f'支付宝支付 {amount} 元')


def submit_pay(channel, amount):
    channel.pay(amount)


wechat_pay = WechatPay()
ali_pay = AliPay()

submit_pay(wechat_pay, 100)
submit_pay(ali_pay, 200)

这里的重点是:

submit_pay 调用的都是 channel.pay()
但是不同对象执行出来的行为不同

多态成立通常有三个条件:

  • 有一组对象
  • 这些对象有同名方法
  • 调用方通过同一个方法名调用它们

Python 还有一种常见说法叫“鸭子类型”。

意思是:不强制要求你继承某个父类,只要你长得像、有对应方法,就可以被当成这个角色使用。

class BankCardPay:
    def pay(self, amount):
        print(f'银行卡支付 {amount} 元')


bank_card_pay = BankCardPay()
submit_pay(bank_card_pay, 300)

BankCardPay 没有继承 PayChannel,但它也有 pay 方法,所以 submit_pay 仍然能正常工作。

前端心智可以这样记:

TS 更关心类型声明是否满足接口
Python 更关心运行时对象是否真的有这个方法

十四、抽象类:先定规范,再让子类实现

抽象类适合在这种场景使用:

你希望一批子类都必须实现同一个方法
如果子类忘记实现,就应该尽早报错

Python 使用 abc 模块定义抽象类。

from abc import ABC, abstractmethod


class PayChannel(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

ABCAbstract Base Class 的缩写。一个类继承了 ABC,就表示它可以作为抽象基类来使用。

@abstractmethod 表示这个方法是抽象方法,子类必须实现。

class WechatPay(PayChannel):
    def pay(self, amount):
        print(f'微信支付 {amount} 元')


wechat_pay = WechatPay()
wechat_pay.pay(100)

如果子类没有实现 pay,就不能正常创建实例。

class BadPay(PayChannel):
    pass


# bad_pay = BadPay() # 报错:没有实现抽象方法 pay

抽象类和普通父类的区别:

写法作用子类是否必须实现
普通父类方法可以给默认实现不一定
抽象方法只定义规范必须

前端类比:

abstract class PayChannel {
  abstract pay(amount: number): void;
}

什么时候用抽象类?

  • 多个子类必须遵守同一套方法规范
  • 父类不应该被直接实例化
  • 你希望错误尽早暴露,而不是等运行到某个方法才发现没实现

什么时候不需要?

  • 只是写一个简单脚本
  • 只有一个实现类
  • 普通函数或普通父类已经足够表达业务

一句话记住:

多态解决“调用方怎么统一调用”
抽象类解决“子类必须实现什么”

十五、多重继承:了解即可,工程里少用

Python 支持一个类继承多个父类。

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


class Worker:
    def __init__(self, company):
        self.company = company


class Student(Person, Worker):
    def __init__(self, name, company, stu_id):
        Person.__init__(self, name)
        Worker.__init__(self, company)
        self.stu_id = stu_id

多重继承的意思是:

一个类可以同时继承多个父类的属性和方法

但是它会带来方法查找顺序、父类初始化顺序、同名方法冲突等问题。

作为前端程序员学习 Python 基础时,先知道它存在即可。

真实业务里,优先选择更简单的组合方式:

class Address:
    def __init__(self, city):
        self.city = city


class User:
    def __init__(self, name, address):
        self.name = name
        self.address = address


address = Address('杭州')
user = User('张三', address)

print(user.address.city)

一句话记住:

继承表达“是什么”
组合表达“拥有什么”

十六、访问控制:公有、受保护、私有

Python 没有像 TS 那样强制的 public / protected / private 关键字。

它更多依赖命名约定。

class Person:
    def __init__(self, name, age, idcard):
        self.name = name
        self._age = age
        self.__idcard = idcard
写法含义工程理解
self.name公有属性外部可以直接访问
self._age受保护属性约定内部或子类使用,外部别随便改
self.__idcard私有属性Python 会做名称改写,外部不建议访问
p1 = Person('张三', 18, '330xxx')

print(p1.name)
print(p1._age)
# print(p1.__idcard) # 会报错

注意:Python 的“私有”不是绝对安全隔离,它更像一种约定和名称保护。

重点不是防黑客,而是提醒业务代码不要乱改内部状态。

十七、property:像属性一样调用方法

有时候你不希望外部直接改一个内部字段,但又希望外部像访问属性一样读取它。

这时候可以使用 @property

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

    @property
    def age(self):
        return self._age


p1 = Person('张三', 18)

print(p1.age)

注意调用方式:

print(p1.age)

不是:

# print(p1.age())

因为 @property 把方法包装成了属性访问。

配合 setter 做校验

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

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError('age 不能小于 0')
        self._age = value


p1 = Person('张三', 18)
p1.age = 20

print(p1.age)

这个能力有点像 TS 里的 get / set

class Person {
  private _age: number;

  get age() {
    return this._age;
  }

  set age(value: number) {
    if (value < 0) {
      throw new Error("age 不能小于 0");
    }
    this._age = value;
  }
}

十八、魔法方法:Python 在特定场景自动调用

魔法方法通常长这样:

__xxx__

它们不是给你日常手动调用的,而是 Python 在特定语法或内置函数下自动调用。

1. __init__:初始化对象

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


p1 = Person('张三')

当你执行 Person('张三') 时,Python 会自动调用 __init__

2. __str__:控制打印对象时的展示

默认打印一个实例,通常不太可读:

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


p1 = Person('张三')
print(p1)

可以重写 __str__

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

    def __str__(self):
        return f'Person(name={self.name})'


p1 = Person('张三')
print(p1)

当执行:

print(p1)

本质上会用到:

str(p1)

然后 Python 会去找 p1.__str__()

3. __len__:支持 len()

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

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


cart = Cart(['苹果', '香蕉'])

print(len(cart))

当执行:

len(cart)

Python 会去找 cart.__len__()

所以 __len__ 返回值必须是整数。

4. __eq__:支持 == 比较

class User:
    def __init__(self, user_id, name):
        self.user_id = user_id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, User):
            return False
        return self.user_id == other.user_id


u1 = User(1, '张三')
u2 = User(1, '张三丰')

print(u1 == u2)

当执行:

u1 == u2

Python 会去找 u1.__eq__(u2)

5. __lt____gt__:支持大小比较

__lt__ 对应小于号 <

__gt__ 对应大于号 >

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

    def __lt__(self, other):
        return self.age < other.age

    def __gt__(self, other):
        return self.age > other.age


p1 = Person('张三', 18)
p2 = Person('李四', 22)

print(p1 < p2)
print(p1 > p2)

当执行:

p1 < p2

Python 会去找:

p1.__lt__(p2)

当执行:

p1 > p2

Python 会去找:

p1.__gt__(p2)

6. __getattr__:访问不存在的属性时兜底

__getattr__ 只会在“正常属性查找失败”时触发。

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

    def __getattr__(self, item):
        return f'你访问的 {item} 属性不存在'


p1 = Person('张三')

print(p1.name)
print(p1.age)

访问 p1.name 时,实例身上本来就有 name,不会触发 __getattr__

访问 p1.age 时,实例身上没有 age,才会触发 __getattr__

这个方法适合做兜底提示、动态属性代理,但不要滥用。

如果什么属性都靠 __getattr__ 动态返回,代码会变得很难读,也不利于 IDE 提示。

常见魔法方法清单

方法触发时机常见用途
__init__Person(...)初始化实例属性
__str__print(obj) / str(obj)给用户看的字符串
__repr__直接查看对象、调试输出给开发者看的字符串
__len__len(obj)返回长度
__eq__obj1 == obj2判断相等
__lt__obj1 < obj2小于比较
__gt__obj1 > obj2大于比较
__getattr__访问不存在的属性兜底属性访问

不要一开始就背很多魔法方法。

先把 __init____str____len____eq____lt____gt____getattr__ 这些高频的看懂,后面遇到协议型代码再补。

十九、isinstance 和 issubclass

isinstance 用来判断某个对象是不是某个类或其子类的实例。

class Person:
    pass


class Student(Person):
    pass


stu = Student()

print(isinstance(stu, Student))
print(isinstance(stu, Person))

issubclass 用来判断某个类是不是另一个类的子类。

print(issubclass(Student, Person))
print(issubclass(Person, Student))

前端里你可能会想到 instanceof

student instanceof Person;

Python 对应的常用写法就是:

isinstance(stu, Person)

二十、object:所有类的基础父类

在 Python 3 里,所有类默认都继承自 object

class Person:
    pass

基本等价于:

class Person(object):
    pass

所以你可以这样验证:

print(issubclass(Person, object))

p1 = Person()
print(isinstance(p1, object))

不仅自定义类继承自 object,很多内置类型也继承自 object

print(issubclass(int, object))
print(issubclass(str, object))
print(issubclass(list, object))
print(issubclass(bool, object))
print(issubclass(tuple, object))

为什么所有对象都能使用一些统一能力?

因为它们最终都继承自 object

print(object.__dict__.keys())

你会看到很多基础能力,比如 __str____repr____eq__ 等。

__dict__dir() 的区别

__dict__ 更像“这个对象自己身上实际保存了什么”。

dir() 更像“这个对象可以访问到什么”,包括自己的、类上的、继承来的。

class Person:
    planet = '地球'

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

    def speak(self):
        print(f'我叫{self.name}')


p1 = Person('张三', 18)

print(p1.__dict__)
print(dir(p1))

你可以先这样记:

__dict__ 看对象自己存了哪些属性
dir() 看对象总共能访问哪些名字

二十一、类和 dict 的关系

对于前端程序员来说,很容易把 Python 类理解成“更复杂的 dict”。

这个理解有一部分是对的:

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


p1 = Person('张三', 18)

print(p1.__dict__)

你会看到:

{'name': '张三', 'age': 18}

实例属性确实可以看成挂在对象上的 key-value。

但是类不只是 dict,它还提供了:

  • 统一的初始化入口
  • 方法复用
  • 继承关系
  • 属性访问规则
  • 魔法方法协议
  • 类型判断能力

所以更准确的心智是:

dict 适合表示一包数据
class 适合表示一类带行为的数据

如果只是接口返回的一坨 JSON 数据,用 dict 很正常。

如果这个对象有稳定结构、有业务行为、有校验逻辑、有复用方法,就可以考虑类。

二十二、最后怎么记

类的核心不是语法多,而是查找规则和对象关系。

先记这几句话:

class 是模板,instance 是具体对象
__init__ 初始化实例,不需要手动调用
self 是当前实例,类似 this,但必须显式写
self.xxx 是实例属性,每个实例自己一份
写在类里、方法外的是类属性,类共享一份
实例方法接收 self,类方法接收 cls,静态方法什么都不自动接收
继承用来复用和扩展父类,super() 用来调用父类逻辑
多态是同一个方法名,在不同对象上有不同表现
抽象类是给子类定规范,要求子类必须实现某些方法
魔法方法由 Python 在特定语法下自动触发

如果只为了写基础业务代码,先掌握:

  • class
  • __init__
  • self
  • 实例属性
  • 实例方法
  • 类属性
  • @classmethod
  • @staticmethod
  • 继承和 super()
  • 多态
  • 抽象类
  • @property
  • __str__

这些已经足够覆盖大多数入门到工程初期的 Python 类使用场景。