Python光学仿真:面向对象光学元件类的实现与代码解析
本文深入解析Python光学仿真中面向对象光学元件类的实现。通过定义包含前后表面的Opti类,封装索引与点集数据,并提供插入、删除表面的方法。结合try-except异常处理机制,演示了如何构建可动态调整的光学模型,并解析了__name__属性在模块导入与直接运行时的行为差异,为构建复杂光学系统奠定基础。
光学元件类
平面反射镜模型相对简单,仅需处理单一平面即可。然而,其他复杂光学元件需同时考量光线在入射面与出射面的交互行为。
针对这一挑战,初步解决方案是将光学元件拆解为前表面和后表面。若光线在元件内部发生多次反射,则可将元件拆解为对应反射次数的表面数量,这种处理方式直观且易于实现。
这体现了程序员的抽象思维:世界不再仅仅是视觉呈现,而是由抽象零部件构成。但程序员与常人的思维差异并非不可逾越,我们既能拆解世界,也能将拆解后的部件重新构建回现实世界。
若将问题复杂化,光学系统中包含众多元件,光线会多次穿过每个元件,且每次的入射点与出射点均存在偏差。此外,光学元件对光能的吸收会导致发热,而发热引起的形变会改变入射点与出射点,进而影响后续光线与元件的相互作用。
因此,每个光学元件除固定的前、后表面外,还包含入射点、出射点、发热及形变等动态参数。为解决这一贴近现实的问题,我们需要构建一种更贴近现实的数据类型,即创建一个对象,该对象能封装各类变量与功能,并在输入参数后自动更新状态。
这便是面向对象编程的核心思想。
class Opti():
def __init__(self,edge1,edge2):
self.edge1 = edge1
self.edge2 = edge2
在前例中,我们定义了一个光学元件类,该类包含两个表面,可以是平面或弧面,从而建立了一个基础类。其中,__init__为初始化方法,self表示类实例本身。通常,若类中方法未加修饰符,需将self作为第一个参数。
self.edge1表示在Opti类中,存在一个名为edge1的成员变量。当类被初始化时,即可对该变量进行赋值操作。
部分元件仅有一个表面,如全反镜;部分则拥有多个表面,如偏振立方体。在实验过程中,我们需要对比不同光学元件以获得最佳结果。因此,若需修改已构建的光学元件,应如何处理?
解决方法简单:只需增加一个方法,支持插入或删除新表面即可。
#文件Opti.py
class Opti():
def __init__(self,edges=[[(0,-1),(0,1)],[(0,1),(0,-1),(1/2,0)]]):
self.edges = [{'index':i,'dots':edges[i]}
for i in range(len(edges))]
#edge格式为(dot1,dot2,...)
def insertEdge(self,edge,albedo=0):
self.edges.append(
{'index':len(self.edges),'dots':edge})
#可接受编号和点集
def delEdge(self,edge):
try:
if isinstance(edge,list): #如果edge的类型是list
for edg in self.edges:
if edg['dots']==edge:
edge = edg['index']
del self.edges[edge]
except:
print("no this edge")
上述代码中,初始化函数预设了默认值,这与普通函数无异。默认插入的两个曲面分别为平面[(0,-1),(0,1)]和弧面[(0,1),(0,-1),(1/2,0)],可见默认生成的是一个平凸镜。
成员变量self.edges即光学表面列表,每个表面包含两个参数:索引index和点集dots。根据此前的光学抽象,当点集包含两个点时代表平面,包含三个点时代表弧面。
方法insertEdge用于插入一个光学表面,其中编号为该表面在self.edges中的索引号;delEdge顾名思义用于删除某个光学表面。若传入的edge为列表,则说明传入的是参数确定的曲面,此时通过遍历self.edges找到该表面并获取其索引。
若传入参数为单值,则说明传入的是索引号,直接执行删除操作即可。
该方法中使用了一种新的代码块try:...except...,这是一种异常处理机制:尝试运行try:块中的代码,若运行失败,则执行except。若未能成功执行delEdge,说明输入的表面不存在于该光学元件中,此时输出"no this edge"。
这似乎是首次接触print命令,通常这是程序员最早接触的函数,因为大多数程序员敲下的第一行代码便是打印输出。
print("hello world")
print('hello world')
此外,除了数值类型,我们还认识了另一种数据类型:字符。在Python中,可通过双引号或单引号表示单个字符或字符串。即上述hello world代码中,两种写法均正确且无区别。
现在,我们已经编写了一个类,可以在命令行中输入以下代码进行调用:
>>> from Opti import Opti
>>> Opti.__name__ #这是什么鬼
'Opti'
>>> x = Opti() #创建对象,由于未输入参数,故皆为默认值
>>> x.edges #现实类成员
[{'index': 0, 'dots': [(0, -1), (0, 1)]}, {'index': 1, 'dots': [(0, 1), (0, -1), (0.5, 0)]}]
>>> x.delEdge(1) #调用类方法
>>> x.edges #果然少了一个边
[{'index': 0, 'dots': [(0, -1), (0, 1)]}]
>>> x.delEdge(1) #删除不存在的边是不可能的
no this edge
>>>
首先,from Opti import Opti中的两个Opti并不相同:前者代表包`Opti.py`,后者代表`Opti.py`中的类`Opti`,导入后即可调用。
随后出现了一个较为特殊的现象:我们在类中并未定义__name__,但调用后却产生了值。
无需惊慌,这其实是熟悉的概念。可以将__name__理解为Python内部的内置属性。当我们直接执行某一.py文件时,该__name__的值为__main__;否则,其值为类的名字。至此,我们应能理解入口函数的真正意义。
继续向下,几乎所有操作都在预期之中。
以上为Python光学仿真中面向对象光学元件类实现的详细内容,更多关于Python光学元件类实现的资料,请关注本站其他相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













