__str__ | str()print(), format() 默认 | |||
__repr__ | repr() | eval()) | ||
__format__ | format()str.format() | |||
__bytes__ | bytes() | bytes |
__str__ 方法:用户友好表示class Person:def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):"""返回用户友好的字符串表示"""return f"Person: {self.name}, {self.age} years old"# 如果没有定义 __str__,Python会使用 __repr__ 代替# 使用p = Person("Alice", 25)print(p) # 隐式调用 __str__print(str(p)) # 显式调用 __str__
class Product:"""产品类 - 展示给用户的信息"""def __init__(self, id, name, price, category):self.id = idself.name = nameself.price = priceself.category = categorydef __str__(self):"""用户看到的格式:清晰、易读"""return f"{self.name} - ¥{self.price:.2f} ({self.category})"def display_details(self):"""更详细的显示方法"""return f"""产品详情:-----------名称:{self.name}价格:¥{self.price:.2f}分类:{self.category}编号:{self.id}-----------"""# 使用product = Product("P001", "Python编程从入门到精通", 89.90, "图书")print(product) # Python编程从入门到精通 - ¥89.90 (图书)print(product.display_details())
__repr__ 方法:开发者友好表示class Point:def __init__(self, x, y):self.x = xself.y = ydef __repr__(self):"""原则:应该返回一个字符串,能够用于重新创建对象格式通常为:ClassName(arg1, arg2, ...)"""return f"Point({self.x}, {self.y})"# __repr__ 应该尽量详细,包含重建对象所需的所有信息# 使用p = Point(3, 4)print(repr(p)) # Point(3, 4)# 理论上可以这样重建对象(如果实现正确)# eval(repr(p)) # 创建一个新的Point对象
from datetime import datetimeimport jsonclass Order:def __init__(self, order_id, customer, items, order_date=None):self.order_id = order_idself.customer = customerself.items = itemsself.order_date = order_date or datetime.now()def __repr__(self):"""详细的、可重建的表示"""return (f"Order(order_id={repr(self.order_id)}, "f"customer={repr(self.customer)}, "f"items={repr(self.items)}, "f"order_date={repr(self.order_date)})")def __str__(self):"""用户友好的表示"""return f"订单 #{self.order_id} - {self.customer}"# 使用order = Order(order_id="ORD20231215001",customer="张三",items=["Python书籍", "鼠标", "键盘"],order_date=datetime(2023, 12, 15, 10, 30))print(str(order)) # 订单 #ORD20231215001 - 张三print(repr(order)) # Order(order_id='ORD20231215001', customer='张三', items=['Python书籍', '鼠标', '键盘'], order_date=datetime.datetime(2023, 12, 15, 10, 30))# 在调试时特别有用import pprintpprint.pprint(repr(order))
__format__ 方法:格式化控制class Temperature:def __init__(self, celsius):self.celsius = celsius@propertydef fahrenheit(self):return self.celsius * 9/5 + 32@propertydef kelvin(self):return self.celsius + 273.15def __format__(self, format_spec):"""format_spec: 格式化说明符,如 'c', 'f', 'k', '.1f' 等"""if not format_spec:# 默认格式:摄氏度,保留1位小数return f"{self.celsius:.1f}°C"if format_spec == 'c':return f"{self.celsius:.1f}°C"elif format_spec == 'f':return f"{self.fahrenheit:.1f}°F"elif format_spec == 'k':return f"{self.kelvin:.1f}K"elif format_spec.startswith('c'):# 自定义精度:c.2 表示摄氏度保留2位小数try:precision = int(format_spec[1:])return f"{self.celsius:.{precision}f}°C"except ValueError:return f"{self.celsius:.1f}°C"else:# 使用Python的默认格式化return format(self.celsius, format_spec)# 使用temp = Temperature(25.5)print(f"默认格式: {temp}") # 25.5°Cprint(f"摄氏度: {temp:c}") # 25.5°Cprint(f"华氏度: {temp:f}") # 77.9°Fprint(f"开氏度: {temp:k}") # 298.7Kprint(f"摄氏度(2位): {temp:c.2}") # 25.50°Cprint(f"科学计数: {temp:e}") # 2.550000e+01°C
from datetime import datetime, timedeltaclass Timer:def __init__(self, seconds):self.seconds = secondsdef __format__(self, format_spec):"""支持多种时间格式:- 's': 只显示秒- 'm': 分:秒- 'h': 时:分:秒- 'full': 完整的时间分解"""if not format_spec:format_spec = 'h' # 默认格式if format_spec == 's':return f"{self.seconds}秒"elif format_spec == 'm':minutes = self.seconds // 60seconds = self.seconds % 60return f"{minutes:02d}:{seconds:02d}"elif format_spec == 'h':hours = self.seconds // 3600minutes = (self.seconds % 3600) // 60seconds = self.seconds % 60return f"{hours:02d}:{minutes:02d}:{seconds:02d}"elif format_spec == 'full':days = self.seconds // 86400hours = (self.seconds % 86400) // 3600minutes = (self.seconds % 3600) // 60seconds = self.seconds % 60parts = []if days > 0:parts.append(f"{days}天")if hours > 0 or parts:parts.append(f"{hours}小时")if minutes > 0 or parts:parts.append(f"{minutes}分")parts.append(f"{seconds}秒")return " ".join(parts)else:# 默认:按秒格式化return format(self.seconds, format_spec)# 使用timer = Timer(3665) # 1小时1分钟5秒print(f"默认格式: {timer}") # 01:01:05print(f"秒格式: {timer:s}") # 3665秒print(f"分:秒格式: {timer:m}") # 61:05print(f"时:分:秒格式: {timer:h}") # 01:01:05print(f"完整格式: {timer:full}") # 1小时1分5秒print(f"科学计数: {timer:e}") # 3.665000e+03
__bytes__ 方法:字节表示class Serializable:def __init__(self, data):self.data = datadef __bytes__(self):"""返回对象的字节表示"""# 通常需要序列化为字节import picklereturn pickle.dumps(self.data)@classmethoddef from_bytes(cls, byte_data):"""从字节重建对象"""import pickledata = pickle.loads(byte_data)return cls(data)# 使用obj = Serializable({"name": "Alice", "age": 25})byte_repr = bytes(obj)print(f"字节表示: {byte_repr}")print(f"长度: {len(byte_repr)} 字节")# 重建对象new_obj = Serializable.from_bytes(byte_repr)print(f"重建的数据: {new_obj.data}")
import structfrom typing import Listclass Vector3D:"""三维向量,支持二进制序列化"""def __init__(self, x: float, y: float, z: float):self.x = xself.y = yself.z = zdef __bytes__(self):"""使用 struct 打包为二进制'fff' 表示三个浮点数(4字节每个)"""return struct.pack('fff', self.x, self.y, self.z)def __str__(self):return f"Vector3D({self.x}, {self.y}, {self.z})"def __repr__(self):return f"Vector3D({self.x}, {self.y}, {self.z})"@classmethoddef from_bytes(cls, data: bytes):"""从字节重建向量"""x, y, z = struct.unpack('fff', data)return cls(x, y, z)class Mesh:"""网格,包含多个顶点"""def __init__(self, name: str, vertices: List[Vector3D]):self.name = nameself.vertices = verticesdef __bytes__(self):"""二进制格式:- 4字节:名称长度- 名称(UTF-8编码)- 4字节:顶点数量- 每个顶点:12字节(3个float)"""# 编码名称name_bytes = self.name.encode('utf-8')name_len = len(name_bytes)# 打包头部信息header = struct.pack(f'I{name_len}sI',name_len, name_bytes, len(self.vertices))# 打包所有顶点vertices_data = b''.join(bytes(v) for v in self.vertices)return header + vertices_data@classmethoddef from_bytes(cls, data: bytes):"""从字节重建网格"""# 解析头部name_len = struct.unpack('I', data[:4])[0]name = struct.unpack(f'{name_len}s', data[4:4+name_len])[0].decode('utf-8')vertex_count = struct.unpack('I', data[4+name_len:8+name_len])[0]# 解析顶点vertices = []offset = 8 + name_lenfor _ in range(vertex_count):vertex_data = data[offset:offset+12]vertices.append(Vector3D.from_bytes(vertex_data))offset += 12return cls(name, vertices)def __str__(self):return f"Mesh '{self.name}' with {len(self.vertices)} vertices"def __repr__(self):return f"Mesh(name={repr(self.name)}, vertices={self.vertices})"# 使用示例print("=== 单个向量 ===")v1 = Vector3D(1.0, 2.0, 3.0)v1_bytes = bytes(v1)print(f"向量: {v1}")print(f"字节表示: {v1_bytes}")print(f"长度: {len(v1_bytes)} 字节")# 重建v1_reconstructed = Vector3D.from_bytes(v1_bytes)print(f"重建的向量: {v1_reconstructed}")print(f"是否相等: {v1.x == v1_reconstructed.x}")print("\n=== 网格对象 ===")vertices = [Vector3D(0, 0, 0),Vector3D(1, 0, 0),Vector3D(0, 1, 0),Vector3D(1, 1, 0)]mesh = Mesh("triangle_mesh", vertices)mesh_bytes = bytes(mesh)print(f"网格: {mesh}")print(f"网格字节长度: {len(mesh_bytes)} 字节")# 重建网格mesh_reconstructed = Mesh.from_bytes(mesh_bytes)print(f"重建的网格: {mesh_reconstructed}")
from dataclasses import dataclass, asdictfrom datetime import datetimeimport jsonimport structfrom typing import Any, Dict@dataclassclass DataRecord:"""数据记录类,支持多种表示形式"""id: intname: strvalue: floattimestamp: datetimemetadata: Dict[str, Any] = Nonedef __post_init__(self):if self.metadata is None:self.metadata = {}def __str__(self) -> str:"""用户友好表示"""return (f"记录 #{self.id}: {self.name} = {self.value:.2f} "f"({self.timestamp.strftime('%Y-%m-%d %H:%M:%S')})")def __repr__(self) -> str:"""开发者表示,可重建"""return (f"DataRecord(id={self.id}, name={repr(self.name)}, "f"value={self.value}, timestamp={repr(self.timestamp)}, "f"metadata={self.metadata})")def __format__(self, format_spec: str) -> str:"""格式化表示"""if not format_spec:return str(self)format_spec = format_spec.lower()if format_spec == 'json':return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)elif format_spec == 'csv':return f"{self.id},{self.name},{self.value},{self.timestamp.isoformat()}"elif format_spec == 'table':return (f"| {self.id:4d} | {self.name:20s} | "f"{self.value:8.2f} | {self.timestamp:%Y-%m-%d %H:%M} |")elif format_spec.startswith('value'):# 只显示值,可指定精度:value.3try:if '.' in format_spec:precision = int(format_spec.split('.')[1])return f"{self.value:.{precision}f}"except (ValueError, IndexError):passreturn f"{self.value:.2f}"else:# 尝试应用格式说明符到值try:return format(self.value, format_spec)except (ValueError, TypeError):return str(self)def __bytes__(self) -> bytes:"""二进制表示"""# 序列化字符串字段name_bytes = self.name.encode('utf-8')name_len = len(name_bytes)# 序列化metadatametadata_json = json.dumps(self.metadata).encode('utf-8')metadata_len = len(metadata_json)# 序列化时间戳(Unix时间戳,8字节double)timestamp_float = self.timestamp.timestamp()# 打包所有数据# 格式:id(4B) + 名称长度(2B) + 名称 + 值(8B) + 时间戳(8B) + metadata长度(4B) + metadatareturn struct.pack(f'I H {name_len}s d d I {metadata_len}s',self.id,name_len,name_bytes,self.value,timestamp_float,metadata_len,metadata_json)@classmethoddef from_bytes(cls, data: bytes) -> 'DataRecord':"""从字节重建"""# 解析id和名称长度id_val = struct.unpack('I', data[:4])[0]name_len = struct.unpack('H', data[4:6])[0]# 解析名称name = struct.unpack(f'{name_len}s', data[6:6+name_len])[0].decode('utf-8')# 解析值和时间戳offset = 6 + name_lenvalue = struct.unpack('d', data[offset:offset+8])[0]timestamp_float = struct.unpack('d', data[offset+8:offset+16])[0]timestamp = datetime.fromtimestamp(timestamp_float)# 解析metadatametadata_len = struct.unpack('I', data[offset+16:offset+20])[0]metadata_json = struct.unpack(f'{metadata_len}s',data[offset+20:offset+20+metadata_len])[0]metadata = json.loads(metadata_json.decode('utf-8'))return cls(id_val, name, value, timestamp, metadata)def to_dict(self) -> dict:"""转换为字典"""return {'id': self.id,'name': self.name,'value': self.value,'timestamp': self.timestamp.isoformat(),'metadata': self.metadata}@classmethoddef from_dict(cls, data: dict) -> 'DataRecord':"""从字典重建"""return cls(id=data['id'],name=data['name'],value=data['value'],timestamp=datetime.fromisoformat(data['timestamp']),metadata=data.get('metadata', {}))# 使用示例def demonstrate_all_representations():print("=== DataRecord 所有表示方法演示 ===\n")# 创建记录record = DataRecord(id=1001,name="温度传感器",value=25.56789,timestamp=datetime.now(),metadata={"unit": "°C", "location": "实验室", "calibrated": True})print("1. __str__ (用户友好):")print(f" {record}")print()print("2. __repr__ (开发者):")print(f" {repr(record)}")print()print("3. __format__ (各种格式):")print(f" 默认: {record}")print(f" JSON: {record:json}")print(f" CSV: {record:csv}")print(f" 表格: {record:table}")print(f" 只显示值: {record:value}")print(f" 值(3位小数): {record:value.3}")print(f" 科学计数: {record:e}")print()print("4. __bytes__ (二进制):")record_bytes = bytes(record)print(f" 字节长度: {len(record_bytes)}")print(f" 前50字节: {record_bytes[:50]}...")print()print("5. 从字节重建:")reconstructed = DataRecord.from_bytes(record_bytes)print(f" 重建的记录: {reconstructed}")print(f" 是否相等: {record == reconstructed}")print()print("6. f-string 格式化:")print(f" 记录: {record}")print(f" JSON格式: {record:json}")print(f" 表格格式: {record:table}")print("\n7. 在列表中使用:")records = [DataRecord(1001, "温度", 25.5, datetime.now()),DataRecord(1002, "湿度", 65.2, datetime.now()),DataRecord(1003, "压力", 101.3, datetime.now())]print(" 表格视图:")print(" " + "-" * 50)print(" | ID | 名称 | 值 | 时间 |")print(" " + "-" * 50)for r in records:print(f" {r:table}")print(" " + "-" * 50)demonstrate_all_representations()
__str__ 设计原则:目标用户:最终用户
内容:简洁、易读、有意义
格式:不需要包含所有细节
示例:"订单 #12345 - 张三"
__repr__ 设计原则:目标用户:开发者
内容:详细、完整、可重建
格式:通常为 ClassName(arg1, arg2, ...)
原则:eval(repr(obj)) == obj(理想情况)
示例:"Order(id=12345, customer='张三', total=199.99)"
__format__ 设计原则:目标用户:需要格式化输出的用户
内容:支持多种格式和样式
格式:根据 format_spec 动态变化
兼容:应该处理未知的格式说明符
示例:支持 f"{obj:json}"、f"{obj:.2f}" 等
__bytes__ 设计原则:目标用户:需要二进制表示的场景
内容:高效、紧凑、可逆
格式:通常使用 struct 模块
注意:考虑大小端和版本兼容性
示例:网络传输、文件存储、二进制协议
优先实现 __repr__:Python 在没有 __str__ 时会使用 __repr__
保持一致性:__repr__ 应该包含重建对象所需的所有信息
提供默认处理:在 __format__ 中处理空的或未知的格式说明符
考虑性能:__bytes__ 可能被频繁调用,应保持高效
支持双向转换:实现 __bytes__ 时,最好也提供对应的类方法从字节重建