类
类可以理解为创建对象的模板。
例如,一只股票通常包含:
股票代码
股票名称
价格
行业
还可能具有一些行为:
显示信息
更新价格
判断是否属于主板
计算涨跌幅
如果只使用字典:
stock = {
"code": "600519",
"name": "贵州茅台",
"price": 1500.0,
}
可以保存数据,但相关行为需要单独写函数。
使用类,可以把数据和行为组织在一起:
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
def show_info(self) -> None:
print(
self.code,
self.name,
self.price,
)
创建对象:
stock = Stock(
code="600519",
name="贵州茅台",
price=1500.0,
)
调用方法:
stock.show_info()
类主要用于:
- 描述现实中的对象
- 把数据和行为组织在一起
- 提高代码复用性
- 管理复杂程序
- 构建大型项目
类是模板,对象是根据模板创建出来的具体实例。
定义类:
class Person:
pass
创建对象:
person = Person()
其中:
Person → 类
person → 对象
可以创建多个对象:
person1 = Person()
person2 = Person()
person3 = Person()
它们都属于 Person 类,但彼此是不同的对象。
查看类型:
print(type(person1))
判断对象是否属于某个类:
print(isinstance(person1, Person))
结果:
True
类使用 class 关键字定义。
基本格式:
class 类名:
类的内容
例如:
class Dog:
pass
Python 类名通常使用大驼峰命名法:
class Stock:
pass
class StockAnalyzer:
pass
class DatabaseConnection:
pass
不推荐:
class stock_analyzer:
pass
定义类:
class Dog:
pass
创建对象:
dog = Dog()
可以创建多个:
dog1 = Dog()
dog2 = Dog()
判断是否是同一个对象:
print(dog1 is dog2)
结果:
False
因为它们是两个独立对象。
__init__() 用于对象创建后的初始化。
class Person:
def __init__(
self,
name: str,
age: int,
) -> None:
self.name = name
self.age = age
创建对象:
person = Person(
name="张三",
age=25,
)
访问属性:
print(person.name)
print(person.age)
结果:
张三
25
创建对象时:
person = Person("张三", 25)
Python 会自动调用:
Person.__init__(
person,
"张三",
25,
)
通常不需要手动调用 __init__()。
self 表示当前对象本身。
例如:
self.name = name
表示:
把参数 name 保存到当前对象的 name 属性中
不同对象拥有自己的属性:
person1 = Person("张三", 25)
person2 = Person("李四", 30)
print(person1.name)
print(person2.name)
结果:
张三
李四
实例方法的第一个参数必须接收当前对象,通常命名为 self。
class Person:
def show_name(self) -> None:
print(self.name)
理论上可以使用其他名称:
class Person:
def show_name(current_object) -> None:
print(current_object.name)
但不推荐。
约定统一使用:
self
实例属性属于具体对象。
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
创建对象:
stock1 = Stock(
"600519",
"贵州茅台",
1500.0,
)
stock2 = Stock(
"000001",
"平安银行",
11.5,
)
每个对象的数据独立:
print(stock1.name)
print(stock2.name)
修改一个对象:
stock1.price = 1520.0
不会影响另一个对象。
Python 可以在对象创建后添加新属性:
stock1.industry = "白酒"
访问:
print(stock1.industry)
但 stock2 没有这个属性:
print(stock2.industry)
会出现:
AttributeError
实际项目中,推荐在 __init__() 中统一声明实例属性。
实例方法是属于对象的方法。
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
def show_info(self) -> None:
print(
f"{self.code} "
f"{self.name} "
f"{self.price}"
)
调用:
stock = Stock(
"600519",
"贵州茅台",
1500.0,
)
stock.show_info()
Python 实际会执行类似:
Stock.show_info(stock)
因此实例方法的第一个参数需要是 self。
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
def update_price(
self,
new_price: float,
) -> None:
self.price = new_price
调用:
stock = Stock(
"600519",
"贵州茅台",
1500.0,
)
stock.update_price(1520.0)
print(stock.price)
结果:
1520.0
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
def is_main_board(self) -> bool:
return self.code.startswith(("00", "60"))
调用:
stock = Stock(
"600519",
"贵州茅台",
1500.0,
)
print(stock.is_main_board())
结果:
True
类属性属于类本身,所有对象共享。
class Stock:
market = "A股"
def __init__(
self,
code: str,
name: str,
) -> None:
self.code = code
self.name = name
访问类属性:
print(Stock.market)
也可以通过对象访问:
stock = Stock(
"600519",
"贵州茅台",
)
print(stock.market)
class Stock:
count = 0
def __init__(
self,
code: str,
name: str,
) -> None:
self.code = code
self.name = name
Stock.count += 1
创建对象:
stock1 = Stock(
"600519",
"贵州茅台",
)
stock2 = Stock(
"000001",
"平安银行",
)
查看对象数量:
print(Stock.count)
结果:
2
class Person:
category = "人类"
创建对象:
person = Person()
访问:
print(person.category)
如果给对象赋值:
person.category = "程序员"
此时创建了实例属性,覆盖对象访问时看到的类属性。
print(person.category)
print(Person.category)
结果:
程序员
人类
类属性本身没有被修改。
类方法使用 @classmethod。
第一个参数通常命名为 cls,表示当前类。
class Stock:
market = "A股"
@classmethod
def show_market(cls) -> None:
print(cls.market)
调用:
Stock.show_market()
也可以通过对象调用:
stock = Stock()
stock.show_market()
但通常通过类调用更清晰。
class Stock:
count = 0
def __init__(self) -> None:
type(self).count += 1
@classmethod
def get_count(cls) -> int:
return cls.count
使用:
Stock()
Stock()
print(Stock.get_count())
结果:
2
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
@classmethod
def from_text(
cls,
text: str,
) -> "Stock":
code, name, price_text = text.split(",")
return cls(
code=code.strip(),
name=name.strip(),
price=float(price_text),
)
调用:
stock = Stock.from_text(
"600519,贵州茅台,1500.0"
)
print(stock.code)
print(stock.name)
print(stock.price)
类方法常用于:
- 从字符串创建对象
- 从字典创建对象
- 从 JSON 创建对象
- 从数据库记录创建对象
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
@classmethod
def from_dict(
cls,
data: dict[str, object],
) -> "Stock":
return cls(
code=str(data["code"]),
name=str(data["name"]),
price=float(data["price"]),
)
调用:
data = {
"code": "600519",
"name": "贵州茅台",
"price": 1500.0,
}
stock = Stock.from_dict(data)
静态方法使用 @staticmethod。
静态方法既不接收 self,也不接收 cls。
class Stock:
@staticmethod
def is_valid_code(code: str) -> bool:
return (
len(code) == 6
and code.isdigit()
)
调用:
print(
Stock.is_valid_code("600519")
)
结果:
True
静态方法适合:
- 与类相关的辅助函数
- 不需要访问对象属性
- 不需要访问类属性
实例方法:
def method(self):
pass
适合访问:
实例属性
类属性
类方法:
@classmethod
def method(cls):
pass
适合访问:
类属性
创建替代构造方法
静态方法:
@staticmethod
def method():
pass
适合:
与类有关,但不依赖对象和类状态的工具逻辑
封装是把数据和操作数据的方法放在类内部,并限制外部随意修改。
Python 没有像某些语言那样严格的私有属性机制,但有命名约定。
class Stock:
def __init__(
self,
price: float,
) -> None:
self.price = price
外部可以直接访问和修改:
stock.price = -100
这可能导致无效数据。
class Stock:
def __init__(
self,
price: float,
) -> None:
self._price = price
单下划线表示:
这是内部属性,外部不应随意访问。
但技术上仍然可以访问:
print(stock._price)
这是一种开发者约定,不是强制限制。
class Stock:
def __init__(
self,
price: float,
) -> None:
self.__price = price
外部直接访问:
print(stock.__price)
会出现:
AttributeError
Python 会进行名称改写。
实际属性名称类似:
_Stock__price
仍然可以访问:
print(stock._Stock__price)
但不应该这样做。
双下划线主要用于避免子类属性冲突,而不是绝对安全。
property 可以让方法像属性一样访问。
class Stock:
def __init__(
self,
price: float,
) -> None:
self._price = price
@property
def price(self) -> float:
return self._price
访问:
stock = Stock(1500.0)
print(stock.price)
调用时不写括号:
stock.price
不是:
stock.price()
class Stock:
def __init__(
self,
price: float,
) -> None:
self.price = price
@property
def price(self) -> float:
return self._price
@price.setter
def price(
self,
value: float,
) -> None:
if value < 0:
raise ValueError(
"价格不能小于零"
)
self._price = value
创建对象:
stock = Stock(1500.0)
修改:
stock.price = 1520.0
非法修改:
stock.price = -100
会抛出:
ValueError
外部看起来仍然像普通属性:
stock.price
但内部已经执行了校验逻辑。
只定义 getter,不定义 setter:
class Rectangle:
def __init__(
self,
width: float,
height: float,
) -> None:
self.width = width
self.height = height
@property
def area(self) -> float:
return self.width * self.height
访问:
rectangle = Rectangle(10, 5)
print(rectangle.area)
不能直接赋值:
rectangle.area = 100
会报错。
以双下划线开头和结尾的方法,称为特殊方法或魔术方法。
例如:
__init__
__str__
__repr__
__len__
__eq__
它们让自定义对象支持 Python 内置行为。
__str__() 定义对象面向用户的字符串表示。
class Stock:
def __init__(
self,
code: str,
name: str,
) -> None:
self.code = code
self.name = name
def __str__(self) -> str:
return f"{self.code} {self.name}"
使用:
stock = Stock(
"600519",
"贵州茅台",
)
print(stock)
结果:
600519 贵州茅台
没有定义 __str__() 时,输出通常类似:
<__main__.Stock object at 0x...>
__repr__() 定义对象面向开发者的字符串表示。
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
def __repr__(self) -> str:
return (
"Stock("
f"code={self.code!r}, "
f"name={self.name!r}, "
f"price={self.price!r}"
")"
)
使用:
stock = Stock(
"600519",
"贵州茅台",
1500.0,
)
print(repr(stock))
结果:
Stock(code='600519', name='贵州茅台', price=1500.0)
通常建议:
__str__ → 给普通用户看
__repr__ → 给开发者调试看
__eq__() 定义两个对象如何判断相等。
class Stock:
def __init__(
self,
code: str,
name: str,
) -> None:
self.code = code
self.name = name
def __eq__(
self,
other: object,
) -> bool:
if not isinstance(other, Stock):
return NotImplemented
return self.code == other.code
创建对象:
stock1 = Stock(
"600519",
"贵州茅台",
)
stock2 = Stock(
"600519",
"贵州茅台",
)
比较:
print(stock1 == stock2)
结果:
True
如果没有定义 __eq__(),默认比较是否为同一个对象。
让对象支持 len()。
class Portfolio:
def __init__(
self,
stocks: list[str],
) -> None:
self.stocks = stocks
def __len__(self) -> int:
return len(self.stocks)
使用:
portfolio = Portfolio([
"600519",
"000001",
"300750",
])
print(len(portfolio))
结果:
3
让对象支持 in。
class Portfolio:
def __init__(
self,
codes: list[str],
) -> None:
self.codes = codes
def __contains__(
self,
code: object,
) -> bool:
return code in self.codes
使用:
portfolio = Portfolio([
"600519",
"000001",
])
print("600519" in portfolio)
结果:
True
让对象可以被遍历。
class Portfolio:
def __init__(
self,
codes: list[str],
) -> None:
self.codes = codes
def __iter__(self):
return iter(self.codes)
使用:
portfolio = Portfolio([
"600519",
"000001",
"300750",
])
for code in portfolio:
print(code)
继承允许一个类复用另一个类的属性和方法。
父类:
class Animal:
def speak(self) -> None:
print("动物发出声音")
子类:
class Dog(Animal):
pass
使用:
dog = Dog()
dog.speak()
结果:
动物发出声音
Dog 继承了 Animal 的方法。
子类可以重新定义父类方法。
class Animal:
def speak(self) -> None:
print("动物发出声音")
class Dog(Animal):
def speak(self) -> None:
print("汪汪")
class Cat(Animal):
def speak(self) -> None:
print("喵喵")
调用:
dog = Dog()
cat = Cat()
dog.speak()
cat.speak()
结果:
汪汪
喵喵
super() 用于调用父类的方法。
class Person:
def __init__(
self,
name: str,
) -> None:
self.name = name
子类:
class Employee(Person):
def __init__(
self,
name: str,
salary: float,
) -> None:
super().__init__(name)
self.salary = salary
创建对象:
employee = Employee(
"张三",
10000.0,
)
print(employee.name)
print(employee.salary)
如果不调用:
super().__init__(name)
父类初始化逻辑不会自动执行。
class Animal:
def speak(self) -> None:
print("动物准备发声")
class Dog(Animal):
def speak(self) -> None:
super().speak()
print("汪汪")
调用:
dog = Dog()
dog.speak()
结果:
动物准备发声
汪汪
多态表示不同对象可以使用相同的方法接口,但表现不同。
class Dog:
def speak(self) -> None:
print("汪汪")
class Cat:
def speak(self) -> None:
print("喵喵")
定义函数:
def make_sound(animal) -> None:
animal.speak()
调用:
make_sound(Dog())
make_sound(Cat())
结果:
汪汪
喵喵
函数不关心对象具体是什么类,只关心它是否有 speak() 方法。
这体现了 Python 的鸭子类型:
如果一个对象走起来像鸭子,叫起来也像鸭子,就可以把它当作鸭子。
class MarketOrder:
def execute(self) -> None:
print("按市场价格成交")
class LimitOrder:
def execute(self) -> None:
print("达到指定价格后成交")
统一执行:
def execute_order(order) -> None:
order.execute()
调用:
execute_order(MarketOrder())
execute_order(LimitOrder())
抽象基类用于定义子类必须实现的方法。
from abc import ABC, abstractmethod
定义抽象类:
class Order(ABC):
@abstractmethod
def execute(self) -> None:
pass
子类:
class MarketOrder(Order):
def execute(self) -> None:
print("执行市价单")
不能直接创建抽象类对象:
order = Order()
会报错。
子类如果没有实现抽象方法,也不能创建对象。
class LimitOrder(Order):
pass
order = LimitOrder()
会报错。
抽象类适合大型项目中统一接口。
Python 支持一个类继承多个父类。
class Flyable:
def fly(self) -> None:
print("飞行")
class Swimmable:
def swim(self) -> None:
print("游泳")
class Duck(Flyable, Swimmable):
pass
使用:
duck = Duck()
duck.fly()
duck.swim()
多重继承可以使用,但不应过度复杂。
查看类的方法查找顺序:
print(Duck.mro())
或者:
print(Duck.__mro__)
Python 会按照 MRO 顺序查找方法。
组合是一个对象内部包含另一个对象。
class Engine:
def start(self) -> None:
print("发动机启动")
class Car:
def __init__(self) -> None:
self.engine = Engine()
def start(self) -> None:
self.engine.start()
print("汽车启动")
使用:
car = Car()
car.start()
结果:
发动机启动
汽车启动
组合表示:
Car 拥有一个 Engine
继承表示:
Dog 是一种 Animal
选择原则:
是一个 → 继承
有一个 → 组合
实际项目中,通常优先考虑组合,而不是过度使用继承。
如果一个类主要用于保存数据,可以使用 dataclass 简化代码。
from dataclasses import dataclass
普通类:
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
数据类:
@dataclass
class Stock:
code: str
name: str
price: float
创建对象:
stock = Stock(
code="600519",
name="贵州茅台",
price=1500.0,
)
自动生成:
__init____repr____eq__
输出:
print(stock)
结果类似:
Stock(code='600519', name='贵州茅台', price=1500.0)
@dataclass
class Stock:
code: str
name: str
price: float = 0.0
industry: str = "未知"
创建:
stock = Stock(
code="600519",
name="贵州茅台",
)
无默认值字段必须放在有默认值字段前面。
正确:
@dataclass
class Stock:
code: str
name: str
price: float = 0.0
错误:
@dataclass
class Stock:
price: float = 0.0
code: str
列表、字典、集合等可变默认值,不能直接写:
@dataclass
class Portfolio:
codes: list[str] = []
这会报错。
正确:
from dataclasses import dataclass, field
@dataclass
class Portfolio:
codes: list[str] = field(
default_factory=list
)
字典:
@dataclass
class Portfolio:
stocks: dict[str, str] = field(
default_factory=dict
)
初始化完成后执行额外校验:
@dataclass
class Stock:
code: str
name: str
price: float
def __post_init__(self) -> None:
self.code = self.code.strip()
if len(self.code) != 6:
raise ValueError(
"股票代码必须为 6 位"
)
if self.price < 0:
raise ValueError(
"价格不能小于零"
)
创建对象时会自动执行:
stock = Stock(
" 600519 ",
"贵州茅台",
1500.0,
)
@dataclass(frozen=True)
class Point:
x: float
y: float
创建:
point = Point(10, 20)
不能修改:
point.x = 100
适合不可变数据对象。
Python 3.10 以上可以使用:
@dataclass(slots=True)
class Stock:
code: str
name: str
price: float
优点:
- 减少内存占用
- 防止随意添加新属性
- 属性访问通常更快
例如:
stock.industry = "白酒"
如果未定义 industry,会报错。
普通类也可以使用 __slots__。
class Stock:
__slots__ = (
"code",
"name",
"price",
)
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
对象不能动态添加未声明属性:
stock.industry = "白酒"
会报错。
适合大量对象、字段固定的场景。
class Stock:
market: str = "A股"
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code: str = code
self.name: str = name
self.price: float = price
类型提示帮助编辑器理解属性类型。
普通类可以手动转换:
class Stock:
def __init__(
self,
code: str,
name: str,
price: float,
) -> None:
self.code = code
self.name = name
self.price = price
def to_dict(
self,
) -> dict[str, object]:
return {
"code": self.code,
"name": self.name,
"price": self.price,
}
调用:
stock = Stock(
"600519",
"贵州茅台",
1500.0,
)
data = stock.to_dict()
from dataclasses import asdict, dataclass
@dataclass
class Stock:
code: str
name: str
price: float
转换:
stock = Stock(
"600519",
"贵州茅台",
1500.0,
)
data = asdict(stock)
结果:
{
"code": "600519",
"name": "贵州茅台",
"price": 1500.0,
}
使用字典:
stock = {
"code": "600519",
"name": "贵州茅台",
"price": 1500.0,
}
优点:
- 简单
- 灵活
- 适合 JSON
- 适合临时数据
缺点:
- 键容易拼错
- 缺少行为
- 类型不明确
- 大型项目难维护
使用类:
stock = Stock(
code="600519",
name="贵州茅台",
price=1500.0,
)
优点:
- 结构明确
- 属性和行为统一
- 编辑器提示更好
- 容易校验数据
- 适合复杂项目
简单数据处理可以用字典。
当数据具有明确结构和行为时,可以考虑类。
适合使用类:
- 数据具有固定字段
- 数据具有相关行为
- 需要创建多个同类型对象
- 需要维护对象状态
- 需要继承或多态
- 项目逻辑越来越复杂
例如:
股票
订单
用户
数据库连接
交易策略
文件解析器
HTTP 客户端
配置对象
不一定需要类:
简单数学计算
一次性数据清洗
只有几个步骤的小脚本
无状态工具函数
不应该为了面向对象而强行使用类。
不推荐:
class StockSystem:
# 读取 Excel
# 请求网络
# 分析股票
# 保存数据库
# 发送邮件
pass
推荐拆分:
class StockLoader:
pass
class StockAnalyzer:
pass
class StockRepository:
pass
class NotificationService:
pass
class Stock:
def __init__(
self,
code: str,
price: float,
) -> None:
self.code = code
self.price = price
def update_price(
self,
new_price: float,
) -> None:
self.price = new_price
上帝类指一个类承担几乎所有功能。
例如:
class Application:
# 上千行代码
# 所有业务逻辑
pass
这种类难以测试和维护。
不推荐为了复用几行代码而建立复杂继承层级。
优先考虑:
class Analyzer:
def __init__(
self,
loader: StockLoader,
) -> None:
self.loader = loader
如果属性有规则,使用:
- 初始化校验
property- 数据类
__post_init__ - 专门更新方法
例如:
def update_price(
self,
price: float,
) -> None:
if price < 0:
raise ValueError(
"价格不能小于零"
)
self.price = price
from dataclasses import dataclass
定义:
@dataclass(slots=True)
class Stock:
code: str
name: str
price: float
previous_close: float
industry: str = "未知"
def __post_init__(self) -> None:
self.code = self.code.strip()
self.name = self.name.strip()
if len(self.code) != 6:
raise ValueError(
"股票代码必须为 6 位"
)
if not self.code.isdigit():
raise ValueError(
"股票代码必须全部是数字"
)
if self.price < 0:
raise ValueError(
"股票价格不能小于零"
)
if self.previous_close <= 0:
raise ValueError(
"昨收价格必须大于零"
)
@property
def change_amount(self) -> float:
return self.price - self.previous_close
@property
def change_percent(self) -> float:
return (
self.change_amount
/ self.previous_close
* 100
)
def is_main_board(self) -> bool:
return self.code.startswith(
("00", "60")
)
def update_price(
self,
new_price: float,
) -> None:
if new_price < 0:
raise ValueError(
"价格不能小于零"
)
self.price = new_price
def to_dict(
self,
) -> dict[str, object]:
return {
"code": self.code,
"name": self.name,
"price": self.price,
"previous_close": (
self.previous_close
),
"industry": self.industry,
"change_amount": (
self.change_amount
),
"change_percent": (
self.change_percent
),
}
def __str__(self) -> str:
return (
f"{self.code} {self.name} "
f"{self.price:.2f} "
f"{self.change_percent:+.2f}%"
)
使用:
stock = Stock(
code="600519",
name="贵州茅台",
price=1520.0,
previous_close=1500.0,
industry="白酒",
)
print(stock)
print(stock.is_main_board())
print(stock.change_amount)
print(stock.change_percent)
class Portfolio:
def __init__(
self,
name: str,
) -> None:
self.name = name
self._stocks: dict[
str,
Stock,
] = {}
添加股票:
def add_stock(
self,
stock: Stock,
) -> None:
self._stocks[stock.code] = stock
删除股票:
def remove_stock(
self,
code: str,
) -> Stock | None:
return self._stocks.pop(
code,
None,
)
查找股票:
def get_stock(
self,
code: str,
) -> Stock | None:
return self._stocks.get(code)
获取主板股票:
def get_main_board_stocks(
self,
) -> list[Stock]:
return [
stock
for stock in self._stocks.values()
if stock.is_main_board()
]
按涨幅排序:
def sort_by_change_percent(
self,
*,
reverse: bool = True,
) -> list[Stock]:
return sorted(
self._stocks.values(),
key=lambda stock: (
stock.change_percent
),
reverse=reverse,
)
统计数量:
def __len__(self) -> int:
return len(self._stocks)
支持成员判断:
def __contains__(
self,
code: object,
) -> bool:
return code in self._stocks
支持遍历:
def __iter__(self):
return iter(
self._stocks.values()
)
完整使用:
portfolio = Portfolio(
"自选股"
)
portfolio.add_stock(
Stock(
"600519",
"贵州茅台",
1520.0,
1500.0,
"白酒",
)
)
portfolio.add_stock(
Stock(
"000001",
"平安银行",
11.8,
11.5,
"银行",
)
)
portfolio.add_stock(
Stock(
"300750",
"宁德时代",
295.0,
300.0,
"电池",
)
)
查看数量:
print(len(portfolio))
判断:
print(
"600519" in portfolio
)
遍历:
for stock in portfolio:
print(stock)
排序:
for stock in (
portfolio.sort_by_change_percent()
):
print(stock)
定义抽象父类:
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class Order(ABC):
code: str
quantity: int
def __post_init__(self) -> None:
if self.quantity <= 0:
raise ValueError(
"交易数量必须大于零"
)
@abstractmethod
def execute(self) -> None:
pass
市价单:
@dataclass
class MarketOrder(Order):
def execute(self) -> None:
print(
f"按市价交易 "
f"{self.code} "
f"{self.quantity} 股"
)
限价单:
@dataclass
class LimitOrder(Order):
limit_price: float
def __post_init__(self) -> None:
super().__post_init__()
if self.limit_price <= 0:
raise ValueError(
"限价必须大于零"
)
def execute(self) -> None:
print(
f"以 {self.limit_price:.2f} "
f"限价交易 "
f"{self.code} "
f"{self.quantity} 股"
)
统一执行:
def execute_orders(
orders: list[Order],
) -> None:
for order in orders:
order.execute()
使用:
orders: list[Order] = [
MarketOrder(
code="600519",
quantity=100,
),
LimitOrder(
code="000001",
quantity=500,
limit_price=11.5,
),
]
execute_orders(orders)
这体现了:
- 抽象类
- 继承
- 方法重写
- 多态
错误:
class Person:
def show_name():
print("张三")
调用:
person = Person()
person.show_name()
会出现参数错误。
正确:
class Person:
def show_name(self) -> None:
print("张三")
错误:
class Person:
def __init__(
self,
name: str,
) -> None:
name = name
对象没有 name 属性。
正确:
self.name = name
不推荐:
class Portfolio:
stocks = []
所有对象共享同一个列表。
portfolio1 = Portfolio()
portfolio2 = Portfolio()
portfolio1.stocks.append(
"600519"
)
print(portfolio2.stocks)
也会看到:
["600519"]
正确:
class Portfolio:
def __init__(self) -> None:
self.stocks = []
每个对象拥有独立列表。
不推荐:
stock._price = -100
应该通过公开接口:
stock.price = 100
或:
stock.update_price(100)
例如:
A
└── B
└── C
└── D
└── E
方法来源难以判断,维护成本高。
优先使用较浅继承和组合。
例如:
class MathHelper:
@staticmethod
def add(a, b):
return a + b
这种情况下普通函数更简单:
def add(a, b):
return a + b
如果一个类同时:
- 读取文件
- 抓取网页
- 分析数据
- 写数据库
- 发送邮件
应该拆分。
创建一个 Person 类,包含:
- 姓名
- 年龄
- 自我介绍方法
参考答案:
class Person:
def __init__(
self,
name: str,
age: int,
) -> None:
self.name = name
self.age = age
def introduce(self) -> None:
print(
f"我叫{self.name},"
f"今年{self.age}岁"
)
创建一个 Rectangle 类,包含:
- 宽度
- 高度
- 面积属性
- 周长属性
参考答案:
class Rectangle:
def __init__(
self,
width: float,
height: float,
) -> None:
if width <= 0 or height <= 0:
raise ValueError(
"宽度和高度必须大于零"
)
self.width = width
self.height = height
@property
def area(self) -> float:
return (
self.width
* self.height
)
@property
def perimeter(self) -> float:
return 2 * (
self.width
+ self.height
)
创建一个银行账户类,包含:
- 账户名称
- 余额
- 存款
- 取款
- 查询余额
参考答案:
class BankAccount:
def __init__(
self,
owner: str,
balance: float = 0,
) -> None:
if balance < 0:
raise ValueError(
"初始余额不能小于零"
)
self.owner = owner
self._balance = balance
@property
def balance(self) -> float:
return self._balance
def deposit(
self,
amount: float,
) -> None:
if amount <= 0:
raise ValueError(
"存款金额必须大于零"
)
self._balance += amount
def withdraw(
self,
amount: float,
) -> None:
if amount <= 0:
raise ValueError(
"取款金额必须大于零"
)
if amount > self._balance:
raise ValueError(
"余额不足"
)
self._balance -= amount
使用数据类创建股票对象。
参考答案:
from dataclasses import dataclass
@dataclass
class Stock:
code: str
name: str
price: float
def is_main_board(self) -> bool:
return self.code.startswith(
("00", "60")
)
创建父类 Animal,子类 Dog 和 Cat,分别重写 speak()。
参考答案:
class Animal:
def speak(self) -> None:
raise NotImplementedError
class Dog(Animal):
def speak(self) -> None:
print("汪汪")
class Cat(Animal):
def speak(self) -> None:
print("喵喵")
创建一个 Student 类,支持按照成绩比较大小。
参考答案:
class Student:
def __init__(
self,
name: str,
score: float,
) -> None:
self.name = name
self.score = score
def __lt__(
self,
other: "Student",
) -> bool:
return self.score < other.score
def __repr__(self) -> str:
return (
f"Student("
f"name={self.name!r}, "
f"score={self.score!r}"
f")"
)
使用:
students = [
Student("张三", 85),
Student("李四", 92),
Student("王五", 78),
]
students.sort()
定义类:
class Person:
pass
创建对象:
person = Person()
初始化:
class Person:
def __init__(
self,
name: str,
) -> None:
self.name = name
实例方法:
def introduce(self) -> None:
print(self.name)
类属性:
class Person:
category = "人类"
类方法:
@classmethod
def create(cls):
return cls()
静态方法:
@staticmethod
def validate(value):
return True
属性控制:
@property
def price(self):
return self._price
继承:
class Dog(Animal):
pass
调用父类:
super().__init__()
数据类:
@dataclass
class Stock:
code: str
name: str
面向对象的四个核心概念:
封装
继承
多态
抽象
实际设计时可以先问:
这个对象有哪些数据?
这个对象有哪些行为?
哪些数据需要校验?
哪些行为属于对象本身?
是否真的需要继承?
是否可以使用组合?
这个类是否承担了太多职责?
类的本质是:
数据
+
操作数据的方法
+
对象之间的关系
当程序从简单脚本发展为复杂系统时,合理使用类可以显著提高代码的结构性、可读性和可维护性。