【问题标题】:Is it possible to add methods on the fly to MATLAB classes?是否可以将方法动态添加到 MATLAB 类?
【发布时间】:2012-08-05 10:41:15
【问题描述】:

编写dynamicprops 的子类允许我向对象动态添加属性:

addprop(obj, 'new_prop')

这很好,但我也很想为这些属性动态创建set / get 函数。或对这些动态属性起作用的分析函数。

到目前为止,我对 Matlab 的体验是,一旦我创建了一个类的实例,就不可能添加新方法。这很麻烦,因为我的对象可能包含大量数据,每次我想添加新方法时都必须重新加载(因为我必须这样做clear classes)。

那么有没有办法即时添加方法?

【问题讨论】:

  • 你可以添加新方法,只要你在之后清除类,但你想绕过它。我认为这通常不会奏效。

标签: matlab class oop dynamic-properties


【解决方案1】:

您不能像添加动态属性那样添加方法。但是,有两种方法可以在开发过程中实现新方法,不需要您每次都重新加载数据。

(1) 我将标准方法编写为单独的函数,并在开发过程中将它们称为myMethod(obj)。一旦我确定它们是稳定的,我将它们的签名添加到类定义文件中——这当然需要clear classes,但它是一个延迟很大的,有时你可能不得不关闭 Matlab ,无论如何。

(2) 使用 set/get 方法,事情变得有点棘手。如果您使用dynamicprops 添加新属性,您也可以指定它们的 set/get 方法,但是(这些方法/函数很可能希望接收属性的名称,以便他们知道要引用什么):

addprop(obj,'new_prop');
prop = findprop(obj,'new_prop');
prop.SetMethod = @(obj,val)yourCustomSetMethod(obj,val,'new_prop')

编辑

