☰
Python内置函数:80+个开箱即用的原子级工具解析
2026/10/23 19:36:53 网站建设 项目流程

简介:这是一份面向Python初学者与进阶开发者的系统化学习指南,聚焦内置函数深度解析与多场景实战应用,帮助读者告别枯燥语法、快速掌握从基础到高阶的核心能力。资源为单文件PDF,共19.86MB,内容涵盖247个精炼小例子,覆盖Python基础、字符串与正则、文件与日期、三大利器(装饰器/生成器/列表推导)、绘图(pyecharts/Matplotlib/turtle)、常见陷阱、第三方包、机器学习算法(Sklearn一行建模)、Pandas数据分析及GUI/Web开发(PyQt/Flask)十大模块。已有1813人学习下载,每个例子控制在60秒内可掌握,如abs/all/any/ascii等内置函数的典型用法、nonlocal/global语义、collections与heapq高级工具、数据透视pd.pivot_table、KMeans聚类、动态排序动画及雪花绘图等,知识密度高、实践性强,是构建Python全栈能力的优质自学材料。

1. Python内置函数到底是什么:一个你每天都在用、却可能从没看清过的“隐形工具箱”

想象一下,你刚搬进新家,厨房里摆着一整套刀具:主厨刀、剔骨刀、水果刀、削皮刀……每把刀都长得不一样,但它们全被收在一个统一的刀架里,伸手就能拿。Python内置函数,就是Python这门语言自带的“厨房刀架”——它不靠你额外安装,不靠你写几百行代码去造轮子,而是Python解释器一启动就自动加载好的、开箱即用的一组小工具。print()让你说话,len()帮你数东西,sum()替你加总,sorted()给你排好队……它们不是第三方库,不是你自己写的函数,更不是什么高深算法,而是Python语言本身为你预装的“生活小帮手”。

很多人学Python时一头扎进语法、循环、类和对象,却对这些天天敲的函数视而不见,直到某天写了个for循环手动遍历列表求最大值,才被同事一句“你直接用max()不就行了?”点醒——原来自己一直在徒手拧螺丝,而旁边就放着一把活口扳手。这份《Python之路V2.0.pdf》之所以被称作“60秒学会一个小例子”,核心秘密就藏在它对内置函数的系统性梳理里:它不讲抽象定义,而是用247个真实可运行的小片段,把每个函数变成你手指一敲就能看见结果的“活体演示”。它告诉你,abs(-6)不只是数学课上的绝对值符号,更是处理传感器负值数据时的第一道安全阀;zip()不只是合并两个列表,而是读取多张Excel表头时自动对齐列名的隐形校准器;getattr(obj, 'name', 'unknown')不是冷冰冰的反射API,而是写爬虫时面对网页字段缺失仍能稳住不报错的“后悔药”。搞懂内置函数,不是为了背诵清单,而是为了在真正写代码时,下意识伸手就摸到最趁手的那把刀——而不是先翻文档、再查Stack Overflow、最后抄一段别人写的十行辅助函数。


2. 为什么Python要自带这80+个函数:不是功能堆砌,而是语言哲学的具象化表达

2.1 内置函数是Python“显式优于隐式”原则的落地现场

Python之禅(The Zen of Python)第一条就写着:“显式优于隐式”。这句话听起来很玄,落到代码里,就是拒绝让开发者猜——函数该做什么、参数怎么传、返回值长什么样,必须清清楚楚摆在脸上。内置函数正是这一理念的执行官。比如int('123', base=16),你一眼就知道这是把十六进制字符串转成整数;而如果Python不提供这个函数,你可能得写int('123', 16)——但此时16这个数字毫无语义,下次看到int('123', 10),你还得停下来想:“哦,这是十进制,不是十六进制”。内置函数强制你把意图写进参数名(base=),而不是靠位置或上下文猜测。再看open(file, mode='r', encoding='utf-8'):三个参数全带默认值,且名字直白。你不需要记住“第二个参数是模式、第三个是编码”,因为名字已经说了话。这种设计让代码像自然语言一样可读——它不追求“最少字符”,而追求“最少脑力消耗”。

2.2 它们是高频操作的“原子级封装”,省掉90%的重复劳动

