I am creating a class in MATLAB and while I have little experience with objects, I am almost positive I should be able to set a class property using a class method. Is this possible in MATLAB?
classdef foo
properties
changeMe
end
methods
function go()
(THIS OBJECT).changeMe = 1;
end
end
end
f = foo;
f.go;
t.changeMe;
ans = 1
Yes, this is possible. Note that if you create a value object, the method has to return the object in order to change a property (since value objects are passed by value). If you create a handle object (classdef foo<handle), the object is passed by reference.
classdef foo
properties
changeMe = 0;
end
methods
function self = go(self)
self.changeMe = 1;
end
end
end
As mentioned above, the call of a setting method on a value object returns the changed object. If you want to change an object, you have to copy the output back to the object.
f = foo;
f.changeMe
ans =
0
f = f.go;
f.changeMe
ans =
1
Related
I'm having a weird problem using object oriented matlab, and I'm not sure if I'm completely missing something, or I've hit some unusual behaviour.
I have a superclass defined as follows:
classdef (Abstract) AbstractSimulationData < matlab.mixin.Heterogeneous
properties (Abstract)
name
data
end
properties
timeStamp = -1
end
methods (Abstract)
CalculateData(obj, t)
end
methods
function data = GetData(obj, t)
if obj.timeStamp == t.t
data = obj.data;
else
obj.timeStamp = t.t;
obj.CalculateData(t);
data = obj.data;
end
end
end
end
When the concrete class implements the method CalculateData I intend it to set the superclass property data.
Two concrete classes from this are:
classdef A < AbstractSimulationData
properties
name = 'A'
data = []
end
methods
function obj = A
% No special initialisation
end
function CalculateData(obj, t)
test = t.B.GetData(t)
obj.data = rand(2,10);
end
end
end
And:
classdef B < AbstractSimulationData
properties
name = 'B'
data = containers.Map
end
methods
function obj = B
% No special initialisation
end
function CalculateData(obj, t)
if isempty(obj.data)
obj.data('left') = 1;
obj.data('right') = 2;
end
% Other operations
end
end
end
Within the A.CalculateData method I call the GetData method for B. This in turn calls the B.CalculateData method.
Now, as it filters back up, the data property gets set correctly so that B.GetData returns the value that was set in B.CalculateData.
But, when I go the next step up to A.GetData it returns [], the initialised value. Going through debug mode, the calculations definitely work in A.CalculateData and it seems to set data correctly, but when it jumps back up to the superclass obj.data is the default value from A.
A minimum working example to get this behaviour:
t.B = B();
t.t = 1;
a = A;
a.GetData(t);
>> test =
Map with properties:
Count: 2
KeyType: char
ValueType: any
>> ans =
[]
This is all part of a pretty big project, so it's hard to give the full context, but if it's possible from this, can someone explain why setting superclass data works for B but not A?
If it makes a difference B is returning a container.Map and A is returning 2xn matrix.
As far as I can tell from the documentation, it should work.
I'd like to save a particular class property to disk, while ignoring the rest of the class properties. I think MATLAB allows the saveobj method to be overridden for this purpose. However, this saves the class object with only that property attached. I want to save the property itself, without any of the class information.
I might think that a suitable method would look like:
classdef myClass
properties
myProp
end
methods
def b = saveobj(a)
b = a.myProp;
end
def Save(a,fname)
save(fname,'a.myProp');
end
end
end
But neither of these have the desired effect. Can anyone help me, please?
You can overload the save function itself without having to go through saveobj:
classdef myClass
properties
myProp
end
methods
function [] = save(a,fname,varargin)
myProp = a.myProp; %#ok<PROP,NASGU>
save(fname,'myProp',varargin{:});
end
end
end
Then on the command window:
>> foo = myClass();
>> foo.myProp = 4;
>> foo.save('var.txt');
>> bar = load('var.txt','-mat');
>> bar.myProp
ans =
4
The first method (the one involving saveobj) is actually correct. For the purposes of discussion let us consider this simple class:
classdef testclass
properties
x
end
methods
function this = testclass(x)
this.x = x ;
end
function a = saveobj(this)
a = this.x ;
end
end
end
When you ask MATLAB to save an instance of your class, it will use the saveobj method when you call save if one exists. The output of this method can be an object, a struct, an array, whatever. You want to test that this has occurred, and you do something natural like this:
>> obj = testclass('hi')
obj =
testclass with properties:
x: 'hi'
>> save tmp.mat obj
>> clear all
>> load tmp.mat
>> obj
obj =
testclass with properties:
x: []
>>
And this is where I suspect your confusion arises. You expect obj to be a char but instead it's an empty object of class testclass. (You can verify that it is just an instance of the object based on the saved definition of the class, and that it is not created by calling the empty constructor.)
This may seem rather confusing until you understand how loadobj works. In order for MATLAB to know which static method to call on load, it saves the class definition in conjunction with whatever output you are providing from your custom saveobj method. When you call load it then loads the class definition and calls the loadobj static method if one exists. We can test this by modifying the class definition above:
classdef testclass
properties
x
end
methods
function this = testclass(x)
this.x = x ;
end
function a = saveobj(this)
a = this.x ;
end
end
methods( Static )
function this = loadobj(a)
this = testclass(a) ;
end
end
end
If you set a breakpoint in the loadobj method you can verify that the type of a is indeed char as you expect.
i want to build a class like that:
classdef myclass < handle
properties
v1 = struct('value', 100)
v2 = struct('value', 200);
end
methods
function plusone(obj, vars) % SHOULD BE MODIFIED SOMEHOW??
vars.value = vars.value + 1;
end
end
end
And my question is, how should i write the method plusone so in Command Window i can choose which property i want to modify, i.e. i choose to modify property v2:
a = myclass();
a.plusone(a.v2);
that update the field value of variable v2 of object a ? Or is there a problem with my thinking method?
Yes, you can create a method like that, though you need to pass the reference to the field differently:
methods
function plusone(obj, propName)
obj.(propName).value = obj.(propName).value + 1;
end
end
To call the method, pass the reference as string:
a = myclass();
a.plusone('v2')
In Matlab, I can define a class as such:
classdef klass < handle
properties(Dependent)
prop
end
end
Matlab is perfectly happy instantiating an object of this class, even without defining a getter for prop. It only fails when I try to access it (understandably). I'd like to set the GetMethod dynamically based upon the property's name.
Unfortunately, even when the property is Dependent, the meta.property field for GetMethod is still read-only. And while inheriting from dynamicprops could allow adding a property and programmatically setting its GetMethod in every instance, I don't believe it could be used to change an existing property. I may have to go this route, but as prop must exist for every object I'd prefer to simply set the getter on a class-by-class basis. Is such a thing possible?
An alternative solution could be through some sort of catch-all method. In other languages, this could be accomplished through a Ruby-like method_missing or a PHP-like __get(). But as far as I know there's no (documented or otherwise) analog in Matlab.
(My use case: this class gets inherited by many user-defined subclasses, and all their dependent properties are accessed in a similar way, only changing based on the property name. Instead of asking users to write get.* methods wrapping a call to the common code for each and every one of their dependent properties, I'd like to set them all dynamically with anonymous function pointers containing the necessary metadata).
Here is my proposal: create a method in the superclass called add_dyn_prop. This method is to be called in the subclasses instead of creating a dependent property the usual way.
The idea is that the superclass inherit from dynamicprops and use addprop to add a new property, and set its accessor methods manually based on its name.
classdef klass < dynamicprops
methods (Access = protected)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
% NOTE: this has to be a simple function_handle (#fun), not
% an anonymous function (#()..) to avoid infinite recursion
p.GetMethod = #get_method;
p.SetMethod = #set_method;
% nested getter/setter functions with closure
function set_method(obj, val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
now in the subclass, instead of defining a dependent property the usual way, we use this new inherited function in the constructor to define a dynamic property:
classdef subklass < klass
%properties (Dependent, SetAccess = private)
% name
%end
%methods
% function val = get.name(obj)
% val = 'Amro';
% end
%end
methods
function obj = subklass()
% call superclass constructor
obj = obj#klass();
% define new properties
add_dyn_prop(obj, 'name', 'Amro');
add_dyn_prop(obj, 'age', [], false)
end
end
end
The output:
>> o = subklass
o =
subklass with properties:
age: []
name: 'Amro'
>> o.age = 10
o =
subklass with properties:
age: 10
name: 'Amro'
>> o.name = 'xxx'
You cannot set the read-only property 'name' of subklass.
Of course now you can customize the getter method based on the property name as you initially intended.
EDIT:
Based on the comments, please find below a slight variation of the same technique discussed above.
The idea is to require the subclass to create a property (defined as abstract in the superclass) containing the names of the desired dynamic properties to be created. The constructor of the superclass would then create the specified dynamic properties, setting their accessor methods to generic functions (which could customize their behavior based on the property name as you requested). I am reusing the same add_dyn_prop function I mentioned before.
In the subclass, we are simply required to implement the inherited abstract dynamic_props property, initialized with a list of names (or {} if you dont want to create any dynamic property). For example we write:
classdef subklass < klass
properties (Access = protected)
dynamic_props = {'name', 'age'}
end
methods
function obj = subklass()
obj = obj#klass();
end
end
end
The superclass is similar to what we had before before, only now is it its responsibility to call the add_dyn_prop in its constructor for each of the property names:
classdef klass < dynamicprops % ConstructOnLoad
properties (Abstract, Access = protected)
dynamic_props
end
methods
function obj = klass()
assert(iscellstr(obj.dynamic_props), ...
'"dynamic_props" must be a cell array of strings.');
for i=1:numel(obj.dynamic_props)
obj.add_dyn_prop(obj.dynamic_props{i}, [], false);
end
end
end
methods (Access = private)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
%p.Transient = true;
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
p.GetMethod = #get_method;
p.SetMethod = #set_method;
% nested getter/setter functions with closure
function set_method(obj,val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
Note: I did not use ConstructOnLoad class attribute or Transient property attribute, as I am still not sure how they would affect loading the object from a saved MAT-file in regards to dynamic properties.
>> o = subklass
o =
subklass with properties:
age: []
name: []
>> o.name = 'Amro'; o.age = 99
o =
subklass with properties:
age: 99
name: 'Amro'
Check if this is what you want. The problem is that the user will need to get the properties using (), which may be quite boring, but anyway, I think this way you can change the variables. You can't change them directly on the class, but you can change the objects property values on demand. It doesn't need to change the values on the constructor, you can do that using another function that will be inherited by the classes.
klass1.m
classdef(InferiorClasses = {?klass2}) klass < handle
methods
function self = klass
selfMeta = metaclass(self);
names = {selfMeta.PropertyList.Name};
for name = names
switch name{1}
case 'prop_child_1'
self.(name{1}) = #newGetChild1PropFcn;
case 'prop_child_2'
self.(name{1}) = #newGetChild2PropFcn;
end
end
end
end
methods(Static)
function out = prop
out = #defaultGetPropFcn;
end
end
end
function out = defaultGetPropFcn
out = 'defaultGetPropFcn';
end
function out = newGetChild1PropFcn
out = 'newGetChild1PropFcn';
end
function out = newGetChild2PropFcn
out = 'newGetChild2PropFcn';
end
klass2.m
classdef klass2 < klass
properties
prop_child_1 = #defaultGetChildPropFcn1
prop_child_2 = #defaultGetChildPropFcn2
end
methods
function self = klass2
self = self#klass;
end
end
end
function out = defaultGetChildPropFcn1
out = 'defaultGetChildPropFcn1';
end
function out = defaultGetChildPropFcn2
out = 'defaultGetChildPropFcn2';
end
Output:
a = klass2
b=a.prop_child_1()
b =
newGetChild1PropFcn
I want a read-only field that I can access as fv=object.field, but where the value that is
returned is computed from other fields of the object (i.e. the return value satisfies fv==f(object.field2)).
The desired functionality is the same as for the property function/decorator in Python.
I recall seeing a reference that this is possible by setting the parameters of the properties block, but the Matlab OOP documentation is so scattered that I can't find it again.
This is called a "dependent" property. A quick example of a class using a derived property is below:
classdef dependent_properties_example < handle %Note: Deriving from handle is not required for this example. It's just how I always use classes.
properties (Dependent = true, SetAccess = private)
derivedProp
end
properties (SetAccess = public, GetAccess = public)
normalProp1 = 0;
normalProp2 = 0;
end
methods
function out = get.derivedProp(self)
out = self.normalProp1 + self.normalProp2;
end
end
end
With this class defined, we can now run:
>> x = dependent_properties_example;
>> x.normalProp1 = 3;
>> x.normalProp2 = 10;
>> x
x =
dependent_properties_example handle
Properties:
derivedProp: 13
normalProp1: 3
normalProp2: 10
You can use the property access methods: http://www.mathworks.co.uk/help/matlab/matlab_oop/property-access-methods.html
To define get/set functions - the get function should allow you to return values computed from other members. The section "When to Use Set Methods with Dependent Properties" in the link above gives an example for this.