<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>类型自省 on 张欣耕的个人博客</title><description>Recent content in 类型自省 on 张欣耕的个人博客</description><link>https://shanechang.com/zh-cn/tags/%E7%B1%BB%E5%9E%8B%E8%87%AA%E7%9C%81/</link><language>zh-cn</language><lastBuildDate>Tue, 16 Sep 2025 00:00:00 GMT</lastBuildDate><atom:link href="https://shanechang.com/zh-cn/tags/%E7%B1%BB%E5%9E%8B%E8%87%AA%E7%9C%81/index.xml" rel="self" type="application/rss+xml"/><item><title>Python运行时的身份危机: 一份靠谱的类型自查指南</title><link>https://shanechang.com/zh-cn/p/python-runtime-type-introspection-guide/</link><guid isPermaLink="true">https://shanechang.com/zh-cn/p/python-runtime-type-introspection-guide/</guid><description>&lt;img src=&quot;https://shanechang.com/_astro/cover.ojhu4dpo_ZfmLHM.webp&quot; alt=&quot;Featured image of post Python运行时的身份危机: 一份靠谱的类型自查指南&quot; /&gt;&lt;h2 id=&quot;谎言从类型安全开始&quot;&gt;谎言从”类型安全”开始&lt;/h2&gt;
&lt;p&gt;曾经我自信满满地认为, 自己的代码是”钢铁防线”。&lt;code&gt;mypy&lt;/code&gt;全绿, IDE小绿勾, Pydantic模型类型注解清清楚楚。我一边写一边默念: Message[int], 妥妥的, 肯定就是装int的嘛!类型都写在那儿了, 还能错?&lt;/p&gt;
&lt;p&gt;直到有一天, 我在运行时问模型: 你到底装的是什么类型?Python回了我一个迷之微笑——它表示”我也不知道”。&lt;/p&gt;
&lt;p&gt;这才明白, 原来有两个世界: 静态类型检查时的秩序井然, 和运行时的混沌江湖。类型检查器手里的地图, 代码跑起来早就扔一边了。想知道”你到底是谁”, 得靠我们自己当侦探, 手绘新地图!&lt;/p&gt;
&lt;h2 id=&quot;两个世界-蓝图vs现实&quot;&gt;两个世界: 蓝图vs.现实&lt;/h2&gt;
&lt;p&gt;类型提示, 其实就像盖房子的蓝图。&lt;code&gt;list[int]&lt;/code&gt;这张图纸写得明明白白: “这里要装整数”。&lt;code&gt;mypy&lt;/code&gt;和IDE像验房师, 发现你往里塞个str立马报警。&lt;/p&gt;
&lt;p&gt;可代码真正跑起来的时候, Python人家可不管你图纸咋画的。它就站在房子里, 看到的只是个list。至于[int]?早丢到角落吃灰去了。&lt;/p&gt;
&lt;p&gt;所以, 我们这些开发者, 得在”房子”里找线索, 倒推出原来设计要装什么。这, 就是运行时侦探的由来。&lt;/p&gt;
&lt;h2 id=&quot;第一招-直接问本人type&quot;&gt;第一招: 直接问本人——type()&lt;/h2&gt;
&lt;p&gt;最直接的办法, 当然是问”你是谁”。Python的&lt;code&gt;type()&lt;/code&gt;函数就是直男型选手, 谁用谁知道。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# 简单例子: 5到底是啥?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;))  &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# 输出: &amp;#x3C;class &apos;int&apos;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# Pydantic场景下&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; pydantic &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; BaseModel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt; User&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;BaseModel&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    name: &lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    age: &lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;user &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; User(&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt;age&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;30&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(user.age))  &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# 输出: &amp;#x3C;class &apos;int&apos;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有了值, &lt;code&gt;type(值)&lt;/code&gt;永远不会忽悠你 —— 这就是铁证如山的事实。&lt;/p&gt;
&lt;p&gt;可问题来了: 有时候你还没值, 比如你要写个函数, 参数可能是个空list, 你想知道”应该”装啥类型。这时光靠type()就不灵了, 得深挖。&lt;/p&gt;
&lt;h2 id=&quot;第二招-翻翻typing小抄get_origin和get_args&quot;&gt;第二招: 翻翻typing小抄——&lt;code&gt;get_origin&lt;/code&gt;和&lt;code&gt;get_args&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;好消息是, Python有时候没把蓝图全扔。有些&lt;code&gt;typing&lt;/code&gt;模块的类型会留点”小抄”, 可以用&lt;code&gt;get_origin&lt;/code&gt;和&lt;code&gt;get_args&lt;/code&gt;来偷看。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_origin(some_type)&lt;/code&gt;: 问”你这容器本体是啥?”比如&lt;code&gt;list&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_args(some_type)&lt;/code&gt;: 问”你里面藏的都是什么类型?”比如&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;来一波操作:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; get_origin, get_args, Optional, Dict&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;int_list_type &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Origin: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;get_origin(int_list_type)&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# --&gt; &amp;#x3C;class &apos;list&apos;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Args: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;get_args(int_list_type)&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)      &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# --&gt; (&amp;#x3C;class &apos;int&apos;&gt;,)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;user_data_type &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; Dict[&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;, Optional[&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Origin: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;get_origin(user_data_type)&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# --&gt; &amp;#x3C;class &apos;dict&apos;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Args: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;get_args(user_data_type)&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)     &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# --&gt; (&amp;#x3C;class &apos;str&apos;&gt;, typing.Optional[int])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;是不是感觉掌握了黑科技?但很快你会遇到”陷阱”。&lt;/p&gt;
&lt;p&gt;**陷阱来了:**换成普通类试试:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt; Message&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(get_origin(Message)) &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# --&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(get_args(Message))   &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# --&gt; ()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;啥也没有!原来这套工具只认typing家族的”特殊类”, 普通类一脸懵。而Pydantic的泛型, 比如&lt;code&gt;Message[int]&lt;/code&gt;, 实际跟普通类更亲——这就让人头大了。&lt;/p&gt;
&lt;h2 id=&quot;终极boss-pydantic-v2的易容术&quot;&gt;终极Boss: Pydantic v2的”易容术”&lt;/h2&gt;
&lt;p&gt;你写&lt;code&gt;MyGenericModel[int]&lt;/code&gt;时, Pydantic不是简单存个int, 而是”现场”生成了一个新类, 这个类专门为int量身定制。&lt;/p&gt;
&lt;p&gt;很酷, 但这下&lt;code&gt;get_origin&lt;/code&gt;和&lt;code&gt;get_args&lt;/code&gt;彻底歇菜。你拿到的是个真·类, 不是typing注解。我曾经折腾了半天, 差点怀疑人生: 为什么就是扒不出&lt;code&gt;Message[int]&lt;/code&gt;里的int?&lt;/p&gt;
&lt;p&gt;其实Pydantic悄悄给我们留了线索, 关键就看你会不会找:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;神秘的私房小字条:&lt;/strong&gt; 类属性&lt;code&gt;__pydantic_generic_metadata__&lt;/code&gt;, 这里面明明白白写着这个泛型到底”特化”成什么了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;公开的字段注解:&lt;/strong&gt; Pydantic会把字段的&lt;code&gt;annotation&lt;/code&gt;同步成特化类型, 比如&lt;code&gt;Message[int]&lt;/code&gt;里的&lt;code&gt;content&lt;/code&gt;字段, 注解就直接变成了int。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;大侦探三件套-万用型类型自查方案&quot;&gt;大侦探三件套: 万用型类型自查方案&lt;/h2&gt;
&lt;p&gt;怎么把这些线索串成一条龙服务?我们写个小方法, 优雅地一层层查找 (从最精确到最笼统) 。&lt;/p&gt;
&lt;p&gt;首先, 来个类型美化小助手, 让类型名看起来顺眼点:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; Any, get_origin, get_args&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt; pretty_type_name&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(tp: Any) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;    &quot;&quot;&quot;把类型名变得人见人爱&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt; hasattr&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(tp, &lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;__name__&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; tp.&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;__name__&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    origin &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; get_origin(tp)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; origin:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;        inner &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt; &quot;, &quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;.join(pretty_type_name(a) &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; a &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; get_args(tp))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;        base &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt; getattr&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(origin, &lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;__name__&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(origin))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;base&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;inner&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt; str&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(tp)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后, 在我们的泛型Pydantic模型里, 加上大侦探方法。用&lt;code&gt;@computed_field&lt;/code&gt;, 让外部一看就明白:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; Generic, TypeVar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; pydantic &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; BaseModel, computed_field&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;T &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; TypeVar(&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;T&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt; Message&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;BaseModel&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;, Generic[T]):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    content: T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt;    @computed_field&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;property&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt; param_type&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(self) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;        &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;        设计时类型: 泛型到底被特化成了啥?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;        &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;        # 1. 优先查Pydantic的私房字条&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;        meta &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt; getattr&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;__class__&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;__pydantic_generic_metadata__&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;None&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; meta &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; meta.get(&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; pretty_type_name(meta[&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;        # 2. 没有的话, 看字段注解&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;        field_annotation &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;__class__&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;.model_fields[&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;].annotation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; field_annotation &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; T:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; pretty_type_name(field_annotation)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;        # 3. 实在查不到, 只能认栽, 看实际存的值&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;.runtime_type&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt;    @computed_field&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;property&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt; runtime_type&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(self) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;        &quot;&quot;&quot;运行时类型: 现在content里实际是什么?&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; pretty_type_name(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;.content))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实战一下:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# 先来个int专用版&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;IntMessage &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; Message[&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;msg1 &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; IntMessage(&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;123&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Param Type: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;msg1.param_type&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)       &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# -&gt; &quot;int&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Runtime Type: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;msg1.runtime_type&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)   &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# -&gt; &quot;int&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# 来个啥都能装的Message&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;msg2 &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; Message(&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Param Type: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;msg2.param_type&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)       &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# -&gt; &quot;str&quot; (查不到设计时类型, 退回运行时)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;Runtime Type: &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;msg2.runtime_type&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;)   &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# -&gt; &quot;str&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完美!这套三层方案, 优先用最可靠的设计时信息, 实在没法查就认准运行时的铁证, 灵活又扎实。&lt;/p&gt;
&lt;h2 id=&quot;番外篇-forward-ref和循环引用的生存法则&quot;&gt;番外篇: Forward Ref和循环引用的生存法则&lt;/h2&gt;
&lt;p&gt;有时候你得定义互相引用的模型, 比如ORM或者复杂API schema。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt; A&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;BaseModel&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    b: &lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&apos;B&apos;&lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;  # B还没定义, 只能先写个字符串&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt; B&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;BaseModel&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    a: &lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&apos;A&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就像”薛定谔的类型”: A要知道B, B又要知道A。字符串’B’其实就是个IOU (暂欠条), 但等你真正要用的时候, Python得知道去哪兑现。&lt;/p&gt;
&lt;p&gt;如果你的模型定义在函数内部, 这些名字只在本地作用域里有。换个地方找, Python就懵了。&lt;/p&gt;
&lt;p&gt;解决办法就是: 把本地命名空间 (就是locals字典) 传给类型解析函数, 当地图用。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light-default github-dark-default&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#0d1117;color:#1f2328;--shiki-dark:#e6edf3; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; get_type_hints&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#8250DF;--shiki-dark:#D2A8FF&quot;&gt; create_circular_models&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;    class&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt; A&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;BaseModel&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;        b: &lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&apos;B&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;    class&lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt; B&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;BaseModel&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;        a: A&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    local_namespace &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt; locals&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;    hints_A &lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt; get_type_hints(A, &lt;/span&gt;&lt;span style=&quot;color:#953800;--shiki-dark:#FFA657&quot;&gt;localns&lt;/span&gt;&lt;span style=&quot;color:#CF222E;--shiki-dark:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;local_namespace)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#0550AE;--shiki-dark:#79C0FF&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;(hints_A[&lt;/span&gt;&lt;span style=&quot;color:#0A3069;--shiki-dark:#A5D6FF&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;])  &lt;/span&gt;&lt;span style=&quot;color:#6E7781;--shiki-dark:#8B949E&quot;&gt;# --&gt; &amp;#x3C;class &apos;__main__.create_circular_models.&amp;#x3C;locals&gt;.B&apos;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1F2328;--shiki-dark:#E6EDF3&quot;&gt;create_circular_models()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果模型都写在模块顶层, 其实Python全局作用域就够用。但只要你进函数里折腾, 这招&lt;code&gt;localns&lt;/code&gt;救你狗命。&lt;/p&gt;
&lt;h2 id=&quot;最后的心法&quot;&gt;最后的心法&lt;/h2&gt;
&lt;p&gt;别再问”为啥Python不给我类型?”而要换个思路:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;你到底在查啥? (蓝图, 类, 还是实际值?) 你有没有带对地图 (作用域) ?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;明白了这个, 类型自省就像侦探破案一样, 小心排查, 步步为营。Pydantic泛型再也不是黑盒, 而是可控可查的得力工具!&lt;/p&gt;
&lt;p&gt;(由人类创作, AI润色助力。)&lt;/p&gt;</description><pubDate>Tue, 16 Sep 2025 00:00:00 GMT</pubDate></item></channel></rss>