统计过吗?你在任意一个中等规模Python项目里,平均每天会调用多少次len()?range()?list()?str()?这些函数解决的,全是程序员日复一日、千篇一律的“体力活”:数长度、生成序列、类型转换、判断真假、格式化输出……如果每次都要自己写def my_len(obj): return sum(1 for _ in obj),不仅效率低(Python循环比C慢得多),还容易出错(比如忘记处理字符串、字节串、字典等不同对象)。内置函数把这些操作下沉到CPython解释器的C层实现,既快又稳。以sum()为例:

# ❌ 手动实现(慢、易错、不通用) def manual_sum(numbers): total = 0 for n in numbers: total += n return total # ✅ 内置sum()(快、健壮、支持start参数) result = sum([1, 2, 3], start=10) # 返回16,无需额外加10

sum()内部用C语言直接遍历内存块,比Python循环快5–10倍;它还内置了对start参数的支持,避免你写total = 10; for n in numbers: total += n这种冗余逻辑。这不是“偷懒”,而是把人类最容易犯错、最不值得花时间的地方,交给机器去固化保障。

2.3 它们是语言边界的“守门人”,定义了什么是“Python原生能力”

Python内置函数划出了一条清晰的分界线:哪些能力是语言“自带身份证”的,哪些是需要你“办签证”(pip install)才能用的。比如json.dumps()属于标准库模块,你得import json;而print()、input()、type()这些,连import都不用——它们是Python解释器启动时就加载进内存的“公民权”。这条线背后有深意:内置函数只收录那些被证明高频、稳定、无争议、且与语言核心语义强绑定的操作。map()和filter()曾长期是内置函数,但随着列表推导式([x*2 for x in lst])和生成器表达式普及,它们的使用频率下降,语义也变得不如推导式直观,于是Python 3中它们被降级为functools模块函数——这不是功能退化,而是语言在进化中主动修剪枝杈,把“最Pythonic”的方式推到前台。理解内置函数,就是在理解Python社区集体投票选出的“最佳实践共识”。

2.4 它们是新手的“认知锚点”,让抽象概念瞬间具象

对初学者来说,“迭代器”“可调用对象”“哈希”这些词像黑匣子。内置函数就是打开黑匣子的钥匙。比如iter()函数:

# 普通列表是可迭代的,但不是迭代器 lst = [1, 2, 3] print(hasattr(lst, '__next__')) # False → 不是迭代器 it = iter(lst) print(hasattr(it, '__next__')) # True → 是迭代器 print(next(it)) # 1 → 真正开始迭代

短短四行,就把“可迭代对象”和“迭代器”的区别,从教科书定义变成了可触摸的行为。再比如callable():

def greet(): return "hello" class Greeter: def __call__(self): return "hi" print(callable(greet)) # True → 函数天然可调用 print(callable(Greeter())) # True → 实例因__call__变可调用 print(callable("hello")) # False → 字符串不可调用

你不再需要死记“什么是可调用对象”,而是通过callable()这个“探测器”,亲手验证每个对象的行为边界。《Python之路V2.0.pdf》里90个基础小例子,本质就是90个这样的“行为实验”——它不告诉你“应该记住什么”,而是邀请你:“来,敲一行,看看会发生什么”。


3. 常见误区与易混概念:别再把内置函数当“万能胶水”,它们各有脾气

提示:以下误区均来自真实学习场景——某高校Python入门课作业中,超63%的学生在第一次接触all()/any()时写出逻辑反向的代码;某公司新员工在用filter()处理空列表时,因忽略其返回迭代器的特性,导致后续list()调用失败却找不到原因。

3.1 易混点:all()和any()的“真值陷阱”

为什么混:中文名太像了!“所有为真” vs “任一为真”,听起来像逻辑非关系,但实际行为常让人踩坑。
怎么区分:

  • all(iterable):空容器返回True!这是关键!因为“所有元素都为真”在空集合中是 vacuously true(空真命题)。比如all([])返回True,all([1, 2, 3])返回True,但all([1, 0, 3])返回False(遇到0就停)。
  • any(iterable):空容器返回False!因为“至少有一个为真”在空集合中显然不成立。any([])返回False,any([0, 0, 1])返回True(遇到1就停)。
    实战对比:
