如何实现四元数的运算
与本文相关图书:《Python大学实用教程》
在前面的一篇文章《Python中的5对必知的魔法方法》中所介绍的“魔法方法”,或者说是特殊方法,其命名均是双下划线开始和结束。英文中称为“dunder methods”。为了更充分理解这类方法,本文通过一个示例,专门介绍此类方法的特点。
构建四元数对象
四元数是一个代数概念,通常用于描述旋转,特别是在3D建模和游戏中有广泛的应用。
其中
。
下面就一步一步演示此对象的创建方法,特别要关注双下划线开始和结束的那些特殊方法。
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
这是首先定义了__init__
初始化方法,通过这个方法,在创建实例时将
分别定义为实数。
>>> q1 = Quaternion(1, 2, 3, 4)
>>> q1
<__main__.Quaternion at 0x7f4210f483c8>
貌似一个四元数对象定义了。但是,它没有友好的输出。接下来可以通过定义__repr__
和__str__
,让它对机器或者开发者都更友好。继续在类中增加如下方法:
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "Quaternion({}, {}, {}, {})".format(
self.w, self.x, self.y, self.z)
def __str__(self):
return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
self.w, self.x, self.y, self.z)
对于这两方法,在前面的《Python中的5对必知的魔法方法》中已经有介绍,请参考。
>>> q1 # calls q1.__repr__
Quaternion(1, 2, 3, 4)
>>> print(q1) # calls q1.__str__
Q = 1.00 + 2.00i + 3.00j + 4.00k
实现代数运算
上述类已经能实现实例化了,但是,该对象还要参与计算,比如加、减法等。这类运算如何实现?
加法
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "Quaternion({}, {}, {}, {})".format(
self.w, self.x, self.y, self.z)
def __str__(self):
return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
self.w, self.x, self.y, self.z)
def __add__(self, other):
w = self.w + other.w
x = self.x + other.x
y = self.y + other.y
z = self.z + other.z
return Quaternion(w, x, y, z)
__add__
是用于实现对象的加法运算的特殊方法。这样就可以使用+
运算符了:
>>> q1 = Quaternion(1, 2, 3, 4)
>>> q2 = Quaternion(0, 1, 3, 5)
>>> q1 + q2
Quaternion(1, 3, 6, 9)
减法
类似地,通过__sub__
实现减法运算,不过,这次用一行代码实现。
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "Quaternion({}, {}, {}, {})".format(
self.w, self.x, self.y, self.z)
def __str__(self):
return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
self.w, self.x, self.y, self.z)
def __add__(self, other):
w = self.w + other.w
x = self.x + other.x
y = self.y + other.y
z = self.z + other.z
return Quaternion(w, x, y, z)
def __sub__(self, other):
return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))
有点酷。这里使用了实例对象的__dict__
属性,它以字典形式包含了实例的所有属性,请参考《Python大学实用教程》中的详细讲解。
乘法
乘法,如果了解一下线性代数,会感觉有点复杂。其中常见的一个是“点积”,自从Python3.5以后,用@
符号调用__matmul__
方法实现,对于四元数对象而言不能,就是元素与元素对应相乘。
对于四元数而言——本质就是向量,也可以说是矩阵,其乘法就跟矩阵乘法类似,比如,同样不遵守互换率:
。
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "Quaternion({}, {}, {}, {})".format(
self.w, self.x, self.y, self.z)
def __str__(self):
return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
self.w, self.x, self.y, self.z)
def __add__(self, other):
w = self.w + other.w
x = self.x + other.x
y = self.y + other.y
z = self.z + other.z
return Quaternion(w, x, y, z)
def __sub__(self, other):
return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))
def __mul__(self, other):
if isinstance(other, Quaternion):
w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z
x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y
y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z
z = self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x
return Quaternion(w, x, y, z)
elif isinstance(other, (int, float)):
return Quaternion(*[other * i for i in self.__dict__.values()])
else:
raise TypeError("Operation undefined.")
在__mul__
方法中,如果other
引用一个四元数对象,那么就会计算Hamilton积,并返回一个新的对象;如果other
是一个标量(比如整数),就会与四元数对象中的每个元素相乘。
如前所述,四元数的乘法不遵循交换律,但是,如果执行2 * q1
这样的操作,按照上面的方式,会报错——在上面的__mul__
方法中解决了q1 * 2
的运算,而一般我们认为这两个计算是相同的。为此,定义了·rmul·来解决此问题:
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "Quaternion({}, {}, {}, {})".format(
self.w, self.x, self.y, self.z)
def __str__(self):
return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
self.w, self.x, self.y, self.z)
def __add__(self, other):
w = self.w + other.w
x = self.x + other.x
y = self.y + other.y
z = self.z + other.z
return Quaternion(w, x, y, z)
def __sub__(self, other):
return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))
def __mul__(self, other):
if isinstance(other, Quaternion):
w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z
x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y
y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z
z = self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x
return Quaternion(w, x, y, z)
elif isinstance(other, (int, float)):
return Quaternion(*[other * i for i in self.__dict__.values()])
else:
raise TypeError("Operation undefined.")
def __rmul__(self, other):
if isinstance(other, (int, float)):
return self.__mul__(other)
else:
raise TypeError("Operation undefined.")
相等
比较两个四元数是否相等,可以通过定义__eq__
方法来实现。
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "Quaternion({}, {}, {}, {})".format(
self.w, self.x, self.y, self.z)
def __str__(self):
return "Q = {:.2f} + {:.2f}i + {:.2f}j + {:.2f}k".format(
self.w, self.x, self.y, self.z)
def __add__(self, other):
w = self.w + other.w
x = self.x + other.x
y = self.y + other.y
z = self.z + other.z
return Quaternion(w, x, y, z)
def __sub__(self, other):
return Quaternion(*list(map(lambda i, j: i - j, self.__dict__.values(), other.__dict__.values())))
def __mul__(self, other):
if isinstance(other, Quaternion):
w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z
x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y
y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z
z = self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x
return Quaternion(w, x, y, z)
elif isinstance(other, (int, float)):
return Quaternion(*[other * i for i in self.__dict__.values()])
else:
raise TypeError("Operation undefined.")
def __rmul__(self, other):
if isinstance(other, (int, float)):
return self.__mul__(other)
else:
raise TypeError("Operation undefined.")
def __eq__(self, other):
r = list(map(lambda i, j: abs(i) == abs(j), self.__dict__.values(), other.__dict__.values()))
return sum(r) == len(r)
其他运算
下面的方法,也可以接续到前面的类中,不过就不是特殊放方法了。
from math import sqrt
def norm(self):
return sqrt(sum([i**2 for i in self.__dict__.values()))
def conjugate(self):
x, y, z = -self.x, -self.y, -self.z
return Quaterion(self.w, x, y, z)
def normalize(self):
norm = self.norm()
return Quaternion(*[i / norm for in self.__dict__.values()])
def inverse(self):
qconj = self.conjugate()
norm = self.norm()
return Quaternion(*[i / norm for i in qconj.__dict__.values()])
用这些方法实现了各自常见的操作。
通过本文的这个示例,可以更深刻理解双下划线的方法为编程带来的便捷。
关注微信公众号:老齐教室
- 用VSCode开发一个asp.net core2.0+angular5项目(5): Angular5+asp.net core 2.0 web api文件上传
- 用VSCode开发一个asp.net core 2.0+angular 5项目(4): Angular5全局错误处理
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(3)
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(2)
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(1)
- element-ui el-input只显示下划线
- 怎样去阅读源码,这篇文章值的你一读
- HTML5 Blob与ArrayBuffer、TypeArray和字符串String之间转换
- Mybatis 3 配置 Log4j
- 基于Three.js的360度全景--photo-sphere-viewer--简介
- MyBatis3-配置使用log4j输出日志
- nginx支持跨域访问
- nginx静态资源文件无法访问,403 forbidden错误
- vue.js如何在标签属性中插入变量参数
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- NLP简报(Issue#9)
- 这些pandas技巧你还不会吗 | Pandas实用手册(PART II)
- 芝麻街跨界NLP,没有一个ERNIE是无辜的
- python实现多层感知机
- 数据分析必备!Pandas实用手册(PART III)
- python实现决策树
- NLP简报(Issue#10)
- Transformer温故知新
- 【作者解读】ERNIE-GEN : 原来你是这样的生成预训练框架!
- Python中那些低调有趣的模块
- 01背包问题讲解(动态规划)
- python实现随机森林
- 在mysql中order by是怎样工作的?
- 多线程应用 - 阻塞队列ArrayBlockingQueue详解
- Java虚拟机 - 超级详细的类加载说明