
简介一份基于Python开发的纳甲六爻排盘项目面向易学编程入门者及进阶学习者适合作为毕业设计、课程作业或工程实训参考。项目以函数独立编写完整覆盖安易卦爻、阳历阴历与干支旬空、卦宫六亲、六神世应、纳甲五行等排盘要素并提供卦符、变爻、卦名等关键数据的结构化输出针对归魂卦六亲判断错误等细节代码中给出了明确的修正方法与排错思路。资源包共35个文件以18个Python脚本为主辅以Markdown文档、JSON/YAML配置、模板文件及测试用例整体约101KB轻量清晰。tests目录覆盖卦象、卦宫、旬空、纳甲等多个模块的单元测试sample.py可快速体验排盘流程适合对照源码理解排盘逻辑、学习函数拆分与测试编写并在此基础上自行扩展功能。目前已有148人学习值得易学与Python交叉领域的学习者参考。1. 为什么用 Python 写纳甲六爻排盘先解决历法转干支再谈卦象纳甲六爻排盘看起来是卦象问题实际上是历法问题加编码问题。起一卦要先确定年月日时的干支再把干支装进八宫卦的壳里按五行生克推六亲、按日干起六神、按卦位定世应——这一串动作本质上是规则驱动的数据转换。Python 处理这种带明确规则的转换有先天优势字符串处理顺手、字典映射直观、逻辑表达接近自然语言而且历法库生态成熟农历到干支的换算不需要自己啃《协纪辨方书》。排盘项目最容易被低估的部分是时间底座。用户报一个公历时间程序要输出完整的卦盘中间隔着的不是一张卦象表而是农历、节气、干支三者叠加的历法计算。这一层做不扎实后面卦排得再漂亮也是错的。本文按一条完整落地的路径走先把时间转干支的规则讲清楚再实现六十四卦和纳甲然后补上六亲六神世应最后给一个可运行的 Python 类设计和验证方法。适合想自己写排盘工具、或者想把六爻逻辑接入自动化流程的开发者懂一点六爻基础术语会更容易跟上但不懂也不影响读代码。2. 农历与干支换算Python 排盘的时间底座2.1 年柱与月柱的边界不是春节是立春很多人写排盘程序第一个坑就踩在年柱上。农历正月初一不等于干支年的起点干支年以立春为界。拿 2024 年举例2024 年 2 月 4 日立春立春之前出生的人年柱仍然算癸卯立春之后才算甲辰。月柱同样跟节气走正月从立春开始而不是从正月初一开始。这意味着排盘程序必须先计算节气再决定干支归属。Python 里做节气计算常见做法是用sxtwl这个库。它封装了农历、公历、干支、节气的换算逻辑准确性经过验证比自己实现寿星公式要可靠得多。在没有网络的环境下也能本地计算适合作为排盘引擎的地基。安装方式pip install sxtwl拿到一个公历时间后先转成农历日对象再取出干支信息import sxtwl # 2024年2月10日 上午10点公历 day sxtwl.fromSolar(2024, 2, 10, 10) # 年柱、月柱、日柱的干支索引 year_tg day.getYearGZ().tg # 天干索引0甲 year_dz day.getYearGZ().dz # 地支索引0子 month_tg day.getMonthGZ().tg month_dz day.getMonthGZ().dz day_tg day.getDayGZ().tg day_dz day.getDayGZ().dz tg [甲, 乙, 丙, 丁, 戊, 己, 庚, 辛, 壬, 癸] dz [子, 丑, 寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥] print(f年柱: {tg[year_tg]}{dz[year_dz]}) print(f月柱: {tg[month_tg]}{dz[month_dz]}) print(f日柱: {tg[day_tg]}{dz[day_dz]})sxtwl返回的天干地支都是索引值0 对应甲和子按这个规则映射到中文即可。月柱和年柱都已经按节气处理过不需要自己在代码里判断立春时刻。这里有一个细节getYearGZ返回的已经是以立春为界的年柱不是农历年的干支这正是排盘需要的。2.2 时柱与早晚子时最容易算错的边界时柱的地支固定子时是 23:00 到次日 01:00每个时辰两小时。但天干要配合日干用五鼠遁来推甲己日从甲子时起乙庚日从丙子时起丙辛日从戊子时起丁壬日从庚子时起戊癸日从壬子时起。这套规则可以编码成一个从日干索引到子时天干索引的映射表# 五鼠遁下标是日干索引值是子时天干索引 wu_shu_dun [0, 2, 4, 6, 8, 0, 2, 4, 6, 8] def get_hour_gz(day_tg_idx, hour): # 23点和0点都算子时但0点的日柱要小心 if hour 23: dz_idx 0 # 子 else: dz_idx (hour 1) // 2 % 12 hour_tg (wu_shu_dun[day_tg_idx] dz_idx) % 10 return hour_tg, dz_idx print(get_hour_gz(0, 10)) # 甲日巳时 # 输出 (4, 5)天干索引4是戊地支索引5是巳这里有一个必须处理的特殊场景23 点之后算不算次日六爻排盘里对这个有分歧主流做法是 23 点以后日柱仍然用当天但时柱按子时处理这样年柱月柱不变日柱不变只有时柱进入新一天。另一种流派认为 23 点以后日柱就算次日了。排盘工具面对这种情况最稳妥的是把两种选项做成一个参数early_zi默认按日不变、时进子处理并且把这一决策暴露在 API 里让使用者自己决定。时柱的取值还有一个容易遗漏的问题公历的时区。如果是给中国大陆用户用的工具直接用datetime的本地时间即可如果要做海外排盘需要先转换到 UTC8 再计算否则时辰会整体偏移。这一点等做到国际化再考虑第一版直接按用户输入的公历时间处理就好。2.3 用 sxtwl 处理农历与节气的取舍sxtwl不是唯一的历法库还有lunar_python也很常用。选择sxtwl的理由是它直接暴露了干支接口不需要自己再套一层农历推算而lunar_python的 API 更贴近中文命名适合快速原生开发但内部逻辑封装得更密排盘时如果要从Solar对象一路取到月柱天干需要多翻几层文档。实际项目中我会在历法层做一层薄封装不让业务代码直接依赖某个库。因为历法库的版本变化可能带来边界结果的差异做一层接口隔离后换库就只改一个文件class LunarDate: def __init__(self, dt): self.day sxtwl.fromSolar(dt.year, dt.month, dt.day, dt.hour) def year_gz(self): tg, dz self.day.getYearGZ().tg, self.day.getYearGZ().dz return TIAN_GAN[tg] DI_ZHI[dz] def month_gz(self): tg, dz self.day.getMonthGZ().tg, self.day.getMonthGZ().dz return TIAN_GAN[tg] DI_ZHI[dz] def day_gz(self): tg, dz self.day.getDayGZ().tg, self.day.getDayGZ().dz return TIAN_GAN[tg] DI_ZHI[dz]TIAN_GAN和DI_ZHI是全局常量列表分别存十个天干和十二个地支的中文字符。封装之后后面的卦象模块只需要调用LunarDate的方法不需要关心sxtwl的存在。这一层隔离对排盘工具来说值得做因为历法库的升级频率不高但出问题就是整体性的错误单独隔离出来后排查范围会小很多。3. 六十四卦与纳甲把易经编码成 Python 数据结构3.1 从八卦到六十四卦的快速映射排盘拿到四柱干支之后接下来要知道卦是什么。手动起卦方式有三种掷钱法、时间起卦、摇卦。无论哪种方式最终得到的都是六个爻的状态——少阳、少阴、老阳、老阴。老阳和老阴是变爻变爻会生成变卦。在 Python 里的表示方式可以非常直接用 0 和 1 表示阴阳用布尔值标记是否变动。from dataclasses import dataclass dataclass class Yao: value: int # 0为阴1为阳 changing: bool # 是否变爻 class Gua: def __init__(self, yaos): self.yaos yaos # 从下往上yaos[0]是初爻 property def upper(self): # 上卦第4、5、6爻 return self._trigram(3, 6) property def lower(self): # 下卦第1、2、3爻 return self._trigram(0, 3) def _trigram(self, start, end): bits [str(y.value) for y in self.yaos[start:end]] return int(.join(reversed(bits)), 2)拿 1 和 0 表示阴阳二进制的三位就能表达一个八卦乾是 111 也就是 7坤是 000 也就是 0。上卦和下卦合并成六位二进制就能对应到六十四卦的序号0是坤卦63是乾卦中间按乾兑离震巽坎艮坤的顺序映射。需要一份六十四卦的字典表存卦名和卦辞索引这个表直接写在 Python 模块里即可数据结构用最简单的字典GUA_NAMES { 0: 坤为地, 1: 山地剥, 2: 水地比, 3: 风地观, # 这里按二进制数值顺序填满64卦 63: 乾为天 }实际项目里我倾向于把六十四卦表单独放一个 JSON 文件或 Python 模块里面不只存卦名还可以存卦辞、大象辞、六个爻的爻辞索引。这样排盘程序、断卦模块、前端展示模块都从同一份数据源读避免数据不一致。3.2 纳甲的本质把天干地支装进卦的每个爻纳甲是六爻排盘的核心步骤规则说起来很简单乾卦纳甲壬坤卦纳乙癸艮纳丙兑纳丁坎纳戊离纳己震纳庚巽纳辛。每个卦的内卦和外卦分别纳不同的天干地支则按阳卦顺行、阴卦逆行的规律排布。以乾卦为例内卦三爻纳甲初爻甲子、二爻甲寅、三爻甲辰外卦三爻纳壬四爻壬午、五爻壬申、六爻壬戌。坤卦内卦纳乙初爻乙未、二爻乙巳、三爻乙卯外卦纳癸四爻癸丑、五爻癸亥、六爻癸酉。这套规则的记忆成本很高但编码成 Python 却很简单只需要一张映射表NA_JIA { 乾: {inner: [甲子, 甲寅, 甲辰], outer: [壬午, 壬申, 壬戌]}, 坤: {inner: [乙未, 乙巳, 乙卯], outer: [癸丑, 癸亥, 癸酉]}, 震: {inner: [庚子, 庚寅, 庚辰], outer: [庚午, 庚申, 庚戌]}, 巽: {inner: [辛丑, 辛亥, 辛酉], outer: [辛未, 辛巳, 辛卯]}, 坎: {inner: [戊寅, 戊辰, 戊午], outer: [戊申, 戊戌, 戊子]}, 离: {inner: [己卯, 己丑, 己亥], outer: [己酉, 己未, 己巳]}, 艮: {inner: [丙辰, 丙午, 丙申], outer: [丙戌, 丙子, 丙寅]}, 兑: {inner: [丁巳, 丁卯, 丁丑], outer: [丁亥, 丁酉, 丁未]}, }要取某一卦每个爻的纳甲先判断上下卦各是什么然后从NA_JIA里按位置取def get_na_jia(gua): na [] * 6 lower_gua trigram_name(gua.lower) # 取乾这样的中文卦名 upper_gua trigram_name(gua.upper) na[0], na[1], na[2] NA_JIA[lower_gua][inner] na[3], na[4], na[5] NA_JIA[upper_gua][outer] return na这段代码把纳甲过程从背口诀变成了查表 按位复制逻辑复杂度降到了最低。trigram_name是一个把三爻二进制索引转成中文卦名的辅助函数按乾兑离震巽坎艮坤的顺序映射。3.3 卦宫与世应八宫卦序的代码表达排盘还需要知道一个卦属于哪个宫、世爻和应爻在哪一爻。这里用的是八宫卦序乾、坎、艮、震、巽、离、坤、兑各管八个卦从本宫卦开始一世卦、二世卦、三世卦、四世卦、五世卦、游魂卦、归魂卦。世爻的位置规律是本宫卦世在上爻一世卦世在初爻二世卦世在二爻三世卦世在三爻四世卦世在四爻五世卦世在五爻游魂卦世在四爻归魂卦世在三爻。应爻永远在隔两位的位置。这一步如果从卦的二进制去推算可以在运行时递归生成整个八宫卦序def build_gong(): gong {} base_gua [乾, 坎, 艮, 震, 巽, 离, 坤, 兑] for g in base_gua: guas [g] * 8 gong[g] guas # 实际应该存每一卦的六爻二进制值 return gong手写完整八宫卦序容易出错更实用的做法是运行时生成方法是本宫卦的初爻变得到一世卦二爻变得到二世卦依此类推到五世卦然后四爻回头变得游魂卦再把下卦整体变得归魂卦。这套变换逻辑可以写成统一的change_yao函数每步调用一次得到八个卦的序列。世应位置按上爻、初爻、二爻、三爻、四爻、五爻、四爻、三爻的顺序排存成一个固定列表查索引即可。4. 六亲、六神与世应的五行逻辑4.1 六亲的推算从本宫五行到每个爻的五行生克纳甲装完之后每个爻有了地支地支有五行属性。六亲的判断基准是本宫的五行属性不是日干的五行。比如乾宫卦属金那么这个卦里的所有爻都以金为我生我者为父母、我生者为子孙、克我者为官鬼、我克者为妻财、比和者为兄弟。编码时先建一张地支五行表再写一个通用的生克判断函数DZ_WX {子: 水, 丑: 土, 寅: 木, 卯: 木, 辰: 土, 巳: 火, 午: 火, 未: 土, 申: 金, 酉: 金, 戌: 土, 亥: 水} SHENG {木: 火, 火: 土, 土: 金, 金: 水, 水: 木} KE {木: 土, 土: 水, 水: 火, 火: 金, 金: 木} def get_liuqin(gong_wx, yao_wx): if yao_wx gong_wx: return 兄弟 if SHENG[gong_wx] yao_wx: return 子孙 if SHENG[yao_wx] gong_wx: return 父母 if KE[gong_wx] yao_wx: return 妻财 if KE[yao_wx] gong_wx: return 官鬼这段代码的顺序有讲究先判断比和再判断相生最后判断相克。因为相生和相克在五行循环里可能出现逻辑上的重叠判断但按这个顺序实测是唯一的不会有歧义。以乾宫金为例金生水所以水是子孙土生金所以土是父母金克木木是妻财火克金火是官鬼金见金是兄弟。需要注意的是六亲判断依赖本宫而本宫的确定依赖上一节的八宫卦序。也就是说必须先算出卦属于哪一宫取该宫的五行再逐爻判断。这个依赖次序在代码里要保持在正确的位置不要先算六亲再找宫。4.2 六神起法按日干定顺序六神也叫六兽顺序固定青龙、朱雀、勾陈、螣蛇、白虎、玄武。起法按日干来甲乙日起青龙丙丁日起朱雀戊日起勾陈己日起螣蛇庚辛日起白虎壬癸日起玄武。然后从初爻开始按顺序分配初爻是日干对应的那个神二爻是下一个依此类推。用代码实现就是查起点的索引然后循环取模LIU_SHEN [青龙, 朱雀, 勾陈, 螣蛇, 白虎, 玄武] # 日干索引0-9对应甲乙丙丁戊己庚辛壬癸 QI_SHEN [0, 0, 1, 1, 2, 3, 4, 4, 5, 5] def assign_liushen(day_tg_idx): start QI_SHEN[day_tg_idx] return [LIU_SHEN[(start i) % 6] for i in range(6)]QI_SHEN表里同一个值的两个相邻位置代表了甲乙、丙丁这种分组规则。这样写比if-else链更紧凑而且如果要调整起法规则——比如有些流派对戊己日的起法有争议——只需要改这个列表。4.3 世应确定的边界情况世应规则前面已经提到本宫卦世在上爻归魂卦世在三爻。这里有一个容易写错的细节游魂卦和四世卦的世爻都在四爻但两者属于不同的阶段不能混淆。游魂卦是五世卦的第四爻再变归魂卦是游魂卦的下卦变回本宫卦的下卦。写代码时世应位置可以直接查表不需要每一步都推算SHI_POS {0: 5, 1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 3, 7: 2} # 键是本宫卦的第几个变体0本宫1一世...6游魂7归魂 def get_shi_ying(gua_index): shi SHI_POS[gua_index] ying (shi 2) % 6 # 应爻在隔两位的位置 return shi, ying注意世爻位置用的是爻的索引0 到 5 对应初爻到上爻。应爻用(shi 2) % 6取模计算但这里要小心世爻在五爻时应爻计算是(4 2) % 6 0回到初爻这个符合隔两位的规则。而世爻在上爻时应爻是(5 2) % 6 1对应二爻也是对的。计算世应前要先确定gua_index这个索引在八宫卦序生成的时候就应该存下来不要临时再算。思考到这一步六爻排盘的核心算法模块已经完整了历法干支、卦象表示、纳甲、六亲、六神、世应。剩下的问题是怎么把这些模块组织成一个干净的可调用项目而不是散落一地的函数。5. 完整排盘流程与 Python 类设计5.1 一个不留状态在全局的类结构设计排盘项目最常见的结构性问题就是把所有逻辑写在一个大函数里参数传得满天飞。更合理的划分是三个类LunarDate负责历法、GuaBuilder负责卦象组装、PanOutput负责输出格式。每个类只做一件事排盘流程组合这三个类。class LiuYaoPaiPan: def __init__(self, lunar: LunarDate): self.lunar lunar self.na_jia [] self.liu_qin [] self.liu_shen [] self.shi_yao 0 self.ying_yao 0 def build(self, gua: Gua, gua_index: int): # 步骤1纳甲 self.na_jia get_na_jia(gua) # 步骤2六亲 gong_wx GONG_WX[get_gong_name(gua)] for dz in self.get_dizhis(): self.liu_qin.append(get_liuqin(gong_wx, DZ_WX[dz])) # 步骤3六神 self.liu_shen assign_liushen(self.lunar.day_gz()[0]) # 步骤4世应 self.shi_yao, self.ying_yao get_shi_ying(gua_index)build方法按步骤调用前面各节实现的功能每一步的结果都存储在实例属性中。这样设计的好处是测试方便构造一个LiuYaoPaiPan实例调用build直接断言每个字段的值每个模块可以独立验证。输出可以做成字典或者直接定义成一个 dataclass。考虑到后续接入 Web 接口或前端展示输出层用字典更灵活序列化成 JSON 不需要额外处理。每个爻需要包含的信息是农历时间、干支、阴阳、纳甲、六亲、六神、是否为世应爻、是否为变爻。这样前端一行表格就能渲染完整卦盘。5.2 时间起卦的完整调用示例时间起卦是六爻排盘最常接入的方式完整的调用链从用户输入公历时间开始到输出完整卦盘结束。下面是命令行场景下的完整代码import datetime def pai_pan_from_datetime(dt: datetime.datetime, methodtime): lunar LunarDate(dt) # 时间起卦年月日 除8取上卦年月日时 除8取下卦 # 这里以用户输入的月日时计算作为示例 year_gz_idx lunar.day.getYearGZ().dz % 8 month_day_idx (dt.month dt.day) % 8 hour_idx dt.hour % 8 upper year_gz_idx # 上卦二进制值 lower month_day_idx # 构造六爻0阴1阳这里简化为按模值奇偶取阴阳 yaos [] for i in range(6): val (upper if i 3 else lower) (1 (i % 3)) yaos.append(Yao(value1 if val else 0, changingFalse)) gua Gua(yaos) paipan LiuYaoPaiPan(lunar) paipan.build(gua, gua_index0) return paipan dt datetime.datetime(2024, 3, 15, 10, 30) pan pai_pan_from_datetime(dt) print(pan.liu_qin)这段代码里的起卦取数规则做了简化实际上时间起卦的取数方式有很多流派有的用农历年月日数字有的用天干地支序号有的要考虑时辰对应数字。真实项目中起卦的取数逻辑应该独立成函数并且把规则参数化方便不同流派切换。5.3 变卦处理从本卦生成变卦链六爻排盘一个关键扩展点是变卦。老阳和老阴是变爻变爻会生成变卦老阳变阴老阴变阳。变卦的纳甲、六亲、六神重新计算但变卦的纳甲基于变卦自身的卦象而不是本卦。这个逻辑在校验断卦时很重要因为断卦时动爻要参考变爻的纳甲和六亲。def gen_bian_gua(gua: Gua) - Gua: yaos [] for y in gua.yaos: if y.changing: yaos.append(Yao(value1 - y.value, changingFalse)) else: yaos.append(Yao(valuey.value, changingFalse)) return Gua(yaos)变卦生成后需要用build方法再跑一遍完整流程得到变卦自己的纳甲和六亲。为了让动爻和变爻对应起来输出数据结构里每个本卦爻应该持有指向变卦对应爻的引用。这个引用关系在生成变卦时就已经确定了因为变卦第 i 爻对应本卦第 i 爻。6. 对照手排验证结果用测试固化规则写完排盘代码后的第一件事不是继续加功能而是验证正确性。六爻排盘的规则精细一步错后面全错。验证手段分两层第一层是拿已知的手排案例做对照测试第二层是写单元测试把关键规则固化成不回归的逻辑。手排案例建议找 5 到 8 组不同干支组合的完整排盘结果最好是书上有标准答案的卦例。每组案例至少覆盖到不同的年柱和月柱组合、不同的日干六神起点的变化、不同宫的卦五行属性不同导致六亲分布不同、至少一个变卦案例。测试代码直接断言输出结果def test_qian_gong_liuqin(): # 乾宫本宫卦金 gua Gua([Yao(1, False)] * 6) # 乾为天全部阳爻 lunar LunarDate(datetime.datetime(2024, 2, 10, 10)) pan LiuYaoPaiPan(lunar) pan.build(gua, 0) assert pan.na_jia[0] 甲子 assert pan.liu_qin[0] 父母 assert pan.shi_yao 5 # 本宫卦世在上爻这个测试断言了三件事纳甲的初爻是否正确、六亲是否按乾宫金来判断、世爻位置是否正确。如果这三项都过了说明核心链路基本打通。这里提一个容易忽视的细节测试用例里的公历时间不能随便选需要选在节气边界前后各一个比如立春前后各一天专门测试年柱月柱的边界逻辑。排盘工具上线后还需要加一层校验逻辑。用户输入的时间如果落在节气当天需要程序自动判断是否跨过节气时刻。sxtwl的fromSolar通过时分秒参数都传入小时后会自动处理这个边界但很多调用方只传年月日小时传 0就会在节气当天产生偏差。排盘 API 的参数设计上建议强制要求传入完整时间不提供只按日期的重载方法从源头上降低边界问题出现的概率。如果只做简易版排盘日期排盘可以保留但要在输出里标注未区分时辰仅按子时处理。整个排盘流程走通后后续值得扩展的方向是断卦辅助功能用 Python 实现用神分析、月破日破判断、旬空处理、生克冲合的组合推演。这些功能仍然建立在本文的排盘数据之上数据层正确断卦层才有意义。本文还有配套的精品资源点击获取