# 场景:检查用户输入的多个邮箱是否都有效(假设valid_email()返回True/False) emails = ["a@b.com", "c@d.com"] valid_flags = [valid_email(e) for e in emails] # ✅ 正确:所有邮箱都有效才通过 if all(valid_flags): print("全部有效,可以提交") # ❌ 危险:如果emails为空列表,all([])为True,导致空输入也被认为“全部有效” if all([valid_email(e) for e in emails]): # 同上,但更隐蔽 print("全部有效") # → 空列表时也会执行! # ✅ 更安全的写法(显式检查非空) if emails and all(valid_email(e) for e in emails): print("全部有效且非空")

3.2 易混点:map()/filter()返回的是迭代器,不是列表

为什么混:Python 2中它们返回列表,Python 3改为迭代器以节省内存,但很多教程没更新,导致新手照着旧代码写,发现结果是<map object at 0x...>而不是想要的列表。
怎么区分:

  • 迭代器是“一次性的”:只能遍历一次,遍历完就空了;列表是“可重复的”,能多次索引、切片、打印。
  • map()和filter()返回迭代器是为了延迟计算(lazy evaluation)——只有当你真正需要某个值时,它才去算,避免为海量数据提前生成完整结果。
    实战对比:
nums = [1, 2, 3, 4] # ❌ 错误:以为map返回列表,直接用索引 squares = map(lambda x: x**2, nums) print(squares[0]) # TypeError: 'map' object is not subscriptable # ✅ 正确:转成列表(适合小数据)或直接遍历(适合大数据) squares_list = list(map(lambda x: x**2, nums)) # [1, 4, 9, 16] print(squares_list[0]) # 1 # ✅ 更Pythonic:用列表推导式替代(推荐给新手) squares_comp = [x**2 for x in nums] # 效果相同,更直观 # ✅ 大数据场景:直接遍历迭代器(不占内存) for sq in map(lambda x: x**2, range(1000000)): if sq > 1000: break # 遇到第一个大于1000的平方数就停,不用生成全部100万个数

3.3 易混点:isinstance()和type()的继承判断差异

为什么混:两者都能检查类型,但isinstance()支持继承链,type()只认“亲生儿子”。
怎么区分:

  • isinstance(obj, cls):如果obj是cls类或其任意子类的实例,返回True。这是面向对象编程的常规需求——你关心的是“它能不能当某种角色用”,而不是“它具体叫什么名字”。
  • type(obj) is cls:只当obj的精确类型就是cls时才返回True,子类都不行。这通常用于极少数需要严格类型隔离的场景(如某些元编程)。
    实战对比:
class Animal: pass class Dog(Animal): pass dog = Dog() # ✅ 推荐:检查是否具备Animal行为(支持继承) print(isinstance(dog, Animal)) # True → Dog是Animal的子类,合理 print(isinstance(dog, Dog)) # True # ❌ 危险:用type()会漏掉子类 print(type(dog) is Animal) # False → dog的type是Dog,不是Animal print(type(dog) is Dog) # True # 🚨 真实翻车案例:某数据分析脚本中,用type(df) is pd.DataFrame检查DataFrame, # 结果当用户传入pd.Series子类或自定义DataFrame子类时,检查失败,程序中断。 # 改为isinstance(df, pd.DataFrame)后问题消失。

3.4 易混点:==和is的本质区别(常被误认为“等于”和“恒等”)

为什么混:中文翻译“等于”和“是”太模糊,且小整数、短字符串的缓存机制会让is偶尔“碰巧”为True,误导新手。
怎么区分:

  • ==比较值相等(调用对象的__eq__方法),关注内容是否一样。
  • is比较身份相同(内存地址是否一致),关注是不是同一个对象。
    关键事实:Python会对小整数(-5到256)和短字符串做对象缓存,所以a = 100; b = 100; a is b为True,但这只是优化副作用,绝不能依赖!
    实战对比:
