返回博客

27. 获取对象信息:type、isinstance、dir

27. 获取对象信息:type、isinstance、dir

第二十七篇:获取对象信息——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() + 调用 灵活的方法调度

总结

本文讲解了对象信息获取的核心知识:

  1. type() — 查询对象的精确类型,也可用于动态创建类
  2. isinstance() — 安全的类型检查,支持继承链和多态
  3. dir() — 列出对象的所有属性和方法
  4. getattr/setattr/hasattr — 动态读写对象属性
  5. 高级应用 — 反射式编程、工厂模式、通用配置读取
  6. 最佳实践 — 优先用 isinstance、善用 getattr 默认值

下一篇文章将深入讲解实例属性与类属性。


本文是 Python 教程系列的第二十七篇。