0%

类和对象

Python 类和对象:别背语法,先理解”模板”和”实例”的关系

面向对象编程,听起来很唬人,其实核心就两个概念:对象

类就是模板,对象就是根据模板造出来的具体东西。打个比方:类是一张”员工入职登记表”的模板,对象就是你拿着这个模板给每个新员工填出来的那张具体表格。

Python 里万物皆对象,但你自己也能定义类。

定义一个类:从空壳子开始

class 关键字定义一个类,类名用帕斯卡命名法(首字母大写):

1
2
3
4
5
6
class Person:
pass # pass 表示啥都不干,占个位置

# 造一个对象出来
p = Person()
print(p) # <__main__.Person object at 0x...>

目前这个 Person 是个空壳子,啥用没有。我们需要给它加东西。

构造方法 __init__:对象出生时该干的事

__init__ 是 Python 的构造方法,对象被创建的时候自动调用。它的作用就是给对象初始化——设置初始状态、存一些基础数据。

1
2
3
4
5
6
7
8
9
10
11
class Person:
def __init__(self, name, age):
self.name = name # 把参数存到对象身上
self.age = age

def introduce(self):
print(f"我叫 {self.name},今年 {self.age} 岁")

# 创建对象的时候传入参数
p = Person("Alice", 20)
p.introduce() # 我叫 Alice,今年 20 岁

关键点:

  • self 就是对象本身,相当于 Java 里的 this
  • self.name 是对象的属性,每个对象有自己的独立副本
  • __init__ 可以有默认参数,让某些属性可选
1
2
3
4
5
6
7
class Student:
def __init__(self, name, major="计算机科学"):
self.name = name
self.major = major

s1 = Student("Bob") # major 用默认值
s2 = Student("Charlie", "数学") # major 自己指定

类变量 vs 实例变量:共享的和独享的

这是刚学 Python 类的时候最容易搞混的地方。

实例变量:每个对象各有一份,互不影响。用 self.xxx 定义。

类变量:所有对象共享一份,属于类本身。直接在类下面定义,不在任何方法里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Employee:
# 类变量:统计员工总数,所有对象共享
emp_count = 0

def __init__(self, name):
self.name = name # 实例变量:每个员工的名字各不同
Employee.emp_count += 1

e1 = Employee("张三")
e2 = Employee("李四")

print(e1.name) # 张三
print(e2.name) # 李四
print(Employee.emp_count) # 2,所有对象共享

什么时候用类变量? 统计总数、全局配置、默认值等所有对象共用的东西。

什么时候用实例变量? 每个对象各自不同的数据,比如名字、年龄、分数。

三种方法:实例方法、类方法、静态方法

1. 实例方法:最常用的,第一个参数是 self

1
2
3
4
5
6
class Person:
def __init__(self, name):
self.name = name

def say_hello(self): # self 是当前对象
print(f"Hello, I'm {self.name}")

实例方法能访问实例变量和类变量,是日常写代码最常用的一种。

2. 类方法:用 @classmethod,第一个参数是 cls(类本身)

1
2
3
4
5
6
7
8
9
10
class Person:
count = 0

def __init__(self, name):
self.name = name
Person.count += 1

@classmethod
def get_count(cls):
return cls.count # 返回总人数

类方法通常用来操作类变量,或者作为工厂方法——用不同方式创建对象。

1
2
3
4
5
6
7
8
9
10
11
12
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

@classmethod
def from_birth_year(cls, name, birth_year):
# 用出生年份推算年龄,创建对象
age = 2026 - birth_year
return cls(name, age)

p = Person.from_birth_year("Alice", 2000) # 不用传 age,自动算

3. 静态方法:用 @staticmethod,不需要 self 或 cls

静态方法跟类本身没有直接关系,只是因为逻辑上属于这个类,所以放在里面。它不访问任何实例或类属性。

1
2
3
4
5
6
7
class MathUtils:
@staticmethod
def is_even(num):
return num % 2 == 0

# 直接用类名调用,不需要创建对象
MathUtils.is_even(10) # True

继承:子类继承父类的东西

单继承:一个子类只有一个父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal:
def __init__(self, name):
self.name = name

def eat(self):
print(f"{self.name} 在吃东西")

# Dog 继承 Animal
class Dog(Animal):
def bark(self):
print(f"{self.name} 在汪汪叫")

dog = Dog("旺财")
dog.eat() # 继承来的
dog.bark() # 自己的

重写父类方法:子类把父类的方法覆盖掉

1
2
3
4
5
6
class Cat(Animal):
def eat(self): # 重写父类的 eat
print(f"{self.name} 爱吃鱼")

cat = Cat("咪咪")
cat.eat() # 咪咪 爱吃鱼(自己的方法)

调用父类的方法:用 super()

1
2
3
4
5
6
7
8
class Bird(Animal):
def __init__(self, name, can_fly):
super().__init__(name) # 先调父类的 __init__,初始化 name
self.can_fly = can_fly # 再初始化子类自己的属性

def eat(self):
super().eat() # 先执行父类的 eat
print(f"{self.name} 还爱吃虫子")

super() 的作用是”调用父类的同名方法”,避免硬编码父类名,代码更好维护。

多继承:一个子类有多个父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Flyable:
def fly(self):
print("会飞")

class Swimmable:
def swim(self):
print("会游泳")

class Duck(Flyable, Swimmable):
pass

duck = Duck()
duck.fly() # 会飞
duck.swim() # 会游泳

多继承的问题在于:如果多个父类有同名方法,Python 按什么顺序找?答案是 MRO(方法解析顺序),从左到右。Duck(Flyable, Swimmable) 先找 Flyable,再找 Swimmable。

私有属性:名字前面加两个下划线

Python 没有真正的私有,但用双下划线开头可以”隐藏”属性:

1
2
3
4
5
6
7
8
9
10
11
12
class Person:
def __init__(self, name, age):
self.name = name # 公开的
self.__age = age # 私有的

def get_age(self):
return self.__age

p = Person("Alice", 20)
print(p.name) # Alice,可以访问
# print(p.__age) # 报错,访问不了
print(p.get_age()) # 20,通过方法访问

其实 Python 只是把 __age 改名叫 _Person__age 了,你非要访问也能 p._Person__age,但强烈不推荐。私有属性的意义是”约定”而不是”强制”——告诉使用这个类的人:别碰这个属性。

几个特殊方法:让对象更像对象

方法 作用 什么时候触发
__init__ 初始化对象 创建对象时
__str__ 对象的字符串表示 print(obj)str(obj)
__repr__ 对象的官方字符串表示 调试时,或在交互式环境直接输入对象名
__len__ 对象的长度 len(obj)

__str__ 最常用:

1
2
3
4
5
6
7
8
9
10
class Book:
def __init__(self, title, author):
self.title = title
self.author = author

def __str__(self):
return f"《{self.title}》(作者:{self.author})"

book = Book("Python 编程", "张三")
print(book) # 《Python 编程》(作者:张三)

不定义 __str__ 的话,print(book) 会打印一串看不懂的内存地址。

类比java的toString方法

欢迎关注我的其它发布渠道