我对 Python 相当陌生,并尝试使用构造函数设置一个类,以具有少量必需属性和大量可选属性,并具有默认值和可接受输入的定义。
我尝试使用 argparse 模块,但我不明白如何解析参数然后将结果传递到类的属性中。这也不允许我定义预期输入的逻辑标准。
我想做一些与此 MATLAB 脚本类似的事情。
methods
function obj = Platform(ClassID,varargin)
inPar = inputParser;
expectedClass = {'Ownship', 'Wingman', 'Flight Group', 'Unknown', 'Suspect', 'Neutral', 'Friend', 'Foe'};
validClassID = @(x) any(validatestring(x,expectedClass));
addRequired(inPar,'ClassID',validClassID)
defaultDim = struct('length', 0, 'width', 0, 'height', 0, 'oOffset', [0 0 0]);
validDim = @(x) ~isempty(intersect(fieldnames(x),fieldnames(defaultDim)));
addOptional(inPar,'Dimensions',defaultDim,validDim)
defaultPos = [0 0 0];
validPos = @(x) isclass(x,'double') && mean(size(x) == [1 3]);
addOptional(inPar,'Position',defaultPos,validPos)
defaultOr = [0 0 0];
validOr = @(x) isclass(x,'double') && mean(size(x) == [1 3]);
addOptional(inPar,'Orientation',defaultOr,validOr)
defaultTraj = struct('Waypoints',[0 0 0],...
'TimeofArrival',0,...
'Velocity',[0 0 0],...
'Orientation',[0 0 0]);
validTraj = @(x) ~isempty(fieldnames(x),fieldnames(defaultTraj));
addOptional(inPar,'Trajectory',defaultTraj,validTraj)
expectedDL = {'LK-16', 'MADL', 'VMF'};
defaultDL = {};
validDL = @(x) any(validatestring(x,expectedDL));
addOptional(inPar,'DataLinks',defaultDL,validDL)
defaultSens = {};
validSens = @(x) isa(x,'Sensor');
addOptional(inPar,'Sensors',defaultSens,validSens)
parse(inPar,ClassID,varargin{:})
obj.PlatformID = randi([1 10000]);
obj.ClassID = inPar.Results.ClassID;
obj.Dimensions = inPar.Results.Dimensions;
obj.Position = inPar.Results.Position;
obj.Orientation = inPar.Results.Orientation;
obj.Trajectory = inPar.Results.Trajectory;
obj.Sensors = inPar.Results.Sensors;
obj.DataLinks = inPar.Results.DataLinks;
end
Python 不需要进行这种临时字符串和数组解析排序。
好的 Python 代码是面向对象的。它们应该被封装为“有意义类型的对象”,而不是作为原始字符串和数组传递值。这些对象应该负责在构造时验证自身并在其整个生命周期中维护其不变量。 更好的 Python 代码利用静态类型提示将大部分验证工作转移到代码运行之前。
惯用的 Python 翻译可能看起来像这样(带有一些自由猜测):
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import NamedTuple, Literal
class Position(NamedTuple):
x: float
y: float
z: float
@classmethod
def origin(cls) -> Position:
return cls(0, 0, 0)
class Orientation(NamedTuple):
yaw: float
pitch: float
roll: float
@classmethod
def pos_x(cls) -> Orientation:
return cls(0, 0, 0)
@classmethod
def pos_y(cls) -> Orientation:
return cls(1, 0, 0)
@classmethod
def pos_z(cls) -> Orientation:
return cls(0, 1, 0)
class Geometry(NamedTuple):
extent: Position
o_offset: Position
@classmethod
def unit_cube(cls) -> Geometry:
return cls((1, 1, 1), (0, 0, 0))
@dataclass
class Trajectory:
waypoints: list[Position] = field(default_factory=list)
time_of_arrival: float = 0
velocity: Position = Position.origin()
orientation: Orientation = Orientation.pos_x()
class Platform(ABC):
_geometry: Geometry
_position: Position
_orientation: Orientation
_trajectory: Trajectory
_datalinks: list[Literal['LK-16', 'MADL', 'VMF']]
_sensors: list[Sensors]
def __init__(
self,
geometry: Geometry = Geometry.unit_cube(),
pos: Position = Position.origin(),
orientation: Orientation = Orientation.pos_x(),
trajectory: Trajectory | None = None,
datalinks: list[Literal['LK-16', 'MADL', 'VMF']] | None = None,
sensors: list[Sensors] | None = None,
) -> None:
if trajectory is None:
trajectory = Trajectory()
if datalinks is None:
datalinks = []
if sensors is None:
sensors = []
self._geometry = geometry
self._position = pos
self._orientation = orientation
self._trajectory = trajectory
self._datalinks = datalinks
self._sensors = sensors
@abstractmethod
def do_something_class_specific(self) -> None:
...
class NeutralPlatform(Platform):
def do_something_class_specific(self) -> None:
self.watch_and_wait()
class FooPlatform(Platform):
def do_something_class_specific(self) -> None:
self.attack_mode()
就是这样。只要类型检查通过,您构造的任何
Platform
都将得到充分验证。无需对类型系统已经可以验证的内容进行手动验证。
需要更多不变量?以适当的类型强制执行它们。在良好的面向对象设计中,Platform
不需要(也不应该)需要知道什么是有效的
Orientation
,只需知道它有一个并且它已经有效。