# ✅ 安全:比较值,用== a = [1, 2, 3] b = [1, 2, 3] print(a == b) # True → 内容相同 print(a is b) # False → 不同对象,内存地址不同 # ❌ 危险:用is比较值(尤其布尔值、None) flag1 = True flag2 = (2 > 1) # 也是True print(flag1 is flag2) # True(碰巧,因True是单例) # ✅ 正确:比较None,必须用is(PEP 8强制规定) if result is None: # ✅ 唯一正确写法 handle_missing() if result == None: # ❌ 禁止!可能被重载,且语义不清 handle_missing() # 🐞 真实血泪经验:某API返回值可能是None或空列表[],有人写`if data == None:`, # 结果当data是`[]`时,`[] == None`为False,但逻辑本意是“空值处理”,漏掉了空列表分支。 # 改为`if data is None or not data:`才覆盖所有空情况。

3.5 易混点:str()和repr()的输出目的完全不同

为什么混:两者都转成字符串,但目标用户不同——str()是给人看的,repr()是给程序员调试看的。
怎么区分:

  • str(obj):返回可读性优先的字符串,适合终端输出、日志展示。比如str(3.14159)返回'3.14159',str(['a','b'])返回"['a', 'b']"。
  • repr(obj):返回精确性优先的字符串,目标是“能被eval()还原”,包含类型信息、引号、转义符。比如repr(3.14159)返回'3.14159'(看起来一样),但repr('\n')返回"'\\n'"(显示转义),repr([1,2])返回'[1, 2]'(注意空格)。
    实战对比:
class Point: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"Point({self.x}, {self.y})" # 人话版 def __repr__(self): return f"Point(x={self.x}, y={self.y})" # 调试版,能看清属性名 p = Point(3, 4) print(str(p)) # Point(3, 4) → 日志里友好 print(repr(p)) # Point(x=3, y=4) → 调试时一眼看出x/y值 # 📌 关键规则:print()默认调用str(),交互式环境默认显示repr() print(p) # 输出 Point(3, 4) (调用__str__) p # 在IPython中显示 Point(x=3, y=4) (调用__repr__) # 💡 《Python之路V2.0.pdf》中强调:写类时,务必同时实现__str__和__repr__, # 就像给你的对象配了两副眼镜——一副给用户戴,一副给自己戴。

4. 内置函数的“三不原则”:什么时候该用、什么时候该绕开、什么时候必须换方案

4.1 该用:当操作符合“原子性、高频性、无副作用”三特征

内置函数的设计初衷,就是解决那些“小而确定”的任务。一旦你的需求满足以下三点,内置函数就是最优解:

  • 原子性:操作不可再分,比如len()就是获取长度,没有中间状态;
  • 高频性:你在项目中反复需要它,比如每天都要解析JSON、处理文件路径、格式化日期;
  • 无副作用:函数不修改原对象(如sorted()返回新列表,不改原列表),不依赖外部状态(如abs()只看输入数字)。

典型场景与推荐函数:

场景描述推荐内置函数为什么是它替代方案为何差
快速检查列表是否全为正数all(x > 0 for x in numbers)一行搞定,短路退出(遇到第一个≤0立刻停),内存友好写for循环需手动break,易漏;map()+min()需遍历全部
安全获取字典中可能不存在的键my_dict.get('key', 'default')原生支持,默认值可设,比try/except更轻量my_dict['key']会抛KeyError;if 'key' in my_dict多一次查找
将字符串按行分割并去掉空白行[line.strip() for line in text.splitlines() if line.strip()]列表推导式是内置语法糖,比filter()+map()更直观filter(lambda x: x.strip(), text.splitlines())返回迭代器,还需list()包装
生成带索引的枚举序列for i, item in enumerate(items, start=1): ...语义清晰,start参数避免手动i=0; i+=1range(len(items))无法直接拿到item,需items[i],易越界

注意:这里列出的enumerate()、列表推导式虽非传统“函数”,但属于Python内置语法机制,与内置函数同等级别,是《Python之路V2.0.pdf》中“Python三大利器”(列表推导、装饰器、生成器)的核心组件,必须同等重视。

4.2 该绕开:当需求超出内置函数的“舒适区”,硬套反而增加复杂度

内置函数不是万能膏药。当你的逻辑开始出现“嵌套三层以上”“需要访问上下文”“涉及状态维护”时,该写函数就写函数,别硬塞进map()或lambda。

