第二十七篇:获取对象信息——type、isinstance、dir 完全指南
前言
在 Python 中,一切皆对象。理解如何查询和探索对象的类型、属性和方法是编写健壮代码的关键。本文将全面讲解 type()、isinstance()、dir() 以及 getattr()、setattr()、hasattr() 等核心工具的使用方法。
type() —— 查询对象的类型
基本用法
x = 42
print(type(x)) # <class 'int'>
name = "Python"
print(type(name)) # <class 'str'>
items = [1, 2, 3]
print(type(items)) # <class 'list'>
def hello():
print("Hello")
print(type(hello)) # <class 'function'>
class Dog:
pass
dog = Dog()
print(type(dog)) # <class '__main__.Dog'>
type() 用于动态创建类
# type() 不仅可以查询类型,还可以动态创建新类
# 语法:type(类名, 父类元组, 属性字典)
Person = type('Person', (), {
'__init__': lambda self, name: setattr(self, 'name', name),
'greet': lambda self: f"你好,我是{self.name}"
})
p = Person("小明")
print(p.greet()) # 你好,我是小明
isinstance() —— 类型判断的最佳实践
基本用法
value = "Hello World"
result = isinstance(value, str)
print(result) # True
number = 42
print(isinstance(number, int)) # True
print(isinstance(number, float)) # False
print(isinstance(number, (int, float))) # True — 判断多种类型之一
isinstance vs type 的区别
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
# ❌ type() 只检查精确类型,不识别继承关系
print(type(dog) == Animal) # False — Dog 不是 Animal 的直接 type
print(type(dog) == Dog) # True
# ✅ isinstance() 会检查整个继承链
print(isinstance(dog, Animal)) # True — Dog 是 Animal 的子类
print(isinstance(dog, Dog)) # True
# 推荐使用 isinstance,因为它支持多态
def make_speak(obj):
if isinstance(obj, Animal): # 任何 Animal 子类都可以
obj.speak()
isinstance 在实战中的应用
from typing import Union
def format_value(value):
"""根据类型格式化值"""
if isinstance(value, int):
return f"{value:,}"
elif isinstance(value, float):
return f"{value:.2f}"
elif isinstance(value, str):
return value.strip().upper()
elif isinstance(value, list):
return ", ".join(str(item) for item in value)
else:
return str(value)
print(format_value(12345)) # 12,345
print(format_value(3.14159)) # 3.14
print(format_value("hello")) # HELLO
print(format_value([1, 2, 3])) # 1, 2, 3
dir() —— 探索对象的属性和方法
列出对象的所有成员
# 列出列表可用的方法和属性
print(dir([]))
# ['append', 'clear', 'copy', 'count', 'extend', 'index',
# 'insert', 'pop', 'remove', 'reverse', 'sort']
# 列出字符串可用的方法
print(dir(""))
# ['capitalize', 'casefold', 'center', 'count', 'encode', ...]
# 列出模块的成员
import math
print(dir(math))
# ['pi', 'e', 'sqrt', 'sin', 'cos', 'tan', ...]
使用 dir() 进行交互式探索
# 在开发时快速查看对象结构
class User:
def __init__(self, name, email):
self.name = name
self.email = email
def greet(self):
return f"{self.name} 向你问好"
user = User("小明", "xiaoming@example.com")
# 查看所有属性
for attr in dir(user):
print(f"{attr}: {getattr(user, attr)}")
# 输出:
# __class__: <class '__main__.User'>
# __delattr__: <slot wrapper '__delattr__' of 'object' object>
# __dict__: {'name': '小明', 'email': 'xiaoming@example.com'}
# __dir__: <built-in method __dir__ of User object>
# __doc__: None
# __eq__: <slot wrapper '__eq__' of 'object' object>
# __format__: <built-in method __format__ of User object>
# __ge__: <slot wrapper '__ge__' of 'object' object>
# __getattribute__: <slot wrapper '__getattribute__' of 'object' object>
# __gt__: <slot wrapper '__gt__' of 'object' object>
# __hash__: <slot wrapper '__hash__' of 'object' object>
# __init__: <function User.__init__ at 0x...>
# __init_subclass__: <built-in method __init_subclass__ of type object>
# __le__: <slot wrapper '__le__' of 'object' object>
# __lt__: <slot wrapper '__lt__' of 'object' object>
# __module__: __main__
# __ne__: <slot wrapper '__ne__' of 'object' object>
# __new__: <built-in method __new__ of type object>
# __reduce__: <built-in method __reduce__ of User object>
# __reduce_ex__: <built-in method __reduce_ex__ of User object>
# __repr__: <slot wrapper '__repr__' of 'object' object>
# __setattr__: <slot wrapper '__setattr__' of 'object' object>
# __sizeof__: <built-in method __sizeof__ of User object>
# __str__: <slot wrapper '__str__' of 'object' object>
# __subclasshook__: <built-in method __subclasshook__ of type object>
# __weakref__: <member '__weakref__' of 'User' object>
# email: xiaoming@example.com
# greet: <bound method User.greet of <__main__.User object>>
# name: 小明
过滤 dunder 方法
user = User("小明", "xiaoming@example.com")
# 只看公有属性和方法(排除双下划线开头的)
public_attrs = [attr for attr in dir(user) if not attr.startswith('_')]
print(public_attrs)
# ['email', 'greet', 'name']
getattr/setattr/hasattr —— 动态操作属性
hasattr —— 检查属性是否存在
user = User("小明", "xiaoming@example.com")
print(hasattr(user, 'name')) # True
print(hasattr(user, 'age')) # False
print(hasattr(user, 'greet')) # True
print(hasattr(user, '__init__')) # True(内置方法也算)
getattr —— 安全获取属性值
user = User("小明", "xiaoming@example.com")
# 方式一:提供默认值(当属性不存在时返回)
age = getattr(user, 'age', 0) # 返回 0(因为 age 不存在)
# 方式二:不提供默认值(不存在则抛 AttributeError)
try:
age = getattr(user, 'age')
except AttributeError:
print("属性 age 不存在")
setattr —— 动态设置属性
user = User("小明", "xiaoming@example.com")
# 动态添加新属性
setattr(user, 'age', 25)
setattr(user, 'city', '北京')
print(user.age) # 25
print(user.city) # 北京
# 修改已有属性
setattr(user, 'name', '小红')
print(user.name) # 小红
实战:通用配置读取器
class ConfigReader:
"""从任意对象读取配置"""
@staticmethod
def read(config_obj, *keys, default=None):
"""
从 config_obj 中按 keys 路径读取嵌套值
Args:
config_obj: 配置对象(可以是 dict 或任意对象)
*keys: 键序列(支持嵌套路径)
default: 找不到时的默认值
Returns:
找到的值或默认值
Examples:
>>> class Config:
... database = type('', (), {'host': 'localhost', 'port': 5432})()
>>> ConfigReader.read(Config, 'database', 'host')
'localhost'
"""
current = config_obj
for key in keys:
if isinstance(current, dict):
current = current.get(key, default)
else:
current = getattr(current, key, default)
if current is default:
return default
return current
# 使用示例
class DatabaseConfig:
host = "localhost"
port = 5432
username = "admin"
password = "secret"
config = DatabaseConfig()
# 简单读取
host = ConfigReader.read(config, 'host')
print(host) # localhost
# 嵌套读取
port = ConfigReader.read(config, 'port')
print(port) # 5432
# 不存在时使用默认值
timeout = ConfigReader.read(config, 'timeout', default=30)
print(timeout) # 30
# 也可以用于 dict
settings = {"app": {"debug": True, "version": "1.0"}}
debug = ConfigReader.read(settings, 'app', 'debug')
print(debug) # True
高级技巧:反射式编程
动态方法调用
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
def multiply(self, a, b):
return a * b
def divide(self, a, b):
if b == 0:
raise ValueError("除数不能为零")
return a / b
calc = Calculator()
# 动态调用方法
operations = ["add", "subtract", "multiply", "divide"]
for op in operations:
method = getattr(calc, op)
result = method(10, 5)
print(f"{op}(10, 5) = {result}")
# add(10, 5) = 15
# subtract(10, 5) = 5
# multiply(10, 5) = 50
# divide(10, 5) = 2.0
工厂模式与动态实例化
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
# 形状注册表
shape_registry = {
"circle": Circle,
"square": Square,
"triangle": Triangle,
}
# 工厂函数
def create_shape(shape_type, **kwargs):
cls = shape_registry.get(shape_type)
if cls is None:
raise ValueError(f"未知形状:{shape_type}")
return cls(**kwargs)
shapes = [
create_shape("circle", radius=5),
create_shape("square", side=4),
create_shape("triangle", base=6, height=8),
]
for shape in shapes:
print(f"面积:{shape.area():.2f}")
最佳实践总结
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 判断对象类型 | isinstance() |
支持继承和多态 |
| 快速查看属性 | dir() |
列出所有公开成员 |
| 安全访问属性 | getattr() + 默认值 |
避免 AttributeError |
| 检查属性存在 | hasattr() |
简洁的条件判断 |
| 动态赋值 | setattr() |
运行时修改状态 |
| 动态调用方法 | getattr() + 调用 |
灵活的方法调度 |
总结
本文讲解了对象信息获取的核心知识:
- type() — 查询对象的精确类型,也可用于动态创建类
- isinstance() — 安全的类型检查,支持继承链和多态
- dir() — 列出对象的所有属性和方法
- getattr/setattr/hasattr — 动态读写对象属性
- 高级应用 — 反射式编程、工厂模式、通用配置读取
- 最佳实践 — 优先用 isinstance、善用 getattr 默认值
下一篇文章将深入讲解实例属性与类属性。
本文是 Python 教程系列的第二十七篇。
