3

I have a problem when adding value inside the nested dictionary using the same keys and the value is always shown the same value, The fact is, i want update the value event the keys is same. This algorithm is the basic of Artificial Fish Swarm Algorithm

# example >> fish_template = {0:{'weight':3.1,'visual':2,'step':1},1:'weight':3,'visual':4,'step':2}}

fish = {}
fish_value = {}
weight = [3.1, 3, 4.1, 10]
visual = [2, 4, 10, 3]
step = [1, 2, 5, 1.5]

len_fish = 4

for i in range(0,len_fish):
  for w, v, s in zip(weight, visual, step):
     fish_value["weight"] = w
     fish_value["visual"] = v
     fish_value["step"] = s
     fish[i] = fish_value

  print("show fish",fish)

I expect the result to be like fish_template, but it isn't. The values for the keys 'weight', 'visual', 'step' are always the same with values of 0, 1, 2, and 3. Any solution?

Subhanshuja
  • 390
  • 1
  • 3
  • 20
  • 1
    Because aliasing; the line `fish[i] = fish_value` is bad practice, `fish_value` gets overwritten. But really you can avoid the loop with a dict comprehension. Anyway, better coding practice is to declare your own `Fish` class with weight, visual, step members. – smci Apr 28 '19 at 01:18
  • @smci, okay, so it is better create class or object to grab the value each loop, thanks for your advice – Subhanshuja Aug 13 '19 at 10:48

3 Answers3

3

The issue is with fish[i], you simply created a dict with the same element: fish_value. Python does not generate a new memory for the same variable name, so all your dict keys point to the same value=fish_value, which gets overwritten and all your dict values take the last state of fish_value. To overcome this, you can do the following:

fish   = {}
weight = [3.1, 3, 4.1, 10]
visual = [2, 4, 10, 3]
step   = [1, 2, 5, 1.5]

len_fish = 4

for i in range(0, len_fish):
     fish[i]= {"weight": weight[i], "visual": visual[i], "step": step[i]}

print("show fish", fish)

As @Error mentioned, the for loop can be replaced by this one-liner:

fish = dict((i, {"weight": weight[i], "visual": visual[i], "step": step[i]}) for i in range(len_fish))
SuperKogito
  • 2,998
  • 3
  • 16
  • 37
1

Not sure I fully understand what you're trying to do here, but the problem is the last line of your inner for loop. You're looping over i in the main loop, then then inner loop is setting fish[i] multiple times. As a result all your fish_value will look identical.

PirateNinjas
  • 1,908
  • 1
  • 16
  • 21
  • yup, i have already tried to inside print the value is show up, but when i get from last loop is the last value is not all value after adding – Subhanshuja Apr 27 '19 at 23:58
1

Because of aliasing; the line fish[i] = fish_value is bad practice, fish_value gets overwritten each time you loop; then fish[i] = fish_value just assigns a shallow copy into fish[i], which is not what you want. But really you can avoid the loop with a dict comprehension.

Anyway, better coding practice is to declare your own Fish class with members weight, visual, step, as below. Note how:

  • we use zip() function to combine the separate w,v,s lists into a tuple-of-list.
  • Then the syntax *wvs unpacks each tuple into three separate values ('weight', 'visual', 'step'). This is called tuple unpacking, it saves you needing another loop, or indexing.
  • a custom __repr__() method (with optional ASCII art) makes each object user-legible. (Strictly we should be overriding __str__ rather than __repr__, but this works)

Code:

class Fish():
    def __init__(self, weight=None, visual=None, step=None):
        self.weight = weight
        self.visual = visual
        self.step = step
    def __repr__(self):
        """Custom fishy __repr__ method, with ASCII picture"""
        return f'<º)))<  [ Weight: {self.weight}, visual: {self.visual}, step: {self.step} ]'
    # define whatever other methods you need on 'Fish' object...

# Now create several Fish'es...
swarm = [ Fish(*wvs) for wvs in zip([3.1, 3, 4.1, 10], [2, 4, 10, 3], [1, 2, 5, 1.5]) ]
# zip() function combines the lists into a tuple-of-list. `*wvs` unpacks each tuple into three separate values ('weight', 'visual', 'step')

# See what we created...
>>> swarm
[<º)))<  [ Weight: 3.1, visual: 2, step: 1 ], <º)))<  [ Weight: 3, visual: 4, step: 2 ], <º)))<  [ Weight: 4.1, visual: 10, step: 5 ], <º)))<  [ Weight: 10, visual: 3, step: 1.5 ]]

# ... or for prettier output...
>>> for f in swarm: print(f)

<º)))<  [ Weight: 3.1, visual: 2, step: 1 ]
<º)))<  [ Weight: 3, visual: 4, step: 2 ]
<º)))<  [ Weight: 4.1, visual: 10, step: 5 ]
<º)))<  [ Weight: 10, visual: 3, step: 1.5 ]
smci
  • 32,567
  • 20
  • 113
  • 146
  • 1
    Strictly classes should override their `__str__` rather than `__repr__`, but this works unless you want to `pickle` your `Fish` ... ;-) – smci Apr 28 '19 at 01:37
  • 1
    Sure thing. When you get to [15 reputation, you can upvote answers you found helpful](https://stackoverflow.com/help/privileges). Till then, happy swarming... – smci May 07 '19 at 05:22
  • 1
    Did you like the custom fishy `__repr__` method? – smci May 07 '19 at 06:31
  • 1
    I already created my own custom fishy based on the first answer before your answer. Sorry, but your answer give me another sight about the new code, thank you – Subhanshuja May 08 '19 at 11:05