How to Load a class from file avoiding the set methods with empty arguments?
21 views (last 30 days)
Show older comments
In the moment when I define the following test class, its constructor is called and with it the two set methods. However, in the moment when I save and then load the same class it seems that something more complicated is happening. By running the code below:
Creating object
Constructor
ans =
0
ans =
0
Loading object from file
ans =
1
ans =
1
ans =
0
ans =
0
Even if the constructor is not called (obviously, unless we use (ConstructOnLoad = true), two calls to the set methods are done to set the two properties to [].
Now, this is (from my perspective) a strange (and undesired) behavior because in set methods I would like to check that the argument passed is valid, but i don't want an empty array to be a valid argument. Ideally instead of having isempty(PAR) i want to have something like assert(PAR>0), but when doing so in the first call PAR will be [] and thus everything will crash.
Do you have any suggestion how to workaround this issue or in general if there is a nicer way to declare classes that will be saved and loaded from file?
I also tried using saveobj and loadobj, but it doesn't help.
Thank you -Roberto
-------------
classdef test < handle
properties
a = [];
b = [];
end
methods
function [OBJ] = test(arg1,arg2)
fprintf('Constructor\n')
switch nargin
case {0}
case {1}
if is(arg1,'test')
OBJ = arg1;
else
error('not a test class')
end
otherwise
OBJ.a = arg1;
OBJ.b = arg2;
end
end
function [] = set.a(OBJ,PAR)
isempty(PAR)
OBJ.a = PAR;
end
function [] = set.b(OBJ,PAR)
isempty(PAR)
OBJ.b = PAR;
end
end
end
-------------
fprintf('Creating object\n')
A = test(1,2);
save('file1')
clearvars
fprintf('Loading object from file\n')
load('file1')
-------------
edit: it seems that the problem is indeed using
properties
a = [];
b = [];
end
to initialize the variables, for some reason the initialization at run-time don't make use of the set functions, but when loading, it does. I therefore changed the code to below and obtained an inverted behavior:
Creating object
Constructor
ans =
1
ans =
1
ans =
0
ans =
0
Loading object from file
ans =
0
ans =
0
Hence, by initializing the properties with valid values I can hopefully get rid of this problem at loading time. Thanks for the help! :) ----------------
classdef test < handle
properties
a
b
end
methods
function [OBJ] = test(arg1,arg2)
fprintf('Constructor\n')
switch nargin
case {0}
case {1}
if is(arg1,'test')
OBJ = arg1;
else
error('not a test class')
end
otherwise
OBJ.default_values()
OBJ.a = arg1;
OBJ.b = arg2;
end
end
function default_values(OBJ)
OBJ.a = [];
OBJ.b = [];
end
function [] = set.a(OBJ,PAR)
isempty(PAR)
OBJ.a = PAR;
end
function [] = set.b(OBJ,PAR)
isempty(PAR)
OBJ.b = PAR;
end
end
end
3 Comments
Matt J
on 1 Jul 2014
Note that your code has the line
>> clear vars
This will not clear A from memory. For that, the statement needs to be
>> clearvars %all one word
Accepted Answer
Oleg Komarov
on 1 Jul 2014
"When loading an object, MATLAB® creates a new object and assigns the stored property values. For properties that had default values at the time you saved the object, MATLAB loads the saved default values, even if the class definition defines new default values for those properties."
So, simply remove the = [] from:
a = []
b = []
4 Comments
Oleg Komarov
on 2 Jul 2014
You're right Matt, I misinterpreted the link. Also, I can't reproduce Roberto's problem on R2014a, which reminds me of a bug in oop with saving properties from a previous version of matlab.
More Answers (3)
Matt J
on 2 Jul 2014
Edited: Matt J
on 6 Jul 2014
After some study, I think Oleg's answer is correct after all, but I still think the cited link makes this a bit murky. In particular, it doesn't describe how property set() methods behave during the load process. So, I'm providing an elaborated answer with illustrations using the classdef below,
classdef myclass
properties
a=1;
end
methods
function obj=myclass(val)
obj.a=val;
end
function obj=set.a(obj,val)
testing = val,
obj.a=val;
end
end
end
Essentially, any explicit property defaults are saved to a .mat file along with the instance property values. When loaded back in, a skeleton object appears to be created first using the stored default property values. The stored defaults are then over-written by the stored instance values. In each of these steps, both the stored defaults and the stored instance values are passed through the current property set methods to ensure consistency with the current class definition. Hence,
>> clear classes; A=myclass(2);save tst A;
testing =
2
>> load tst
testing =
1
testing =
2
To verify this, I change the classdef below to have both a different default property value a=3 and also a different set.a() method.
classdef myclass
properties
a=3;
end
methods
function obj=myclass(val)
obj.a=val;
end
function obj=set.a(obj,val)
testing = 10*val,
obj.a=val;
end
end
end
Note after "clear classes" and reloading that the new default value a=3 has no influence on the load behavior, but the new set.a() method does:
>> clear classes; load tst
testing =
10
testing =
20
The slightly confusing thing (for me) is that when a default property value is not supplied explicitly in the classdef,
classdef myclass
properties
a;
end
methods
function obj=myclass
constructorDefault = obj.a
end
function obj=set.a(obj,val)
testing = 10*val,
obj.a=val;
end
end
end
a default of [] is supplied by MATLAB during object construction, as clear from,
>> clear classes; obj=myclass; obj.a=1; save tst obj
constructorDefault =
[]
testing =
10
but a default of [] is not stored to the .mat file, as clear from
>> load tst
testing =
10
Anyway, I guess this all makes sense since the load process isn't supposed to rely on the constructor...
1 Comment
Oleg Komarov
on 6 Jul 2014
Very nice and clear. I think TMW should incorporate your examples (especially the order) in the documentation.
James Tursa
on 1 Jul 2014
When you load a class object from a mat file, MATLAB must ensure that the object in the file matches the current definition of the class (i.e., properties match up). To do so it will call your class with no inputs, the intent being to get a skeleton of your class object back that it can use to compare properties with the mat file object. You need to be able to handle this behavior in your class constructor code. I don't know if this behavior can be changed or modified (I don't have the latest releases of MATLAB to test with).
2 Comments
Matt J
on 1 Jul 2014
Edited: Matt J
on 1 Jul 2014
You need to be able to handle this behavior in your class constructor code.
I don't think so. That's what the ConstructOnLoad attribute is supposed to dictate.
Clearly, MATLAB does create a skeleton of the class using the initial property values in some way, but does not do so by calling the class constructor.
Also, it is not clear why this skeleton-creator calls the property set methods. Property default values are supposed to be exempt from the set method, as mentioned here.
James Tursa
on 1 Jul 2014
Hmmm. I may be confusing the old style class with the classdef class, or maybe I just need a later version of MATLAB. (This used to be the behavior). Thanks for the correction.
Xin Niu
on 13 Jul 2015
Edited: Matt J
on 13 Jul 2015
Hi, I have a more serious problem: when I load a object, a property is change to the default value even when I assigned a value to it before saving it. My code is like this:
classdef stim_info
properties
single_duration = [];% in seconds
onset = [];
sample_rate = 10^4; % in Hz
stim_thresh_ratio = 2/3; % used to determine onset sample.
recording = [];
stim_status = [];
default_spike_thresh = 1; % parameters for function findspikes
refractory_period = 4; % in milliseconds
end
methods
% constructing methods:--------------------------------------------
function SI = stim_info(varargin)
if mod(nargin,2)
error('only pair wise input of name and value of parameters allowed')
end
SI.para_name=varargin(1:2:end);
SI.para_value = cell2mat(varargin(2:2:end));
end
% set methods:-----------------------------------------------------
function self = set.onset(self, onset)
if self.check_onset(onset);
self.onset = onset(:);
else
warning('onset not successfully defined')
end
end
end
when I assign a value to onset and saved it, the value was empty when I loaded it. This problem happens when I did not set default value to that property.
2 Comments
Matt J
on 13 Jul 2015
I think we need to know how to run the code, so as to reproduce what you're seeing.
Xin Niu
on 14 Jul 2015
Edited: Xin Niu
on 14 Jul 2015
I write a brief code to reproduce the error:
if true
% code
end
classdef test
properties
pro1 = [];
pro2 = [];
end
methods
function obj = test(p1,p2)
if nargin>1; obj.pro1 = p1; end
if nargin>2; obj.pro2 = p2; end
end
function self = set.pro1(self, p1)
if self.check_prop(p1)
self.pro1 = p1;
end
end
end
methods (Access=private)
function isok = check_prop(self,p1)
isok = length(p1)==length(self.pro2);
end
end
end
and the problem occurs when I run:
if true
% code
end
t = test
t.pro2 = 4
t.pro1 = 3;
save t t
clear
load t
t.pro1
t.pro2
See Also
Categories
Find more on Methods in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!