三、类
类型注解(typing、pydantic)
属性可以指定类型注解和默认值。
Python中创建对象不使用new关键字。
python
class User:
name: str
country: str = "China"
# China
print(User().country)
java
// java创建对象使用new关键字
User user = new User();
python
# 很多编程语言创建对象都使用关键字new
user = User()
typing
定义类时常结合typing显示指定类型。
-
List[T]:元素类型为 T 的列表
-
Dict[K, V]:键类型 K、值类型 V 的字典
-
Optional[T]:等价于 Union[T, None],表示可以是 T 或 None
-
Union[T1, T2, ...]:允许多种类型之一
-
Any:任意类型(不进行静态类型检查)
python
from typing import List, Dict, Optional, Union, Any
class User:
def __init__(
self,
user_id: int,
name: str,
tags: List[str], # 字符串列表
metadata: Dict[str, Any], # 键为字符串,值为任意类型的字典
age: Optional[int] = None, # 可以是 int 或 None
role: Union[str, int] = "user" # 可以是字符串或整数
) -> None:
self.user_id = user_id
self.name = name
self.tags = tags
self.metadata = metadata
self.age = age
self.role = role
# 使用示例
if __name__ == "__main__":
user = User(
user_id=1,
name="Alice",
tags=["admin", "premium"],
metadata={"signup_date": "2025-01-01", "score": 95},
age=30,
role="moderator"
)
print(user.name, user.tags)
pydantic
class定义通常还使用pydantic进行设置一些默认值或者参数合法检查。
Field:
- default:默认值,default=[ ] :所有模型实例会共享同一个列表对象,修改一个实例的 会影响其他实例(经典的可变默认值陷阱),注意列表要用default_factory。
- default_factory:动态生成,接收任何无参数的 callable,list表示每次自动执行 list(),生成全新空列表。常用于dict、set、datetime.now、uuid4
| 参数 | 作用 | 示例 |
|---|---|---|
... (省略号) |
必填字段 | message: str = Field(...) |
default |
默认值 | session_id: str = Field(default=None) |
default_factory |
默认工厂 | decisions: list = Field(default_factory=list) |
description |
字段描述 | description="用户消息" |
ge / le |
数值范围 | confidence: float = Field(ge=0.0, le=1.0) |
min_length / max_length |
字符串长度 | message: str = Field(max_length=8000) |
python
class MeetingSummary(BaseModel):
"""会议纪要"""
title:str=Field(...,description="会议标题")
date: str = Field(...,description="会议日期")
attendees:List[str]= Field(...,description="参会人员")
summary: str=Field(...,description="会议摘")
decisions: List[str]=Field(default_factory=list, description="决议事项")
action_items: List[MeetingAction]= Field(default_factory=list, description="行动顶")
notes:str=Field(default=None,description="备注")
python
from enum import Enum
from pydantic import BaseModel, Field,ValidationError
class Intent(str, Enum):
"""用户意图枚举"""
CHAT = "chat" # 闲聊问答
MEETING = "meeting" #会议纪要
UNKNOWN = "unknown" # 未知
class RouterDecision(BaseModel):
"""路由决策结果"""
intent: Intent # 识别到的意图
confidence:float = Field(ge=0.0,le=1.0) # 置信度0-1
reasoning: str # 推理过程
extracted_info:dict = None # 提取的信息
decision =RouterDecision(
intent=Intent.CHAT,
confidence-0.95,
reasoning="用户在进行日常对话"
)
functools
bash
from functools import lru_cache
@lru_cache
def get_settings() -> Settings:
"""获取全局配置单例
使用 lru_cache 缓存配置对象,避免重复读取 .env 文件。
"""
return Settings()
实例类型判断
python
if isinstance(msg, AIMessage):
pass
elif isinstance(msg, HumanMessage):
pass
多重继承
多个继承使用逗号分隔。
python
from enum import Enum
class Intent(str, Enum):
"""用户意图枚举"""
CHAT = "chat" # 闲聊问答
MEETING = "meeting" #会议纪要
UNKNOWN = "unknown" # 未知
self
对象方法必须有参数,而且第一个参数必须是self。注意:self并不是Python的关键字,处于习惯大家都默认使用self,Java中使用关键字this。默认是继承 object ,可以省略。
java
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + '\'' + ", age=" + age + '}';
}
}
py
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"User(name={self.name}, age={self.age})"
Python初始化方法 __init__ 相当于 Java的构造方法,只不过不用在最上面声明属性,相对来说更简洁。
py
import keyword
# 查看Python中的关键字列表
print(keyword.kwlist)
魔法方法
Python中方法名以双下划线__作为前缀和后缀的方法叫做魔法方法,魔法方法相当于Java的接口方法,具有特定的功能。
__new__:实例化方法,先执行__new__再执行__init__。__init__:初始化方法,常用于类的构造方法,例如给属性初始化None, 如 self.xxx = None。__str__:toString()方法。__eq__:用于比较两个对象是否相等。__iter__:迭代器通常与__next__配合使用。__next__:表示迭代器对应的next()方法。__add__:加法运算符 +__or__:或运算符 |__mul__:乘法运算 *,如 字符串乘以数字,print("-" * 20)__mod__: 取余,self % value,常见的字符串拼接,print("name=%s age=%d" % (name, age))__gt__:大于 >__ge__:大于等于 > =__lt__:小于<__le__:小于等于 <=__call__:让"对象像函数一样可调用"
python
class Adder:
def __init__(self, n):
self.n = n
def __call__(self, x):
return x + self.n
add5 = Adder(5)
# 这里 add5 是对象,但可以像函数一样调用
print(add5(10)) # 15
有了魔法方法,只要类中实现了魔法方法就可以通过符号来操作类了,如 对象之间比较大小,重写__or__来实现管道 | 的作用。
python
class MyList:
def __init__(self, items=None):
self.items = items or []
def __or__(self, other):
"""实现 self | other 操作"""
# 创建新实例,避免修改原对象
new_list = MyList(self.items.copy())
new_list.items.append(other)
return new_list
def __repr__(self):
return f"MyList({self.items})"
# 使用示例
ml = MyList([1, 2, 3])
ml2 = ml | 4 | 5 | 6 # 链式调用
print(ml) # MyList([1, 2, 3]) - 原对象不变
print(ml2) # MyList([1, 2, 3, 4, 5, 6])
访问权限约定
Python所提倡的用更多的符号来代替关键字,在Java中定义私有变量要使用private关键字。
- 私有属性在属性名前使用双下划线命名规则。如:__password
- 类的私有方法也是在方法名前使用双下划线前缀。如 __set_password
- 访问私有属性或者私有方法的规范是:_类名__私有成员
py
class Person:
country = "China"
def say_hello(self):
print("I'm Chinese")
class Student(Person, Chinese):
name = None
age = 0
__weight = 60
def __init__(self):
pass
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def say_hello(self):
# 方式一:显式调用父类方法使用super()方法,而不是super关键字
super().say_hello()
# 方式二:通过类名指定父类的方法(多继承时此种方式更加直观)
Person.say_hello(self)
print("I'm Student")
def __private_func(self):
print("私有函数")
def __add__(self, other):
return Student(self.name + other.name, self.age + other.age)
zhangsan = Student("zhangsan", 26)
zhangsan.say_hello()
super(Student, zhangsan).say_hello()
lisi = Student("lisi", 24)
zhangli = zhangsan + lisi
print(zhangli.age)
# 访问私有属性和方法
print(zhangsan._Person__gender)
zhangsan._Person__private_func
# 多重继承时,当方法名重名时优先调用子类的方法,然后再按照继承的先后顺序优先使用前面的顺序,__mro__返回父类的继承顺序
# MRO(method resolution order)方法解析顺序
print(zhangsan.__mro__)
抽象方法 @abstractmethod
Python认为一个方法没有方法体,即方法体为pass就可以认为是一个抽象方法,Python一直在倡导约定大于语法,所以也没有提供什么语法强制校验,毕竟程序员要对自己写的代码负责。Python虽然没有把抽象方法作为一级语法,但是也可以在运行时检测抽象方法有没有实现,没有实现就抛异常,需要通过abc模块来检查。TypeError: Can't instantiate abstract class Chinese with abstract method hello。
Python中只有class关键字,没有接口interface关键字,Python认为一个类中的所有方法的方法体都是pass,那么这个类就算一个接口。Python中也没有接口,抽象类就是接口,这也是Python简化代码的一个重要提现。
python
from abc import ABC, abstractmethod
class Person(ABC):
def __init__(self, name=None, age=None):
self._name = name
self._age = age
@abstractmethod
def hello(self):
pass
class Chinese(Person):
pass
if __name__ == '__main__':
chinese = Chinese()
chinese.hello()
静态方法 @staticmethod 和类方法 @classmethod
- @staticmethod: 方法逻辑与类相关,但不需要任何类或实例数据时使用(如工具函数),参数列表中第一个参数不需要 cls或self,直接定义真实的参数,类似普通函数,类似于Java中的StringUtils.isBank()
python
class MathUtils:
@staticmethod
def is_even(number):
return number % 2 == 0
@staticmethod
def validate_email(email):
return "@" in email and "." in email
# 使用
print(MathUtils.is_even(10)) # True
print(MathUtils.validate_email("test@example.com")) # True
@classmethod: 需要访问或修改类状态、实现多态工厂方法时使用
python
class Person:
count = 0
def __init__(self, name):
self.name = name
Person.count += 1
@classmethod
def get_count(cls):
return cls.count # 访问类属性
@classmethod
def from_birth_year(cls, name, birth_year):
"""工厂方法:根据出生年份创建实例"""
age = 2026 - birth_year
return cls(name, age) # 创建实例
# 使用
print(Person.get_count()) # 0
person = Person.from_birth_year("张三", 2000)
多态(鸭子类型🦆)
鸭子类型(Duck Typing)是指如果它走起来像鸭子,叫起来叫鸭子,那么它就是鸭子。
在鸭子类型中,关注的是对象的行为而不是对象的类型。
在Python中多态不依赖是否有继承关系,只要有相同的方法即可。Python中的类型只是一种注释,一种提示,并不会导致编译错误,所以多态的所谓类型在Python中没有任何作用。
Python中的多态更像是Java中的接口中的多态,拥有相同的方法。
类型对象
在 Python 里,类本身也是对象,所以可以像普通值一样当作实参传进去。
"类型对象"传给了构造函数,供内部做检查/反射/实例化等。
python
class State:
name: str
def print_state(state: type):
print(state)
# <class '__main__.State'>
print_state(State)
dataclasses 类似于Java的反射,将类名作为参数传参 MiniGraph(State) ,类似于Java中的Class对象,MiniGraph(State.class)
python
from dataclasses import dataclass, fields
# 1) 定义一个"状态类型"
@dataclass
class State:
count: int
name: str
# 2) 一个接收"类型"的类
class MiniGraph:
def __init__(self, state_type: type):
self.state_type = state_type # 保存传入的类
# 用这个类做一些事情:读取字段信息(反射)
self.field_names = [f.name for f in fields(state_type)]
def create_state(self, **kwargs):
# 用传入的类来创建实例
return self.state_type(**kwargs)
def show_schema(self):
print("state_type:", self.state_type.__name__)
print("fields:", self.field_names)
# 3) 传入"类名"作为实参
graph = MiniGraph(State) # 注意:不是 State(),而是 State
graph.show_schema()
s = graph.create_state(count=1, name="Alice")
print(s)
print(type(s))
"""
state_type: State
fields: ['count', 'name']
State(count=1, name='Alice')
<class '__main__.State'>
"""
关键点:
- State 是类对象(type instance),可传参。
- State() 才是实例对象。
- 这种写法常用于框架里:传类型给框架,框架再基于类型做校验、序列化、自动建模等。