(2.1) 下面是一个如何设置隐藏属性来存储和检索结果的示例(基于jmlopez' answer)。显然,如果您对实际设计的内容有更好的了解,这可以得到很大的改进

classdef myDynamicClass < dynamicprops
    properties (Hidden)
        name %# class name
        store %# structure that stores the values of the dynamic properties
    end
    methods
        function self = myDynamicClass(clsname, varargin)
            % self = myDynamicClass(clsname, propname, type)
            % here type is a handle to a basic datatype.
            self.name_ = clsname;
            for i=1:2:length(varargin)
                key = varargin{i};
                addprop(self, key);
                prop = findprop(self, key);
                prop.SetMethod = @(obj,val)myDynamicClass.setMethod(obj,val,key);
                prop.GetMethod = @(obj)myDynamicClass.getMethod(obj,key);
            end
        end
        function out = classname(self)
            out = self.name_;
        end
    end
    methods (Static, Hidden) %# you may want to put these in a separate fcn instead
        function setMethod(self,val,key)
           %# have a generic test, for example, force nonempty double
           validateattributes(val,{'double'},{'nonempty'}); %# will error if not double or if empty

           %# store
           self.store.(key) = val;

        end
        function val = getMethod(self,key)
           %# check whether the property exists already, return NaN otherwise
           %# could also use this to load from file if the data is not supposed to be loaded on construction 
           if isfield(self.store,key)
              val = self.store.(key);
           else
              val = NaN;
           end
        end
    end
end

【讨论】:

  • 所以myMethod 实际上是一个泛型函数(而不是类方法),您可以随意更改,对吧?
  • @memyself:完全正确。之后,您可以将myMethod 复制到classdef 文件中,或者简单地将签名添加到classdef 文件中并将myMethod 移动到class 文件夹中。
  • @Jonas,是否有可能知道我们试图从yourCustomMethod 中更改什么属性?在您提供的示例中,我们可能有类似 obj.new_prop = val; 的东西来设置值。我们能以某种方式访问​​属性名称吗?
  • @jmlopez:你是对的,当然。您很可能必须将属性名称显式传递给方法。
  • @jmlopez: @(obj,val) is an anonymous function that calls setMethod` 带有参数obj,val。构造时,arg1等在当前工作空间中定义,一旦被调用就会传递给setMetod
【解决方案2】:

我添加这个答案是因为我认为这不直观。至少此刻对我自己不是。找到这个问题后,我认为我拥有了为我的动态类定义 set/get 方法所需的东西。我想要实现的只是类似于 python 使用其__setattr__ 方法所做的事情。无论如何,这里是 @jonas 不久前创建的类的 continuation,并进行了一些修改以添加我们的自定义 set 方法。

classdef myDynamicClass < dynamicprops
    properties (Hidden)
        name_ %# class name
    end
    methods
        function self = myDynamicClass(clsname, varargin)
            % self = myDynamicClass(clsname, propname, type)
            % here type is a handle to a basic datatype.
            self.name_ = clsname;
            for i=1:2:length(varargin)
                key = varargin{i};
                addprop(self, key);
                prop = findprop(self, key);
                prop.SetMethod = makefunc(key, varargin{i+1});
            end
        end
        function out = classname(self)
            out = self.name_;
        end
    end
end

function h = makefunc(key, argtype)
    h = @newfunc;
    function newfunc(obj, val)
       obj.(key) = argtype(val); 
    end
end

通过这个类,我定义了 set 方法,以便将传递给属性的参数复制到正确的类型。要了解我的意思,请考虑以下用法:

>> p_int = myDynamicClass('Point', 'x', @int8, 'y', @int32);
>> p_int.x = 1000 

p_int = 

  myDynamicClass with properties:

    y: []
    x: 127

>> class(p_int.x)

ans =

int8

通过这个,我们强制x 属性为 8 位整数,只能保存 -128 到 127 之间的整数。还要注意每个属性的类如何为我们提供预期的类型。

【讨论】:

  • 这是正确的。请注意,在定义自定义 set/get 方法时,您可能会遇到无限递归的风险,因为 Matlab 解析器并不总是知道在您分配属性值时避免调用 set 方法。为了让您的生活更轻松(也出于调试目的),拥有一个隐藏属性来存储值可能会有所帮助,然后由 getter/setter 方法访问。
  • @Jonas 很棒的评论 - 你能详细说明一下隐藏属性以及如何实现它吗?
  • @Jonas,非常类似于 python __dict__ 变量。他们提到了无限递归的可能性here。是否有一些关于 matlab 的文档解释了如何做到这一点?
  • @memyself, jmlopez:我已经用一个例子更新了我的答案。
【解决方案3】:

到目前为止,我对 Matlab 的体验是,一旦我创建了一个类的实例,就不可能添加新方法。这非常麻烦,因为我的对象可能包含大量数据,每次我想添加新方法时都必须重新加载(因为我必须这样做clear classes)。

对于这个问题的当今读者来说,值得注意的是,这不再是正确的。 As of MATLAB R2014b MATLAB 会在您保存类定义时更新它们,以及现有类实例 automatically updates accordingly 的行为。在添加新方法的情况下,这并不复杂:新方法可以简单地用于调用类实例,即使它们是在将方法添加到类之前创建的。

为动态属性选择 set/get 方法给出的解决方案仍然适用。

在某些情况下,您可能希望动态地向实例添加方法,而该方法不构成属性 set/get 方法。我认为在这种情况下唯一的答案是将函数句柄作为值分配给动态属性。这不会创建真正的方法,但允许您以与方法调用相同的方式调用它:

addprop(obj, 'new_method');
obj.new_method = @(varargin) my_method(obj,varargin{:});

obj.new_method(args) 的调用因此被传递给my_method;但是这只适用于标量obj;实例数组将具有单独的 new_method 属性值,因此 obj.new_method 不再解析为可以在 obj 是数组时调用的单个函数句柄。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-18
    • 2021-12-06
    • 1970-01-01
    • 2011-06-07
    • 2010-09-22
    • 2013-01-16
    • 1970-01-01
    • 2020-02-12
    相关资源
    最近更新 更多