I have a class with a dependent property that takes a while to be evaluated. For this reason, I would like it not to be evaluated every time it is queried, but only when the object is instantiated and when certain properties are changed. I have found this interesting solution that is based on defining an extra private property that is set whenever necessary, with the dependent property taking the value of this extra private property.
Now the problem is: how can I make sure that this private property is set when the object is instantiated and it is automatically updated when certain properties of the object are changed?
The proposed solution is perfect, just make sure that in your class constructor you define default value for both the dependent property and its proxy are set to a default value that fits your needs:
methods
function this = MyClass(...)
% default starting values for your properties
this.MyProperty1 = 0;
this.MyProperty2 = -8;
this.MyProperty3 = 3;
% calculate the initial value of your dependent
% property based on the default values of the other
% properties (this is basically the computeModulus
% function of your example)
this.UpdateMyDependent();
end
end
The logics that make your dependent property update when other properties are modified is already included in the linked thread, implement them in your class.
function this = set.MyProperty1(this,value)
this.MyProperty1 = value;
this.UpdateMyDependent();
end
function this = set.MyProperty2(this,value)
this.MyProperty2 = value;
this.UpdateMyDependent();
end
function UpdateMyDependent(this)
this.MyDependentProxy = this.MyProperty1 * this.MyProperty2;
end
function value = get.MyDependent(this)
value = this.MyDependentProxy;
end
Related
Matlab specifies When [a] Set Method is Called
I ran into odd results, so I conducted a series of tests on the following class:
% #cScenInsts/cScenInsts.m
%-------------------------
classdef cScenInsts < matlab.mixin.Copyable
properties
TwinYrs = 5 % Default value
end % properties
methods
function o = cScenInsts( TwinYrs_in ) % Constructor
if exist('TwinYrs_in') && ~isempty( TwinYrs_in )
o.TwinYrs = TwinYrs_in ;
else
o.TwinYrs = o.TwinYrs ;
end % if
end % function cScenInsts()
function o = set.TwinYrs(o,TwinYrs_in)
o.TwinYrs = TwinYrs_in;
fprintf( '%s: Property TwinYrs = %g\n', mfilename, o.TwinYrs );
end % function set.TwinYrs
end % methods
end % classdef
The else block is superfluous in this minimum working example, but in real life, I want it to invoke the set method, which sets the values of other dependent parameters.
Using the class definition above, I ran into mixed results in terms of whether they seemed to follow the rules for when a set method is called.
UNEXPECTED behaviour in the following command: "Assigning a property to its default value that is specified in the class definition" shouldn't trigger set method, but it does.
>> o = cScenInsts;
cScenInsts: Property TwinYrs = 5
As well, "As an optimization, if MATLAB determines that a property value does not change as a result of an assignment referencing the property, MATLAB does not call the property set method". It seems that if the property is on the LHS and RHS of the assignment and the value doesn't change, the set method isn't called. This should apply above, as it is the else block that executes. The above result shows that it doesn't apply, which is the effect I seek. My anxt is that I don't know how much confidence I can have in the reliability of this behaviour in all circumstances when it seems to contradict the optimization provision.
EXPECTED behaviour in the following command:
>> o = cScenInsts(3);
cScenInsts: Property TwinYrs = 3
UNEXPECTED behaviour in the following command: "Assigning a property to its default value that is specified in the class definition" shouldn't trigger set method, but it does.
>> o = cScenInsts(5);
cScenInsts: Property TwinYrs = 5
EXPECTED behaviour in the following command:
>> o.TwinYrs = 3;
cScenInsts: Property TwinYrs = 3
EXPECTED behaviour in the following command, since the property's AbortSet attribute not set to true
>> o.TwinYrs = 3;
cScenInsts: Property TwinYrs = 3
UNEXPECTED behaviour in the following command: "Assigning a property to its default value that is specified in the class definition" shouldn't trigger set method, but it does.
>> o.TwinYrs = 5;
cScenInsts: Property TwinYrs = 5
AMBIGUOUS behaviour in the following command: On one hand, "[a]ssigning a property to its default value that is specified in the class definition" shouldn't trigger set method, but it does. On the other hand, it can also be considered expected behaviour, since the property's AbortSet attribute not set to true
>> o.TwinYrs = 5;
cScenInsts: Property TwinYrs = 5
Can anyone please explain the unexpected and/or ambiguous results?
I'll address each of the confusing bullet points you mention from this list of cases when the property set method IS NOT called:
Initializing default values in class definitions when loading the class
I believe this is specifically referring to creating a class object by loading it from a MAT-file (i.e. using the loadobj method). When loading the property values, they are simply copied without passing them to the set method.
Assigning a property to its default value that is specified in the class definition
I believe this is specifically referring to when MATLAB initially creates the object and sets its default values. It does this without passing the values to any set methods first. To see this, you can add this line to the very beginning of your class constructor:
disp(o);
When you run your code, you'll see that a default object with the default property values is already created for you upon entering the constructor, before you've even done anything with the input arguments. These properties are set without invoking any set methods (as the bullet point states). Any subsequent changes to the property, like when you set them in your if-else statement, DOES still invoke the set method, as expected.
You may notice then that your else statement is actually unnecessary in this case, since the defaults are already set. Removing the else statement will give you your expected behavior for:
o = cScenInsts;
As an additional aside, since you are subclassing the matlab.mixin.Copyable class (which has handle class behavior), you don't need to return an object from your set methods:
function set.TwinYrs(o, TwinYrs_in)
...
As an optimization, if MATLAB determines that a property value does not change as a result of an assignment referencing the property, MATLAB does not call the property set method.
This statement is a little unclear. As you already noted, leaving the AbortSet attribute unset (i.e. it is set to false by default) means that the set method will still be called even when setting a property to the same value. The statement above seems to suggest that under some circumstances, even when the AbortSet attribute is false, MATLAB may still decide to forego calling the set method. If so, it's not clear what those conditions are under which that optimization is used.
Removing the else statement as I suggest above, here are the expected results for some of your examples above:
>> o = cScenInsts; % Set method not invoked, default value used
>> o = cScenInsts(3);
cScenInsts: Property TwinYrs = 3 % Set method invoked, overriding default value
>> o = cScenInsts(5);
cScenInsts: Property TwinYrs = 5 % Set method invoked, even though same as default value
% (since AbortSet is false)
>> o.TwinYrs = 5;
cScenInsts: Property TwinYrs = 5 % Set method invoked, even though same as current value
% (since AbortSet is false)
This is the answer from TMW:
...the statement:
"As an optimization, if MATLAB determines that a property value
does not change as a result of an assignment referencing the
property, MATLAB does not call the property set method."
...is plainly incorrect. This optimization does not exist -- the
only time MATLAB forgoes calling the setter due to an assignment not
changing the value is when "AbortSet" is set to true.<...snip...>
As for the other unclear bullet points:
"Initializing default values in class definitions when loading the
class"
This refers to loading the class definition which happens when doing
“?” or when first constructing the class. This is the
step that parses and compiles the class definition and sets the
DefaultValue property of the meta.property object accessible from
the meta.class object returned from ?. This has nothing
to do with loading instances of a class from a MAT-File or loadobj.
"Assigning a property to its default value that is specified in
the class definition"
This bullet means that the set-function is not called when MATLAB
assigns a default value to a property when making a new instance of
a class. This refers to the assignment of the default value during
property initialization. As we suspected, it does not refer to any
other assignment to a value that happens to be equal to the default
value.
I will go ahead and submit the documentation update request...
Thank you all for having chimed in.
I come from a Java background. I am having issues with classes in Matlab particularly getters and setters. getting a message saying conflict between handle and value class I'm a little lost with what to do so any help for lack of a better word will be helpful.
classdef Person
properties(Access = private)
name;
age;
end
methods
% class constructor
function obj = Person(age,name)
obj.age = age;
obj.name = name;
end
%getters
function name = get.name(obj)
end
function age = get.age(obj)
end
%setters
function value = set.name(obj,name)
end
function value = set.age(obj,age)
end
end
end
Implementation
Since your class is currently a subclass of the default Value class, your setters need to return the modified object:
function obj = set.name(obj,name)
end
function obj = set.age(obj,age)
end
From the documention: "If you pass [a value class] to a function, the function must return the modified object." And in particular: "In value classes, methods ... that modify the object must return a modified object to copy over the existing object variable".
Handle classes (classdef Person < handle) do not need to return the modified object (like returning void):
function [] = set.name(obj,name)
end
function [] = set.age(obj,age)
end
Value vs. Handle
Going a bit deeper, the difference between a Value class and a Handle class lies mostly in assignment:
Assigning a Value class instance to a variable creates a copy of that class.
Assigning a Handle class instance to a variable create a reference (alias) to that instance.
The Mathworks has a good rundown on this topic.
To paraphrase their illustration, the behavior of a Value class is
% p is an instance of Polynomial
p = Polynomial();
% p2 is also an instance of Polynomial with p's state at assignment
p2 = p;
and of a Handle class is
% db is an instance of Database
db = Database();
% db2 is a reference to the db instance
db2 = db;
Quick'n Dirty from the Java perspective:
- "handle" classes are what your mind is set to. proper object instances with pointers to them. use them.
- "value" classes are always returning a full clone of whatever object (which has been modified by what you just did, e.g. setting a name).
the reason they have both in Matlab is that in Matlab you would expect the "value" behaviour natively. Imagine you have a matrix A = [1 2; 3 4], then assign that via B = A. if you now set B(1) = -1 you'd hope that A(1) is still 1, right? this is because matlab keeps track of "copies" and truly creates them as you modify different variables initially set to the same matrix. in OOP you'd have A(1)=-1 now as everythings an object reference.
furthermore, "native" matlab routines dont have a "this/self/me" variable that contains the instance reference to access from within functions. instead, the convention is that the class instance will be prepended to the function's argument list.
so for a function call myclass.mymethod(arg1,arg1), the declaration must be
function mymethod(this, arg1, arg2)
% Note that the name you choose for "this" is arbitrary!
end
mind you, this is the java-perspective (and also my favourite one), the above function call is equivalent to mymethod(myclass,arg1,arg1). this is more native to matlab-style, but somehow makes it harder to see you're calling an objects method.
now, regarding setters/getters: for handle classes, everything feels java-ish now:
classdef MyClass < handle
properties
MyProp;
end
methods
function set.MyProp(this, value) %Note: setMyProp is also valid!
... % do checks etc, trigger calls,
this.MyProp = value;
end
function value = get.MyProp(this)
... % notify, update, triggers etc
value = this.MyProp;
end
end
Of course it goes without saying that you dont need to define a getter if you just want to return the value, i.e. myclassinstance.MyProp will work without any just as well.
Finally, getters/setters for value classes are something that [never encountered me/i never needed] in my 7 years of matlab oop, so my advise would be to go with handle classes and enjoy happy matlab coding :-)
otherwise, the above explanation & official matlab docs is doing the job for value class getter/setters.
In Matlab, I would like a data structure that looks like so:
DataStruct
.model
.Q
.Qchol
.
.
.system
.
.
The structure may well be a class, although I don't really need all the other functionality that goes with oop.
But I require
If Q is assigned something, then automatically Qchol = cholcov(Q).
If Qchol is assigned something, then automatically Q = Qchol' * Qchol.
Meanwhile, both Q and Qchol are stored for fast read-access
And Q and Qchol are writable through simple assignment, e.g.: DS1.mod.Q = value
I know I can make model a class, and have set/get methods for Q and Qchol. However, this really seems like an overkill for just two matrices (plus maybe some more fields). Also Matlab warns me that I should not access other properties during in a set method.
So: What is the best way to have such data structures, preferably without warnings?
You basically want assignment (DS1.mod.Q = value) to have side-effects, which inevitably implies a setter, and hence a class. You should either drop this requirement, or write a class.
If you wish to avoid definition of properties in the class declaration, you could use Dynamic Properties, which allows you to add properties at runtime (although with some telltale syntax addprop()).
EDIT
Patric, the problem goes deeper then just M-lint. Consider the following class:
classdef cantInstantiateMe < handle
properties
x
minus_x
end
methods
function obj = cantInstantiateMe(x)
obj.x = x; % <-- this calls set.x(), which calls set.minus_x(), which calls set.x(), ...
obj.minus_x = -x;
end
function set.x(obj, value)
obj.x = value;
obj.minus_x = -value; % <-- this gives an M-Lint warning
end
function set.minus_x(obj, value)
obj.minus_x = value;
obj.x = -value;
end
end
end
This class cannot be instantiated, because each setter calls the other setter (this is not Matlab-specific). Trying to instantiate on my machine gives:
??? Maximum recursion limit of 500 reached. Use set(0,'RecursionLimit',N)
to change the limit. Be aware that exceeding your available stack space can
crash MATLAB and/or your computer.
At this point I think you have two options:
Make either Q or Qchol a dependent property. This will come at the cost of re-calculating the dependent property each time you read-access it.
Use some private shadow properties e.g. shadow_Q and shadow_Qchol which will be set when the setter for the public property is called, and returned when their getter is called. Similar to:
function set.x(obj, value)
obj.shadow_x = value;
obj.shadow_minus_x = -value;
end
function value = get.x(obj)
value = obj.shadow_x;
end
Note the I did not test this properly, so I don't know all implications in Matlab. In other languages I'm familiar with, this should work fine.
Regarding the warning - my approach is that it is safe to disable the warning, as long as you really know what you are doing.
As suggested by #bavaza, one way to implement this is to use a dependent property with corresponding shadow private properties.
Below is the code implementing the inner data structure (inspired by this post). You need to use composition to make an instance of this class a property of the outer object:
classdef Model < handle
properties (Dependent)
Q
Qchol
end
properties (Access = private)
Q_
Qchol_
end
methods
function obj = Model()
end
function val = get.Q(obj)
val = obj.Q_;
end
function val = get.Qchol(obj)
val = obj.Qchol_;
end
function set.Q(obj, val)
obj.Q_ = val;
obj.Qchol_ = cholcov(val);
end
function set.Qchol(obj, val)
obj.Qchol_ = val;
obj.Q_ = val'*val;
end
end
end
Setting one value using the exposed dependent properties affects both underlying variables:
>> m = Model
m =
Model with properties:
Q: []
Qchol: []
>> m.Qchol = rand(3)
m =
Model with properties:
Q: [3x3 double]
Qchol: [3x3 double]
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.
Writing a subclass of dynamicprops allows to me to add properties dynamically to an object:
addprop(obj, 'new_prop')
This is great, but I would also love to create set / get functions for these properties on the fly. Or analysis functions that work on these dynamic properties.
My experience with Matlab has been so far, that once I create an instance of a class, adding new methods is not possible. That is very cumbersome, because my object may contain a lot of data, which I'll have to re-load every time that I want to add a new method (because I have to do clear classes).
So is there a way to add methods on the fly?
You cannot add methods like you add dynamic properties. However, there are two ways for implementing new methods during development that won't require you to re-load the data every time.
(1) I write standard methods as separate functions, and call them as myMethod(obj) during development. Once I'm sure they're stable, I add their signature into the class definition file - this requires a clear classes, of course, but it is a much delayed one, and from time to time you may have to shut down Matlab, anyway.
(2) With set/get methods, things are a little trickier. If you are using dynamicprops to add new properties, you can also specify their set/get methods, however (most likely, these methods/functions will want to receive the name of the property so that they know what to refer to):
addprop(obj,'new_prop');
prop = findprop(obj,'new_prop');
prop.SetMethod = #(obj,val)yourCustomSetMethod(obj,val,'new_prop')
EDIT
(2.1) Here's an example of how to set up a hidden property to store and retrieve results (based on jmlopez' answer). Obviously this can be improved a lot if you have a better idea what you're actually designing
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
I'm adding this answer because I think that this is not intuitive. At least not to myself at this moment. After finding this question I thought I had what I needed to be able to define the set/get methods for my dynamic class. All I wanted to achieve with this was something similar to what python does with its __setattr__ method. In any case, here is a continuation of the class made by #jonas a while ago with a few modifications to add the our custom set method.
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
With this class I'm defining the set method so that the parameter passed to the attribute is copied to the right type. To see what I mean consider the following usage:
>> 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
With this we have forced the x attribute to be an integer of 8 bits which can only hold integers from -128 to 127. Also notice how the class of each attribute gives us the intended type.
My experience with Matlab has been so far, that once I create an instance of a class, adding new methods is not possible. That is very cumbersome, because my object may contain a lot of data, which I'll have to re-load everytime that I want to add a new method (because I have to do clear classes).
It's worth noting for present-day readers of this question that this is no longer true. As of MATLAB R2014b MATLAB updates class definitions at the moment you save them, and the behaviour of existing class instances automatically updates accordingly. In the case of adding new methods, this is uncomplicated: the new method simply becomes available to call on class instances even if they were created before the method was added to the class.
The solutions given for choosing set/get methods for dynamic properties still apply.
There are still cases where you might want to add methods to an instance dynamically and the method doesn't constitute a property set/get method. I think the only answer in this case is to assign a function handle as the value to a dynamic property. This doesn't create a bona fide method, but will allow you to call it in the same way you would a method call:
addprop(obj, 'new_method');
obj.new_method = #(varargin) my_method(obj,varargin{:});
Calls to obj.new_method(args) are thus passed to my_method; however this only works with a scalar obj; an array of instances will have separate values for the new_method property so obj.new_method no longer resolves to a single function handle that can be called if obj is an array.