类与对象
类与对象
- 类 (Class): 模板,定义属性和方法。
- 对象 (Object): 类的实例 (Instance)。
# 定义类
class Person:
"""简单类示例"""
species = "人类" # 类变量
def __init__(self, name: str, age: int):
self.name = name # 实例变量
self.age = age
def greet(self) -> str: # 实例方法
return f"Hi, I'm {self.name}, {self.age} years old."
# 创建对象
p = Person("Alice", 30)
print(p.greet()) # Hi, I'm Alice, 30 years old.
print(Person.species) # 人类
要点:
__init__是 初始化方法, 在实例创建后被调用。self表示当前对象的引用(self虽不是关键字, 但约定俗成), 用来访问对象的属性和方法。方法调用时 Python 自动传入,不需要手动写。- 类变量 在所有实例间共享, 实例变量是每个实例独有的。
实例变量/类变量/私有变量
实例变量 (Instance Variables): 属于特定实例(对象)的变量,每个实例都拥有自己独立的一份副本。
特点:
- 在
__init__构造函数 或 其他实例方法中 通过self.变量名定义 - 每个对象的 实例变量值 可以不同
类变量 (Class Variables): 属于类本身的变量,被所有实例共享。
特点:
- 在 类内部 但 在所有方法之外 定义
- 所有对象 共享同一份数据
私有变量 (Private Variables): 在变量名前加 双下划线 __ 的变量,Python 会对其进行名称修饰,实现一定程度的"私有化"。
特点:
- Python 会将其重命名为
_类名__变量名 - 只是一种约定,并非真正的私有(Python 没有真正的私有变量)
保护变量 (Protected Variables): 单下划线 _ 开头的变量。
class C:
shared = [] # 类变量(可变,需小心)
def __init__(self, x):
self.x = x # 实例变量
self._hidden = 0 # 约定“受保护”
self.__private = 1 # 名称改写(name mangling)
c = C(10)
print(c.shared)
私有变量 __name 会被改写为 _ClassName__name,用于避免子类覆盖(不是绝对私密)。
实例方法/类方法/静态方法
访问权限:
- 实例方法:对实例和类都有完全访问权限。最常用、最灵活的方法。
- 类方法:仅能看到所有类属性和其他类方法,看不到任何具体的实例。
- 静态方法:与类完全隔离。它就像一个定义在类命名空间下的普通函数,不能直接使用
self或cls。
class Student:
school = "ABC School"
def __init__(self, name):
self.name = name
def say_hi(self): # 实例方法
print(f"Hi, I am {self.name}")
@classmethod
def school_info(cls): # 类方法
print(f"School name: {cls.school}")
@staticmethod
def greet(): # 静态方法
print("Welcome to our school!")
@classmethod 第一个参数是类 (cls);常用于 工厂方法 或 访问/修改类状态。@staticmethod 不接收 self 或 cls;像普通函数,但放 在类命名空间下 便于组织。
封装 (Encapsulation)
封装的意义:
- 隐藏实现细节,只暴露必要的接口。
- 保护数据不被外部直接访问和修改。
公有属性与方法
Python 默认所有属性和方法都是 公有 的,可以直接访问。
class Car:
def __init__(self, brand):
self.brand = brand # 公有属性
def drive(self): # 公有方法
print(f"{self.brand} is driving")
私有属性与方法
- 私有属性/方法以
双下划线 __开头。 - 外部不能直接访问 (Python 会做名称改写)。
class BankAccount:
def __init__(self, balance):
self.__balance = balance # 私有属性
def deposit(self, amount):
self.__balance += amount
def get_balance(self): # 提供公有方法访问
return self.__balance
acc = BankAccount(100)
acc.deposit(50)
print(acc.get_balance()) # 150
print(acc.__balance) # ❌ 报错: AttributeError
@property 装饰器
用来实现 getter 和 setter,让属性像访问变量一样。
class Person:
def __init__(self, age):
self.__age = age
@property
def age(self): # getter
return self.__age
@age.setter
def age(self, value): # setter
if value < 0:
raise ValueError("Age cannot be negative")
self.__age = value
p = Person(18)
print(p.age) # 调用 getter
p.age = 20 # 调用 setter
继承 (Inheritance)
子类 (Subclass) 可以继承 父类 (Superclass) 的 属性和方法,并且可以 扩展 或 重写。
继承的优缺点:
- 优点: 代码复用, 层次结构清晰(表达“是一个 (is-a)”的关系)
- 缺点: 耦合度高(父类改动可能影响所有子类)
建议: 优先 组合 (Composition),再考虑 继承。
单继承
子类 Dog 继承了 Animal 的属性,并重写了 speak() 方法:
class Animal: # 父类
def __init__(self, name):
self.name = name
def speak(self): # 父类方法
print("Animal makes a sound")
class Dog(Animal): # 子类
def speak(self): # 重写方法
print(f"{self.name} says Woof!")
dog = Dog("Buddy")
dog.speak() # Buddy says Woof!
super() 的用法: super() 用来调用父类的方法,常见于构造函数中。
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类构造方法
self.breed = breed
dog = Dog("Buddy", "Labrador")
print(dog.name, dog.breed)
多重继承
Python 支持多重继承,一个类可以继承多个父类。
class A:
def say(self):
print("A")
class B:
def say(self):
print("B")
class C(A, B):
pass
c = C()
c.say()
# 类名.mro() 查看 MRO 方法解析顺序
print(C.mro())
# 结果:
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
因为 Python 采用 MRO (Method Resolution Order, 方法解析顺序),会优先搜索 A。
多态 (Polymorphism)
多态 (Polymorphism): 相同的接口,不同的对象表现出不同的行为。
好处: 增加代码的 灵活性 和 可扩展性。
方法重写 (Override)
子类可以重写父类的方法,实现不同的功能。
相同的方法名 speak(),不同对象表现不同:
class Animal:
def speak(self):
print("Animal speaks")
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
animals = [Dog(), Cat()]
for animal in animals:
animal.speak()
鸭子类型 (Duck Typing)
Python 的多态不要求必须继承同一个父类,只要对象实现了同样的方法,就可以当作同类使用。
“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。”
class Duck:
def speak(self):
print("Quack!")
class Person:
def speak(self):
print("Hello!")
def make_it_speak(obj):
obj.speak()
make_it_speak(Duck()) # Quack!
make_it_speak(Person()) # Hello!
抽象基类 (ABC)
有时候我们希望强制子类必须实现某些方法,可以用抽象基类。
from abc import ABC, abstractmethod
class Animal(ABC): # 抽象类
@abstractmethod
def speak(self): # 抽象方法
pass
class Dog(Animal):
def speak(self):
print("Woof!")
# a = Animal() # ❌ 报错: 不能实例化抽象类
dog = Dog()
dog.speak()
类的高级特性
魔术方法 (Magic Methods)
魔术方法是 Python 内置的一组特殊方法,名字前后都有两个下划线,比如 __init__、__str__。
魔术方法 让类的对象表现得更 “像内置类型”,比如:
__init__初始化对象__str__打印对象时调用__len__让对象可以用 len(obj)__getitem__让对象可以像列表一样用 obj[i]__add__让对象支持 + 运算符__lt__让对象支持 < 运算符__contains__让对象支持 in 运算符__call__让对象可以像函数一样调用
好处: 让自定义类更加“Pythonic”,像内置类型一样好用。
对象的创建与销毁
__new__: 控制对象的创建 (通常用在实现单例模式)__init__: 控制对象的初始化 (最常见)__del__: 控制对象的销毁 (不建议使用, 因为 Python 的垃圾回收不保证立即调用)。
class Person:
def __new__(cls, *args, **kwargs):
print("Creating instance")
return super().__new__(cls)
def __init__(self, name):
print("Initializing instance")
self.name = name
p = Person("Alice")
# 输出:
# Creating instance
# Initializing instance
对象的字符串表示
__str__: 用于 print(obj),面向用户,返回可读性强的字符串。__repr__: 用于调试,面向开发者,返回更精确的字符串。
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Person(name={self.name})"
def __repr__(self):
return f"<Person: {self.name}>"
p = Person("Alice")
print(p) # Person(name=Alice)
p # <Person: Alice>
容器类相关魔术方法
这些方法让自定义类像列表/字典一样使用:
class MyList:
def __init__(self, data):
self.data = data
def __len__(self): # 让对象支持 len()
return len(self.data)
def __getitem__(self, index): # 支持 obj[i]
return self.data[index]
def __setitem__(self, index, value): # 支持 obj[i] = value
self.data[index] = value
def __iter__(self): # 支持 for 循环
return iter(self.data)
lst = MyList([1, 2, 3])
print(len(lst)) # 3
print(lst[0]) # 1
lst[1] = 10
print(lst.data) # [1, 10, 3]
for x in lst:
print(x)
运算符重载 (Operator Overloading)
Python 允许我们通过魔术方法重载运算符。
常见的运算符对应的方法:
| 运算符 | 魔术方法 |
|---|---|
+ | __add__ |
- | __sub__ |
* | __mul__ |
/ | __truediv__ |
// | __floordiv__ |
% | __mod__ |
** | __pow__ |
== | __eq__ |
!= | __ne__ |
< | __lt__ |
<= | __le__ |
> | __gt__ |
>= | __ge__ |
可迭代对象与迭代器
__iter__: 返回一个迭代器对象__next__: 迭代器对象的方法,用于获取下一个元素
class CountDown:
def __init__(self, start):
self.start = start
def __iter__(self):
return self
def __next__(self):
if self.start <= 0:
raise StopIteration
self.start -= 1
return self.start
cd = CountDown(5)
for num in cd:
print(num)
# 输出:
# 4
# 3
# 2
# 1
# 0
模块化与包 (Modules & Packages)
模块是 .py 文件,包是带 __init__.py 文件 的文件夹。
最佳实践:
在包的 init.py 中定义对外暴露的 API。
模块 (Module)
模块 就是一个 Python 文件 (.py),里面包含变量、函数、类等定义。
模块的作用: 代码复用 和 代码组织。
创建一个 my_module.py 文件,内容如下:
# my_module.py
def greet(name):
return f"Hello, {name}!"
pi = 3.14
在另一个文件中使用:
import my_module
print(my_module.greet("Alice")) # Hello, Alice!
print(my_module.pi) # 3.14
模块的导入方式
标准导入:
import my_module
print(my_module.pi) # 3.14
导入时重命名 (别名):
import my_module as mm
print(mm.pi) # 3.14
从模块中导入指定成员:
from my_module import greet, pi
print(greet("Bob")) # Hello, Bob!
print(pi) # 3.14
导入所有成员 (不推荐):
from my_module import *
print(greet("cx")) # Hello, cx!
print(pi) # 3.14
无论 import 多少次,一个模块只会在第一次加载时执行。后续的导入都不会重新执行模块代码。
name == “main” 机制
当一个 Python 文件被直接运行时,__name__ 的值是 “__main__"。
当它被作为模块导入时,__name__ 的值是模块名。
# my_module.py
def greet(name):
return f"Hello, {name}!"
pi = 3.14
if __name__ == "__main__":
print("This module is being run directly")
else:
print("This module is being imported")
Python 内置模块
Python 内置了很多常用模块,可以直接导入:
- math (数学函数)
- os (操作系统接口)
- sys (解释器相关)
- random (随机数)
- datetime (日期和时间)
- json (JSON 编码/解码)
- urllib (URL 处理)
- requests (HTTP 请求)
例子:
import math
print(math.pi) # 3.141592653589793
import random
print(random.randint(1, 10)) # 随机整数
print(random.choice(["a", "b", "c"])) # 随机选择
import datetime
print(datetime.datetime.now()) # 当前时间
包 (Package)
包 是一个 包含多个 模块 的 文件夹,并且这个文件夹里必须有一个 __init__.py 文件 (Python 3.3+ 可以省略,但建议保留)。
作用: 让 Python 能够更好地组织、管理大型项目的代码
包的结构示例:
my_project/
│
├── my_package/
│ ├── __init__.py
│ ├── module1.py
│ └── module2.py
│
└── main.py
包的使用:
# module1.py
def foo():
print("This is foo() from module1")
# module2.py
def bar():
print("This is bar() from module2")
# __init__.py
from .module1 import foo
from .module2 import bar
然后在 main.py 中使用:
from my_package import foo, bar
foo()
bar()
包的导入方式
绝对导入 (推荐)
from my_package import module1, module2
相对导入 (包内使用)
from . import module1, module2
from .module1 import foo
from .module2 import bar
from ..other_package.moduleX import something
模块搜索路径
当导入一个模块时,Python 会在以下几个位置搜索:
1. 当前目录
2. PYTHONPATH 环境变量指定的目录
3. Python 标准库目录
4. 第三方库目录
可以通过 sys.path 查看搜索路径:
import sys
print(sys.path)
第三方模块管理
安装第三方模块:
pip install requests
使用第三方模块:
import requests
response = requests.get("https://www.baidu.com")
print(response.status_code) # 200
requirements.txt
requirements.txt 是一种约定:一个文本文件,列出项目运行时(或某个环境)所需的 Python 包及其版本约束。
作用: 重复部署时,确保使用的是相同的 Python 版本和依赖版本,以避免不一致的问题。
文件格式简单:每行一个依赖,可以包含版本约束、来源、注释等。
示例(简单):
# requirements.txt
requests==2.25.1
生成 / 更新 requirements.txt:
# 把当前环境所有(包含传递依赖)精确锁定(适合生产部署的“快照”文件)。
pip freeze > requirements.txt
安装 requirements.txt 中的依赖:
pip install -r requirements.txt