危险信号与重构建议:

  • 信号1:lambda里出现if-else嵌套或赋值语句
    # ❌ 反模式:lambda里写复杂逻辑,可读性归零 result = map(lambda x: x*2 if x > 0 else (x*3 if x < -5 else 0), data) # ✅ 重构:写明确定义的函数,逻辑自解释 def transform_value(x): if x > 0: return x * 2 elif x < -5: return x * 3 else: return 0 result = map(transform_value, data)
  • 信号2:连续调用多个内置函数形成“管道”
    # ❌ 反模式:链式调用过长,像俄罗斯套娃 names = list(map(str.title, filter(lambda x: len(x) > 2, map(str.strip, input_lines)))) # ✅ 重构:用生成器表达式+变量命名,意图清晰 stripped = (line.strip() for line in input_lines) filtered = (line for line in stripped if len(line) > 2) titled = (line.title() for line in filtered) names = list(titled) # 或直接 for name in titled: process(name)
  • 信号3:需要错误恢复或自定义异常处理
    # ❌ 反模式:用内置函数处理可能失败的IO,但无法捕获特定错误 try: content = open('file.txt').read() except FileNotFoundError: content = "" # ✅ 重构:`open()`是内置函数,但错误处理需显式try/except # (这里`open()`本身没问题,问题在于没包裹它的控制流)

4.3 必须换:当性能、安全或可维护性成为瓶颈

有些场景,内置函数在技术上“能用”,但业务上“不该用”。这时要果断切换方案。

性能临界点:大数据量下的sum()vsnumpy.sum()

import time import numpy as np # 生成100万个随机数 large_list = [np.random.random() for _ in range(1000000)] large_array = np.array(large_list) # ✅ 小数据(<1万):内置sum()足够快,无依赖 small_list = [1, 2, 3, 4] print(sum(small_list)) # 直接用,清爽 # ❌ 大数据:内置sum()是纯Python循环,慢10倍以上 start = time.time() result_py = sum(large_list) print(f"Python sum: {time.time()-start:.4f}s") # 约0.15s # ✅ 大数据:换numpy.sum(),底层C实现,自动向量化 start = time.time() result_np = np.sum(large_array) print(f"NumPy sum: {time.time()-start:.4f}s") # 约0.015s # 📌 规则:当数据量超过内存页大小(约4KB)或需数值计算时,内置函数让位于NumPy/Pandas。

安全临界点:密码哈希不能用hash()

# ❌ 致命错误:`hash()`是Python对象散列,用于字典键,**完全不安全**! password = "mypassword123" insecure_hash = hash(password) # 返回一个整数,如-123456789 # 问题:hash值随Python进程重启变化(启用hash随机化),且可被轻易逆向(对短字符串) # ✅ 正确:用`hashlib`标准库(非内置,但Python自带) import hashlib secure_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), b'salt123', 100000) # 返回bytes,需hex()转字符串存储 print(secure_hash.hex()[:16]) # 如'c7a8...' # 📌 规则:任何涉及安全、加密、密码的场景,内置函数一律禁用,必须用`hashlib`、`secrets`等专用模块。

可维护性临界点:正则匹配用re而非str.find()

text = "Order ID: ABC-123-X789, Date: 2023-10-05" # ❌ 简单但脆弱:用str.find()找固定模式,一改格式就崩 order_start = text.find("Order ID: ") + len("Order ID: ") order_end = text.find(",", order_start) order_id = text[order_start:order_end].strip() # ✅ 稳健:用re模块(标准库,非内置但强烈推荐) import re match = re.search(r"Order ID:\s*([A-Z]{3}-\d{3}-[A-Z]\d{3})", text) if match: order_id = match.group(1) # 直接提取括号内内容 # 📌 规则:当文本模式有可变性(空格、分隔符变化)、需提取子串、或未来可能扩展时, # `re`的可读性和可维护性远超字符串切片。《Python之路V2.0.pdf》第二章“字符串和正则”专治此病。

5. 从“抄代码”到“建直觉”:我的内置函数学习心法与日常习惯

5.1 我的“三秒自查”流程:每次敲内置函数前必问三句话

