• Object in List : how?

    From Khairil Sitanggang@21:1/5 to All on Fri Jul 22 23:28:11 2022
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to check
    if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20
    NODELIST.append(NODE)

    NODE.NO = 30
    NODELIST.append(NODE)


    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dennis Lee Bieber@21:1/5 to All on Sat Jul 23 16:55:28 2022
    On Fri, 22 Jul 2022 23:28:11 -0500, Khairil Sitanggang <ksit70@gmail.com> declaimed the following:


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []


    Comment...

    The convention is that ALL CAPS is used to indicate something that is to be treated as a CONSTANT. Classes get capitalized initial letters. Names
    of variable data is traditionally all lower case, lower case with _ between "words" (eg: lower_case), or camel case (eg: camelCase).

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20
    NODELIST.append(NODE)

    NODE.NO = 30
    NODELIST.append(NODE)


    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    The [:], in this statement, just says "make a copy of nodelist". The /list/ does not have an attribute named "NO". You have to ask for each
    element IN the list.

    One convoluted way (I've not tested it) is:

    if len([node for node in nodelist if node.no == no1]):
    print("Found at least one occurence")

    This is a list comprehension; it loops over each element in nodelist, making a new list if the element attribute matches the criteria. Python
    treats 0 as "false" and if no element matched, the list created is empty,
    so len() is 0. Anything else implies a match was found.


    --
    Wulfraed Dennis Lee Bieber AF6VN
    wlfraed@ix.netcom.com http://wlfraed.microdiversity.freeddns.org/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Khairil Sitanggang on Sat Jul 23 21:54:45 2022
    Khairil Sitanggang <ksit70@gmail.com> writes: I was trying to check >if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    To check whether an object is not a member of an object,
    you can use "not in".

    For example, the expression

    1 not in[ 1, 2, 3 ]

    evaluates to "False".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From MRAB@21:1/5 to Khairil Sitanggang on Sat Jul 23 22:57:34 2022
    On 23/07/2022 05:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to check if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20
    NODELIST.append(NODE)

    NODE.NO = 30
    NODELIST.append(NODE)


    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    No, you can't do it that way. You have to iterate through the list and
    check each member individually:

    if any(NO1 == N.NO for N in NODELIST):

    And another thing: you've created only 1 node, and you're changing it
    each time before adding it to the list, so the list ends up with 3
    references to the _same_ object.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Dennis Lee Bieber on Sat Jul 23 22:55:47 2022
    Dennis Lee Bieber <wlfraed@ix.netcom.com> writes:
    if len([node for node in nodelist if node.no == no1]):
    print("Found at least one occurence")

    I'm not sure you need the "len"; an empty list already is falsy.

    print( bool( [] ))
    |False
    print( bool( [ 1, 2, 3 ] ))
    |True

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Khairil Sitanggang@21:1/5 to Khairil Sitanggang on Sat Jul 23 22:50:13 2022
    Thank you.
    I did it as you said. Yes, I forgot to create a new object Node() for each of the 3 instances.

    The reason I wanted to do as I asked was that I expected Python provides that convenient feature (since it is a high level language). I use Matlab a lot and it is so rich of features that allow us to process object array in many different ways.

    Anyway, as I said, I just started playing with Python, and I like it so much. Thanks to Visual Studio Code making coding very enjoyable.



    Get Outlook for iOS<https://aka.ms/o0ukef>
    ________________________________
    From: Python-list <python-list-bounces+ksit70=gmail.com@python.org> on behalf of MRAB <python@mrabarnett.plus.com>
    Sent: Saturday, July 23, 2022 4:57:34 PM
    To: python-list@python.org <python-list@python.org>
    Subject: Re: Object in List : how?

    On 23/07/2022 05:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to check if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20
    NODELIST.append(NODE)

    NODE.NO = 30
    NODELIST.append(NODE)


    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    No, you can't do it that way. You have to iterate through the list and
    check each member individually:

    if any(NO1 == N.NO for N in NODELIST):

    And another thing: you've created only 1 node, and you're changing it
    each time before adding it to the list, so the list ends up with 3
    references to the _same_ object.
    --
    https://mail.python.org/mailman/listinfo/python-list

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to MRAB on Sun Jul 24 11:52:45 2022
    On 24/07/2022 09.57, MRAB wrote:
    On 23/07/2022 05:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code.  I was trying to
    check
    if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
         def __init__(self):
             self.NO = 0
             self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20
    NODELIST.append(NODE)

    NODE.NO = 30
    NODELIST.append(NODE)


    NO1 = 20
    if NO1 not in NODELIST[:].NO  ???

    No, you can't do it that way. You have to iterate through the list and
    check each member individually:

        if any(NO1 == N.NO for N in NODELIST):

    And another thing: you've created only 1 node, and you're changing it
    each time before adding it to the list, so the list ends up with 3
    references to the _same_ object.

    +1


    Imagine the object (Node) was instead a person, and a bunch of us-people
    get together in a room. You could shout (above the polite conversation)
    "is Fred here?" and Fred will reply - or if Fred is elsewhere, silence
    will reign. That works - but only because everyone knows their own name.

    Now imagine something like (my) grade school, where the teacher takes a roll/register to note who is present for (or absent from) class. In this
    case another staff-member could enter the room and instead of shouting
    (which would be rude), can ask the teacher "is Fred here?". The teacher
    is able to answer from the roll.

    The former case is (sort of) the solution proposed above - in looking
    for Fred, you walk through the room, asking each person in-turn "are you Fred?".

    The latter is the case for Node and what you were hoping to implement.

    Thus, an alternate approach is to keep a register of nodes. Note that
    this is more than a list, because each element of the list (each node)
    is also identified (on the list) by its name. So, two pieces of
    (related) data: the id of the node, and the node itself - the name of
    the person and the person him-/her-self.

    Assuming you only have one ID that will be used to access a node,
    Python's built-in dict[ionary] data-structure will provide the advantage
    of direct-access (instead of going through (on average) half the nodes,
    asking each one in-turn, are you ...

    So:

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    becomes:

    graph = dict{} # could be written as: graph = {}
    node10 = Node( 10 )
    graph[ node.id ] = node10

    or even:

    graph[ 20 ] = Node( 20 )

    if you don't need lots of nodes 'hanging around' outside of the 'list' (actually a dict) representing the graph.


    Then:

    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    becomes a single line:

    the_node_required = graph[ 20 ]

    where "20" is the search-criteria.

    Does that make sense?


    If it does, and when you realise that you'd like to do more with the
    graph than retrieve single nodes (and more-importantly, should you want
    (or dare) to delve further into the depths of Object-Oriented
    Programming) you could declare a second class (graph) containing a
    number of "methods". If one of the methods implemented is __contains__()
    then you could indeed ask:

    if 20 in graph:

    ie "is Fred in-class today?"
    but just as we would ask "is Fred here?" rather than "is someone
    identified by the name 'Fred' here?", it would seem better form to write:

    if node( 20 ) in graph:

    ie is there a node with the id of 20, somewhere within the graph?


    Such might involve sub-classing OrderedDict, UserDict, or even UserList,
    from the Collections library in the (provided/"batteries-included")
    Python Standard Library: https://docs.python.org/3/library/collections.html

    If needed, the extra methods you choose to implement might include such functionality as connecting nodes 10 and 20 with a path/edge, being able
    to traverse edges, and so-on...

    Thus, we've covered two categories of class/object: one, a 'data-class'
    which contains data about a single node; the other a 'collection-class'
    which contains multiple nodes making-up the graph. A useful distinction
    and a common related-pair of data-constructs!
    --
    Regards,
    =dn

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dennis Lee Bieber@21:1/5 to All on Sat Jul 23 20:43:58 2022
    On 23 Jul 2022 22:55:47 GMT, ram@zedat.fu-berlin.de (Stefan Ram) declaimed
    the following:

    Dennis Lee Bieber <wlfraed@ix.netcom.com> writes:
    if len([node for node in nodelist if node.no == no1]):
    print("Found at least one occurence")

    I'm not sure you need the "len"; an empty list already is falsy.


    Just wanted to be explicit for the OP.


    --
    Wulfraed Dennis Lee Bieber AF6VN
    wlfraed@ix.netcom.com http://wlfraed.microdiversity.freeddns.org/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Khairil Sitanggang@21:1/5 to PythonList@danceswithmice.info on Sat Jul 23 20:01:18 2022
    dn,

    Thanks for the good analogy and explanation. I need some time to digest it.

    Regards,
    -Irfan


    On Sat, Jul 23, 2022 at 6:55 PM dn <PythonList@danceswithmice.info> wrote:

    On 24/07/2022 09.57, MRAB wrote:
    On 23/07/2022 05:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to
    check
    if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20
    NODELIST.append(NODE)

    NODE.NO = 30
    NODELIST.append(NODE)


    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    No, you can't do it that way. You have to iterate through the list and check each member individually:

    if any(NO1 == N.NO for N in NODELIST):

    And another thing: you've created only 1 node, and you're changing it
    each time before adding it to the list, so the list ends up with 3 references to the _same_ object.

    +1


    Imagine the object (Node) was instead a person, and a bunch of us-people
    get together in a room. You could shout (above the polite conversation)
    "is Fred here?" and Fred will reply - or if Fred is elsewhere, silence
    will reign. That works - but only because everyone knows their own name.

    Now imagine something like (my) grade school, where the teacher takes a roll/register to note who is present for (or absent from) class. In this
    case another staff-member could enter the room and instead of shouting
    (which would be rude), can ask the teacher "is Fred here?". The teacher
    is able to answer from the roll.

    The former case is (sort of) the solution proposed above - in looking
    for Fred, you walk through the room, asking each person in-turn "are you Fred?".

    The latter is the case for Node and what you were hoping to implement.

    Thus, an alternate approach is to keep a register of nodes. Note that
    this is more than a list, because each element of the list (each node)
    is also identified (on the list) by its name. So, two pieces of
    (related) data: the id of the node, and the node itself - the name of
    the person and the person him-/her-self.

    Assuming you only have one ID that will be used to access a node,
    Python's built-in dict[ionary] data-structure will provide the advantage
    of direct-access (instead of going through (on average) half the nodes, asking each one in-turn, are you ...

    So:

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    becomes:

    graph = dict{} # could be written as: graph = {}
    node10 = Node( 10 )
    graph[ node.id ] = node10

    or even:

    graph[ 20 ] = Node( 20 )

    if you don't need lots of nodes 'hanging around' outside of the 'list' (actually a dict) representing the graph.


    Then:

    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    becomes a single line:

    the_node_required = graph[ 20 ]

    where "20" is the search-criteria.

    Does that make sense?


    If it does, and when you realise that you'd like to do more with the
    graph than retrieve single nodes (and more-importantly, should you want
    (or dare) to delve further into the depths of Object-Oriented
    Programming) you could declare a second class (graph) containing a
    number of "methods". If one of the methods implemented is __contains__()
    then you could indeed ask:

    if 20 in graph:

    ie "is Fred in-class today?"
    but just as we would ask "is Fred here?" rather than "is someone
    identified by the name 'Fred' here?", it would seem better form to write:

    if node( 20 ) in graph:

    ie is there a node with the id of 20, somewhere within the graph?


    Such might involve sub-classing OrderedDict, UserDict, or even UserList,
    from the Collections library in the (provided/"batteries-included")
    Python Standard Library:
    https://docs.python.org/3/library/collections.html

    If needed, the extra methods you choose to implement might include such functionality as connecting nodes 10 and 20 with a path/edge, being able
    to traverse edges, and so-on...

    Thus, we've covered two categories of class/object: one, a 'data-class'
    which contains data about a single node; the other a 'collection-class'
    which contains multiple nodes making-up the graph. A useful distinction
    and a common related-pair of data-constructs!
    --
    Regards,
    =dn
    --
    https://mail.python.org/mailman/listinfo/python-list


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Peter Otten@21:1/5 to Khairil Sitanggang on Sun Jul 24 09:19:45 2022
    On 23/07/2022 06:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to check if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20

    This just overwrites the attribute; the previous value 10 is lost.

    NODELIST.append(NODE)

    NODE.NO = 30

    This againoverwrites the attribute; the previous value 20 is lost.

    NODELIST.append(NODE)


    You are three times appending the *same* node to the list.
    To create a new node you need to invoke the initializer:

    [I'm following a common convention and use lowercase names in my examples]

    nodelist = []

    # first node
    node = Node()
    node.no = 10
    nodelist.append(node)

    # second node
    node = Node() # this is crucial
    node.no = 20
    nodelist.append(node)

    ... and so on. However, usually object creation and initialization is
    combined by allowing arguments to the initializer:

    class Node:
    def __init__(self, no, a):
    self.no = no
    self.a = a

    nodelist = []
    for no in [10, 20, 30]:
    nodelist.append(Node(no, 20))

    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    You are checking if the list contains an item with a specific attribute
    value, so you cannot use the nodelist directly, you need an intermediate
    list that contains the attribute values:

    no1 = 20
    nos = [node.no for node in nodelist]
    if no1 not in nos:
    print("not found")

    There's one disadvantage to this approach. If the node list is huge
    another huge list with the attribute values is built even though the
    first item in the nodelist may already have the searched-for attribute
    value. To avoid the overhead you could write a function:

    def contains_no(nodes, no):
    for node in nodes:
    if node.no == no:
    return True
    return False

    if not contains_no(nodelist, 20):
    print("not found")

    But Python has something more elegant, a kind of /lazy/ /list/ called "generator expression" where each item is calculated on demand. With
    that you can write

    if 20 not in (node.no for node in nodelist):
    print("not found")

    and your script will stop inspecting further nodes as soon as a matching
    node is found.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Khairil Sitanggang@21:1/5 to __peter__@web.de on Sun Jul 24 19:28:17 2022
    Peter:
    Thanks for the explanation. It is clear and easy to understand for a
    beginner like me. I highly appreciate it.
    Regards,
    -Irfan

    On Sun, Jul 24, 2022 at 2:23 AM Peter Otten <__peter__@web.de> wrote:

    On 23/07/2022 06:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to
    check
    if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20

    This just overwrites the attribute; the previous value 10 is lost.

    NODELIST.append(NODE)

    NODE.NO = 30

    This againoverwrites the attribute; the previous value 20 is lost.

    NODELIST.append(NODE)


    You are three times appending the *same* node to the list.
    To create a new node you need to invoke the initializer:

    [I'm following a common convention and use lowercase names in my examples]

    nodelist = []

    # first node
    node = Node()
    node.no = 10
    nodelist.append(node)

    # second node
    node = Node() # this is crucial
    node.no = 20
    nodelist.append(node)

    ... and so on. However, usually object creation and initialization is combined by allowing arguments to the initializer:

    class Node:
    def __init__(self, no, a):
    self.no = no
    self.a = a

    nodelist = []
    for no in [10, 20, 30]:
    nodelist.append(Node(no, 20))

    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    You are checking if the list contains an item with a specific attribute value, so you cannot use the nodelist directly, you need an intermediate
    list that contains the attribute values:

    no1 = 20
    nos = [node.no for node in nodelist]
    if no1 not in nos:
    print("not found")

    There's one disadvantage to this approach. If the node list is huge
    another huge list with the attribute values is built even though the
    first item in the nodelist may already have the searched-for attribute
    value. To avoid the overhead you could write a function:

    def contains_no(nodes, no):
    for node in nodes:
    if node.no == no:
    return True
    return False

    if not contains_no(nodelist, 20):
    print("not found")

    But Python has something more elegant, a kind of /lazy/ /list/ called "generator expression" where each item is calculated on demand. With
    that you can write

    if 20 not in (node.no for node in nodelist):
    print("not found")

    and your script will stop inspecting further nodes as soon as a matching
    node is found.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Khairil Sitanggang@21:1/5 to __peter__@web.de on Sun Jul 24 19:21:21 2022
    Thank you.

    On Sun, Jul 24, 2022 at 2:23 AM Peter Otten <__peter__@web.de> wrote:

    On 23/07/2022 06:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to
    check
    if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20

    This just overwrites the attribute; the previous value 10 is lost.

    NODELIST.append(NODE)

    NODE.NO = 30

    This againoverwrites the attribute; the previous value 20 is lost.

    NODELIST.append(NODE)


    You are three times appending the *same* node to the list.
    To create a new node you need to invoke the initializer:

    [I'm following a common convention and use lowercase names in my examples]

    nodelist = []

    # first node
    node = Node()
    node.no = 10
    nodelist.append(node)

    # second node
    node = Node() # this is crucial
    node.no = 20
    nodelist.append(node)

    ... and so on. However, usually object creation and initialization is combined by allowing arguments to the initializer:

    class Node:
    def __init__(self, no, a):
    self.no = no
    self.a = a

    nodelist = []
    for no in [10, 20, 30]:
    nodelist.append(Node(no, 20))

    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    You are checking if the list contains an item with a specific attribute value, so you cannot use the nodelist directly, you need an intermediate
    list that contains the attribute values:

    no1 = 20
    nos = [node.no for node in nodelist]
    if no1 not in nos:
    print("not found")

    There's one disadvantage to this approach. If the node list is huge
    another huge list with the attribute values is built even though the
    first item in the nodelist may already have the searched-for attribute
    value. To avoid the overhead you could write a function:

    def contains_no(nodes, no):
    for node in nodes:
    if node.no == no:
    return True
    return False

    if not contains_no(nodelist, 20):
    print("not found")

    But Python has something more elegant, a kind of /lazy/ /list/ called "generator expression" where each item is calculated on demand. With
    that you can write

    if 20 not in (node.no for node in nodelist):
    print("not found")

    and your script will stop inspecting further nodes as soon as a matching
    node is found.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Khairil Sitanggang@21:1/5 to __peter__@web.de on Sun Jul 24 19:47:38 2022
    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()





    On Sun, Jul 24, 2022 at 2:23 AM Peter Otten <__peter__@web.de> wrote:

    On 23/07/2022 06:28, Khairil Sitanggang wrote:
    Hello Expert:

    I just started using python. Below is a simple code. I was trying to
    check
    if, say, NO1 is not in the NODELIST[:].NO
    How can I achieve this purpose?

    Regards,
    -Irfan


    class Node:
    def __init__(self):
    self.NO = 0
    self.A = 20

    NODE = Node()
    NODELIST = []

    NODE.NO = 10
    NODELIST.append(NODE)

    NODE.NO = 20

    This just overwrites the attribute; the previous value 10 is lost.

    NODELIST.append(NODE)

    NODE.NO = 30

    This againoverwrites the attribute; the previous value 20 is lost.

    NODELIST.append(NODE)


    You are three times appending the *same* node to the list.
    To create a new node you need to invoke the initializer:

    [I'm following a common convention and use lowercase names in my examples]

    nodelist = []

    # first node
    node = Node()
    node.no = 10
    nodelist.append(node)

    # second node
    node = Node() # this is crucial
    node.no = 20
    nodelist.append(node)

    ... and so on. However, usually object creation and initialization is combined by allowing arguments to the initializer:

    class Node:
    def __init__(self, no, a):
    self.no = no
    self.a = a

    nodelist = []
    for no in [10, 20, 30]:
    nodelist.append(Node(no, 20))

    NO1 = 20
    if NO1 not in NODELIST[:].NO ???

    You are checking if the list contains an item with a specific attribute value, so you cannot use the nodelist directly, you need an intermediate
    list that contains the attribute values:

    no1 = 20
    nos = [node.no for node in nodelist]
    if no1 not in nos:
    print("not found")

    There's one disadvantage to this approach. If the node list is huge
    another huge list with the attribute values is built even though the
    first item in the nodelist may already have the searched-for attribute
    value. To avoid the overhead you could write a function:

    def contains_no(nodes, no):
    for node in nodes:
    if node.no == no:
    return True
    return False

    if not contains_no(nodelist, 20):
    print("not found")

    But Python has something more elegant, a kind of /lazy/ /list/ called "generator expression" where each item is calculated on demand. With
    that you can write

    if 20 not in (node.no for node in nodelist):
    print("not found")

    and your script will stop inspecting further nodes as soon as a matching
    node is found.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From 2QdxY4RzWzUUiLuE@potatochowder.com@21:1/5 to Khairil Sitanggang on Sun Jul 24 23:28:30 2022
    On 2022-07-24 at 19:47:38 -0500,
    Khairil Sitanggang <ksit70@gmail.com> wrote:

    Regarding [Peter Otten's] 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.

    No. Please use option 1.

    Another option would be to expose b as an optional parameter with a
    default value:

    class Node:
    def __init__(self, a, b=0):
    self.a = a
    self.b = self.calculation() if b == 0 else b

    There are other ways to write that particular assignment to b (because
    the default is 0), but the relevant concept for right now is that
    callers *can* supply a value for b, but that they don't *have* to:

    n1 = Node(a) # uses a default value for b
    n2 = Node(a, 22) # overrides the default; use 22 instead

    Designing APIs can be tricky, and it's not an exact science.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to Khairil Sitanggang on Mon Jul 25 18:01:10 2022
    On 25/07/2022 12.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()

    Let's start with calculation() - even though it is not your specific
    question:

    Given that "self" makes it into an instance-method, it will have access
    to self.b! Accordingly, the intermediate variable "r" and its return
    serves no purpose - assuming calculation() is only used to produce a
    value for self.b - which would leave:

    def calculation( self ):
    self.b = self.a + 10

    At which point, the method becomes pointless - may as well put its
    single line in-line within __init__() - as I say, with above assumptions.


    Some languages do expect that every identifier (data-attribute in this
    case) be declared (as to type) and probably also initialised with a
    value. Some languages, and some Style Guides require that all
    data-attributes are declared within the constructor/initialiser.

    Python requires neither of these.

    Accordingly, if the "b" argument will only ever be a "dummy", there is absolutely no need for it - indeed one could argue that its presence is confusing because it gives the impression than it could assume any
    value. (see elsewhere in this thread).

    So, with assumptions and short of facts, "option 1" seems better (with
    the additional consideration regarding calculation(), as above).
    --
    Regards,
    =dn

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Peter Otten@21:1/5 to Khairil Sitanggang on Mon Jul 25 10:50:44 2022
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Khairil Sitanggang@21:1/5 to __peter__@web.de on Mon Jul 25 09:34:11 2022
    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 <__peter__@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.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Khairil Sitanggang@21:1/5 to All on Mon Jul 25 13:29:55 2022
    Again, thank you so much for all your suggestions; it's even easier for me
    now to proceed with my coding with all the guidance in one place (this
    email thread). I highly appreciate all of your help.
    I love this python community :)

    Regards,
    -Irfan


    On Mon, Jul 25, 2022 at 12:38 PM Dennis Lee Bieber <wlfraed@ix.netcom.com> wrote:

    On 23 Jul 2022 22:55:47 GMT, ram@zedat.fu-berlin.de (Stefan Ram) declaimed the following:

    Dennis Lee Bieber <wlfraed@ix.netcom.com> writes:
    if len([node for node in nodelist if node.no == no1]):
    print("Found at least one occurence")

    I'm not sure you need the "len"; an empty list already is falsy.


    Just wanted to be explicit for the OP.


    --
    Wulfraed Dennis Lee Bieber AF6VN
    wlfraed@ix.netcom.com
    http://wlfraed.microdiversity.freeddns.org/
    --
    https://mail.python.org/mailman/listinfo/python-list


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)