Re: Object in List : how?

2022-07-24 Thread Peter Otten

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.
--
https://mail.python.org/mailman/listinfo/python-list


Re: Object in List : how?

2022-07-24 Thread Khairil Sitanggang
Thank you.

On Sun, Jul 24, 2022 at 2:23 AM Peter Otten <__pete...@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.
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Object in List : how?

2022-07-24 Thread Khairil Sitanggang
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 <__pete...@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.
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Object in List : how?

2022-07-24 Thread Khairil Sitanggang
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 <__pete...@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.
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Object in List : how?

2022-07-24 Thread 2QdxY4RzWzUUiLuE
On 2022-07-24 at 19:47:38 -0500,
Khairil Sitanggang  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.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Object in List : how?

2022-07-24 Thread dn
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
-- 
https://mail.python.org/mailman/listinfo/python-list