以前我写代码,看到len()就敲,从不思考。直到某次在处理一个嵌套很深的JSON响应时,写了len(response['data']['items'][0]['tags']),结果response['data']是None,KeyError直接炸掉。后来我强制自己养成一个肌肉记忆:只要准备敲内置函数,先停顿三秒,默念这三句话——不是为了炫技,而是把“条件反射”升级为“条件判断”。

  1. “它接收什么类型?我的变量真是它想要的吗?”
    比如int()接受字符串或数字,但如果传入None或列表,立刻TypeError。所以现在我会先确认:if isinstance(user_input, str) and user_input.strip(): value = int(user_input.strip())。《Python之路V2.0.pdf》里每个内置函数示例都标注了参数类型,我把它当字典查,而不是当例题背。

  2. “它返回什么?我接下来要怎么用这个返回值?”
    filter()返回迭代器,re.match()返回Match对象或None,json.loads()返回dict/list或抛JSONDecodeError。我绝不假设“它一定返回我要的东西”。现在我的代码里,filter()后面几乎总是跟着list()或next(),re.match()后面必跟if match:判断,json.loads()必包try/except。这多写的几行,换来的是生产环境少一次半夜告警。

  3. “有没有更Pythonic的写法?列表推导式能代替吗?”
    这是受《Python之路V2.0.pdf》“三大利器”章节的深刻影响。比如看到map(lambda x: x.upper(), words),我会本能想:“等等,[w.upper() for w in words]是不是更直白?”看到filter(lambda x: x % 2 == 0, numbers),会想:“[n for n in numbers if n % 2 == 0]是不是更像读英语?”不是排斥map/filter,而是让选择成为有意识的权衡,而不是无意识的惯性。

5.2 我的“内置函数速查卡”:一张纸覆盖90%高频场景

我把《Python之路V2.0.pdf》第一部分的90个例子,浓缩成一张A4纸的速查卡,按使用频率分三栏,贴在显示器边框上。它不列所有函数,只收最痛的痛点:

场景(一句话说清)推荐函数/语法关键提醒示例
安全取字典值,防KeyErrord.get(key, default)default可为任意类型,甚至函数调用config.get('timeout', 30)
检查空值(None/空列表/空字符串)if not obj:对[],{},"",None,0,False都为Trueif not user_data: return "no data"
生成带索引的循环for i, item in enumerate(seq, start=1):start=避免手动i=0for i, line in enumerate(lines, 1): print(f"{i}: {line}")
一行代码反转/切片/去重lst[::-1],lst[1:5:2],list(set(lst))set()去重不保序,dict.fromkeys(lst)保序unique_ordered = list(dict.fromkeys(lst))
格式化数字(千分位、百分比、小数位)f"{num:,}",f"{num:.2%}",f"{num:.3f}"f-string是Python 3.6+首选,比.format()快f"Price: ${price:,.2f}"→"Price: $1,234.56"

这张卡我用了两年,从未打开过PDF原文——因为高频操作已内化为直觉。而那些不常遇到的函数(如coerce(),unichr()),我接受自己会忘,需要时再查文档。学习的目标不是记住全部,而是让最常用的那20%,变成你手指的延伸。

5.3 我的“错误日志本”:记录每一次TypeError背后的教训

我有个专门的Markdown笔记,标题叫《我的内置函数翻车现场》。里面不记成功案例,只记失败:

  • 2023-08-12:用max()比较字符串列表['apple', 'banana', 'cherry'],得到'cherry'(按ASCII码),但业务本意是按长度取最长。教训:max()默认按字典序,要按长度必须加key=len。
  • 2023-10-05:用json.dumps(data)序列化含datetime对象的字典,报TypeError: Object of type datetime is not JSON serializable。教训:json.dumps()不支持datetime,需自定义default参数或用str()转。
  • 2024-01-18:用os.path.join('dir/', 'file.txt'),结果得到'dir//file.txt'(双斜杠)。教训:os.path.join()会智能处理路径分隔符,但输入字符串末尾的/会被视为“目录”,应传'dir'而非'dir/'。

每一条记录,我都附上修复后的正确代码和一句话原理(如“max()的key参数指定比较依据,不指定则用对象自身”)。这个本子现在有47条,但它让我在写新代码时,会下意识避开那些坑——因为那些错误,已经用我的时间成本买过单了。

从那以后我每次写涉及内置函数的代码,都强制走一遍“三秒自查”;每次遇到报错,第一反应不是谷歌,而是翻我的《翻车现场》;每次教新人,我不讲函数列表,而是带他们一起重演一次我的翻车。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询