Thank you everyone. The specific requirements for that class: *(1)* Provide the values of the "input" (via constructors). *I think everyone agrees with the way it is implemented in the example. *
*(2)* Provide other products such as *b* that can be of any type (array, object, etc.). It is like an "output" if you will. *I think everyone suggests that it should be designed such that people should not be able (inadvertently) to change the value from outside the class. I agree, as a matter of fact this is my intent as well.* *(3)* About the function calc(), my intent is NOT to expose it outside the class: it is "private". *And I see the way to do that from your suggestions. * Based on all your valuable suggestions, I should be able to accomplish the 3 goals. By the way, I am writing this code for finite element analysis (FEA): number crunching. Even though the final goal is to get the correct results, I still want to write it following the correct python "grammar" and style. Best regards, -Irfan On Mon, Jul 25, 2022 at 3:54 AM Peter Otten <__pete...@web.de> wrote: > On 25/07/2022 02:47, Khairil Sitanggang wrote: > > Regarding your comment : " > > *However, usually object creation and initialization iscombined by > allowing > > arguments to the initializer:*" , so which one of the two classes Node1, > > Node2 below is more common in practice? Option 2, I guess. > > Thanks, > > > > > > # option 1: > > class Node1: > > def __init__(self, a): > > self.a = a > > self.b = self.calculation() > > > > def calculation(self): > > r = self.a + 10 > > return r > > > > # option 2: > > class Node2: > > def __init__(self, a, b): > > self.a = a > > self.b = b > > > > self.b = self.calculation() > > > > def calculation(self): > > r = self.a + 10 > > return r > > > > nd1 = Node1(10) > > nd2 = Node2(10, 0) # 0 is dummy, will be overwritten by the call to > > calculation() > > An attribute that can be calculated from other attributes should never > be modified through other means. Some day you may want b to become > something else, write, for example, > > node = Node2(10, "twenty") > > and because by then you have forgotten about the calculation() call end > up with a buggy script. But manually invoking the calculation() method > is also bug prone. You have to remember to repeat it every time you > change a: > > node = Node1(10) > assert node.b == 20 # OK > > node.a = 20 > assert node.b == 30 # fails, a and b are out of sync. > > The solution Python has to offer is called "property". Properties in > their simplest form are calculated read-only attributes, i. e. when you > write > > print(node.b) > > under the hood node.a + 10 is calculated. Here's how to change Node1 to > turn b into such a property: > > class Node3a: > def __init__(self, a): > self.a = a > def calculation(self): > return self.a + 10 > b = property(calculation) > > node = Node3a(42) > print(node.b) # 52 > > node.a = 1 > print(node.b) # 11 > > Often you are not interested in exposing both the calculation() method > and the property. For cases when you only want to access the property > Python provides a way to define the property with a "decorator": > > class Node3b: > def __init__(self, a): > self.a = a > @property > def b(self): > return self.a + 10 > > When you compare the two classes you can see that I > > (1) renamed calculation() to b() and > > (2) replaced > > def b(self): ... > b = property(b) > > with > > @property > def b(self): ... > > thus avoiding the repetitons of the name. > > Are there any disadvantages to properties? > > What I presented as an advantage, that the value of the attribute is > recalculated every time the attribute is accessed, may sometimes become > a disadvantage, e. g. when it takes a very long time to calculate. In > most cases that should not be a problem, though. > -- https://mail.python.org/mailman/listinfo/python-list