Appearance
面向对象编程
Python 是一门多范式编程语言,支持面向对象编程(OOP)。
类与对象
定义类
python
class Person:
"""人员类"""
# 类属性
species = "人类"
# 构造方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def introduce(self):
print(f"我叫{self.name},今年{self.age}岁")
# 类方法
@classmethod
def from_birth_year(cls, name, birth_year):
"""根据出生年份创建实例"""
age = 2024 - birth_year
return cls(name, age)
# 静态方法
@staticmethod
def is_adult(age):
"""判断是否成年"""
return age >= 18
# 创建实例
person = Person("张三", 25)
person.introduce() # 我叫张三,今年25岁
# 访问属性
print(person.name) # 张三
print(Person.species) # 人类
# 调用类方法
person2 = Person.from_birth_year("李四", 1995)
person2.introduce()
# 调用静态方法
print(Person.is_adult(20)) # True实例属性与类属性
python
class Student:
# 类属性
school = "清华大学"
count = 0
def __init__(self, name, grade):
# 实例属性
self.name = name
self.grade = grade
Student.count += 1
# 类属性被所有实例共享
s1 = Student("张三", "大一")
s2 = Student("李四", "大二")
print(Student.count) # 2
print(s1.school) # 清华大学
print(s2.school) # 清华大学
# 修改类属性
Student.school = "北京大学"
print(s1.school) # 北京大学
# 实例属性会遮蔽同名类属性
s1.school = "复旦大学" # 创建实例属性
print(s1.school) # 复旦大学(实例属性)
print(s2.school) # 北京大学(类属性)
# 删除实例属性
del s1.school
print(s1.school) # 北京大学(回到类属性)继承
基本继承
python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
def info(self):
print(f"我是{self.name}")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类构造方法
self.breed = breed
def speak(self):
print(f"{self.name}说: 汪汪!")
def fetch(self):
print(f"{self.name}在捡球")
class Cat(Animal):
def speak(self):
print(f"{self.name}说: 喵喵!")
# 使用
dog = Dog("旺财", "金毛")
dog.info() # 我是旺财
dog.speak() # 旺财说: 汪汪!
dog.fetch() # 旺财在捡球
cat = Cat("咪咪")
cat.speak() # 咪咪说: 喵喵!多继承
python
class Flyable:
def fly(self):
print(f"{self.name}在飞翔")
class Swimmable:
def swim(self):
print(f"{self.name}在游泳")
class Duck(Animal, Flyable, Swimmable):
def speak(self):
print(f"{self.name}说: 嘎嘎!")
duck = Duck("唐老鸭")
duck.speak() # 唐老鸭说: 嘎嘎!
duck.fly() # 唐老鸭在飞翔
duck.swim() # 唐老鸭在游泳
# MRO (Method Resolution Order)
print(Duck.__mro__)
# (<class '__main__.Duck'>, <class '__main__.Animal'>,
# <class '__main__.Flyable'>, <class '__main__.Swimmable'>, <class 'object'>)super() 函数
python
class A:
def __init__(self):
print("A.__init__")
def method(self):
print("A.method")
class B(A):
def __init__(self):
super().__init__() # 调用父类构造方法
print("B.__init__")
def method(self):
super().method() # 调用父类方法
print("B.method")
class C(A):
def __init__(self):
super().__init__()
print("C.__init__")
def method(self):
super().method()
print("C.method")
class D(B, C):
def __init__(self):
super().__init__() # 按 MRO 顺序调用
print("D.__init__")
def method(self):
super().method()
print("D.method")
d = D()
# A.__init__
# C.__init__
# B.__init__
# D.__init__
d.method()
# A.method
# C.method
# B.method
# D.method多态
方法重写
python
class Shape:
def area(self):
pass
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# 多态
shapes = [Rectangle(4, 5), Circle(3), Rectangle(2, 6)]
for shape in shapes:
print(f"面积: {shape.area():.2f}")
print(f"周长: {shape.perimeter():.2f}")
print("---")鸭子类型
python
# "如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子"
class Duck:
def swim(self):
print("鸭子游泳")
def quack(self):
print("鸭子叫")
class Person:
def swim(self):
print("人游泳")
def quack(self):
print("人模仿鸭子叫")
def make_it_quack(thing):
# 不关心类型,只关心是否有 quack 方法
thing.quack()
duck = Duck()
person = Person()
make_it_quack(duck) # 鸭子叫
make_it_quack(person) # 人模仿鸭子叫封装
访问控制
python
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner # 公有属性
self._balance = balance # 保护属性(约定)
self.__secret = "password" # 私有属性(名称修饰)
# 公有方法
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"存款{amount},余额{self._balance}")
# 保护方法
def _validate(self, amount):
return amount > 0
# 私有方法
def __log(self, message):
print(f"[LOG] {message}")
# 属性访问器
@property
def balance(self):
return self._balance
account = BankAccount("张三", 1000)
account.deposit(500)
print(account.owner) # 张三
print(account._balance) # 1000(可以访问,但不推荐)
# print(account.__secret) # AttributeError
# 名称修饰后可以访问(不推荐)
print(account._BankAccount__secret) # passwordproperty 装饰器
python
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
"""获取摄氏温度"""
return self._celsius
@celsius.setter
def celsius(self, value):
"""设置摄氏温度"""
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
@property
def fahrenheit(self):
"""获取华氏温度"""
return self._celsius * 9 / 5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
"""设置华氏温度"""
self.celsius = (value - 32) * 5 / 9
temp = Temperature(25)
print(temp.celsius) # 25
print(temp.fahrenheit) # 77.0
temp.celsius = 30
print(temp.fahrenheit) # 86.0
temp.fahrenheit = 100
print(temp.celsius) # 37.77...
# temp.celsius = -300 # ValueError特殊方法
对象表示
python
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
"""str() 和 print() 调用"""
return f"Point({self.x}, {self.y})"
def __repr__(self):
"""repr() 调用,应该返回可以重建对象的字符串"""
return f"Point({self.x}, {self.y})"
def __format__(self, format_spec):
"""format() 调用"""
if format_spec == "polar":
r = (self.x ** 2 + self.y ** 2) ** 0.5
theta = math.atan2(self.y, self.x)
return f"r={r:.2f}, θ={theta:.2f}"
return str(self)
p = Point(3, 4)
print(p) # Point(3, 4)
print(repr(p)) # Point(3, 4)
print(f"{p:polar}") # r=5.00, θ=0.93比较运算
python
from functools import total_ordering
@total_ordering # 只需实现 __eq__ 和一个比较方法
class Money:
def __init__(self, amount, currency="CNY"):
self.amount = amount
self.currency = currency
def __eq__(self, other):
if not isinstance(other, Money):
return NotImplemented
return self.amount == other.amount and self.currency == other.currency
def __lt__(self, other):
if not isinstance(other, Money):
return NotImplemented
if self.currency != other.currency:
raise ValueError("货币不同无法比较")
return self.amount < other.amount
m1 = Money(100)
m2 = Money(200)
m3 = Money(100)
print(m1 == m3) # True
print(m1 < m2) # True
print(m1 <= m2) # True
print(m1 > m2) # False算术运算
python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""加法: v1 + v2"""
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
"""减法: v1 - v2"""
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
"""乘法: v * scalar"""
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar):
"""右乘: scalar * v"""
return self.__mul__(scalar)
def __truediv__(self, scalar):
"""除法: v / scalar"""
return Vector(self.x / scalar, self.y / scalar)
def __neg__(self):
"""负号: -v"""
return Vector(-self.x, -self.y)
def __abs__(self):
"""绝对值: abs(v)"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(v1 - v2) # Vector(2, 2)
print(v1 * 2) # Vector(6, 8)
print(2 * v1) # Vector(6, 8)
print(-v1) # Vector(-3, -4)
print(abs(v1)) # 5.0容器协议
python
class Deck:
def __init__(self):
self.cards = list(range(1, 53))
def __len__(self):
"""len()"""
return len(self.cards)
def __getitem__(self, index):
"""索引访问: deck[0]"""
return self.cards[index]
def __setitem__(self, index, value):
"""索引赋值: deck[0] = value"""
self.cards[index] = value
def __delitem__(self, index):
"""删除: del deck[0]"""
del self.cards[index]
def __contains__(self, item):
"""成员检测: item in deck"""
return item in self.cards
def __iter__(self):
"""迭代: for card in deck"""
return iter(self.cards)
deck = Deck()
print(len(deck)) # 52
print(deck[0]) # 1
print(1 in deck) # True
for card in deck[:5]:
print(card)上下文管理器
python
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
"""进入 with 语句"""
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
"""退出 with 语句"""
if self.file:
self.file.close()
# 返回 True 会抑制异常
return False
# 使用
with FileManager("test.txt", "w") as f:
f.write("Hello")
# 使用 contextlib
from contextlib import contextmanager
@contextmanager
def file_manager(filename, mode):
f = open(filename, mode)
try:
yield f
finally:
f.close()
with file_manager("test.txt", "r") as f:
print(f.read())可调用对象
python
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, value):
"""使实例可调用"""
return value * self.factor
double = Multiplier(2)
triple = Multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
# 检查是否可调用
print(callable(double)) # True数据类
@dataclass 装饰器
python
from dataclasses import dataclass, field
from typing import List
@dataclass
class Person:
name: str
age: int
city: str = "北京" # 默认值
def introduce(self):
print(f"我叫{self.name},{self.age}岁,来自{self.city}")
p1 = Person("张三", 25)
p2 = Person("李四", 30, "上海")
print(p1) # Person(name='张三', age=25, city='北京')
print(p1 == p2) # False
# 更多选项
@dataclass(order=True) # 支持比较
class Student:
grade: int # 排序依据
name: str = field(compare=False) # 不参与比较
scores: List[int] = field(default_factory=list) # 可变默认值
s1 = Student(1, "张三", [90, 85, 88])
s2 = Student(2, "李四", [95, 92, 90])
print(s1 < s2) # True命名元组
python
from collections import namedtuple
from typing import NamedTuple
# 使用 namedtuple
Point = namedtuple('Point', ['x', 'y'])
p1 = Point(3, 4)
print(p1.x, p1.y) # 3 4
# 使用 NamedTuple(支持类型注解)
class Point3D(NamedTuple):
x: float
y: float
z: float
def distance(self):
return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5
p2 = Point3D(1, 2, 2)
print(p2.distance()) # 3.0抽象基类
abc 模块
python
from abc import ABC, abstractmethod
class Shape(ABC):
"""抽象基类"""
@abstractmethod
def area(self):
"""计算面积"""
pass
@abstractmethod
def perimeter(self):
"""计算周长"""
pass
def info(self):
"""具体方法"""
print(f"面积: {self.area()}, 周长: {self.perimeter()}")
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
# shape = Shape() # TypeError: 不能实例化抽象类
rect = Rectangle(4, 5)
rect.info() # 面积: 20, 周长: 18实践示例
链表实现
python
class Node:
def __init__(self, value):
self.value = value
self.next = None
def __repr__(self):
return f"Node({self.value})"
class LinkedList:
def __init__(self):
self.head = None
self._length = 0
def append(self, value):
"""添加元素"""
node = Node(value)
if not self.head:
self.head = node
else:
current = self.head
while current.next:
current = current.next
current.next = node
self._length += 1
def prepend(self, value):
"""头部添加"""
node = Node(value)
node.next = self.head
self.head = node
self._length += 1
def delete(self, value):
"""删除元素"""
if not self.head:
return
if self.head.value == value:
self.head = self.head.next
self._length -= 1
return
current = self.head
while current.next:
if current.next.value == value:
current.next = current.next.next
self._length -= 1
return
current = current.next
def __len__(self):
return self._length
def __iter__(self):
current = self.head
while current:
yield current.value
current = current.next
def __str__(self):
return " -> ".join(str(v) for v in self)
# 使用
lst = LinkedList()
lst.append(1)
lst.append(2)
lst.append(3)
lst.prepend(0)
print(lst) # 0 -> 1 -> 2 -> 3
print(len(lst)) # 4银行账户系统
python
from dataclasses import dataclass
from datetime import datetime
from typing import List
from enum import Enum
class TransactionType(Enum):
DEPOSIT = "存款"
WITHDRAW = "取款"
TRANSFER = "转账"
@dataclass
class Transaction:
type: TransactionType
amount: float
balance: float
timestamp: datetime = field(default_factory=datetime.now)
description: str = ""
class BankAccount:
def __init__(self, owner: str, balance: float = 0):
self.owner = owner
self._balance = balance
self._transactions: List[Transaction] = []
@property
def balance(self) -> float:
return self._balance
def deposit(self, amount: float, description: str = ""):
if amount <= 0:
raise ValueError("存款金额必须大于0")
self._balance += amount
self._transactions.append(Transaction(
TransactionType.DEPOSIT, amount, self._balance, description=description
))
def withdraw(self, amount: float, description: str = ""):
if amount <= 0:
raise ValueError("取款金额必须大于0")
if amount > self._balance:
raise ValueError("余额不足")
self._balance -= amount
self._transactions.append(Transaction(
TransactionType.WITHDRAW, amount, self._balance, description=description
))
def transfer(self, other: 'BankAccount', amount: float):
self.withdraw(amount, f"转账给{other.owner}")
other.deposit(amount, f"来自{self.owner}的转账")
def get_statement(self):
print(f"\n{'='*50}")
print(f"账户持有人: {self.owner}")
print(f"当前余额: {self._balance:.2f}")
print(f"{'='*50}")
print(f"{'类型':<8}{'金额':<12}{'余额':<12}{'时间':<20}{'备注'}")
print("-" * 50)
for t in self._transactions:
print(f"{t.type.value:<8}{t.amount:<12.2f}{t.balance:<12.2f}"
f"{t.timestamp.strftime('%Y-%m-%d %H:%M:%S'):<20}{t.description}")
# 使用
acc1 = BankAccount("张三", 1000)
acc2 = BankAccount("李四", 500)
acc1.deposit(500, "工资")
acc1.withdraw(200, "购物")
acc1.transfer(acc2, 300)
acc1.get_statement()
acc2.get_statement()