3

My objective is to create a population of individuals with attributes like iid, gender and state. The population is defined as a class. I then create a population class instance which allows me to add individuals to and remove individuals from the population using the following:

class PopulationClass(object):

    def __init__(self):
        self.__population = dict()
        self.__iid_counter = 0
        self.__removed_individuals = []

    def add2population(self, no2add):
        import random

        iid_counter = self.__iid_counter

        for a in range(no2add):
            self.__population[iid_counter] = (iid_counter, random.sample(('F', 'M'), 1)[0], 'S')

            iid_counter += 1  

        self.__iid_counter = iid_counter

    def remove_from_population(self, key):
        self.__removed_individuals.append(self.__population.pop(key))

In the terminal, I do the following:

>>> population_instance = PopulationClass()
>>> population_instance.add2population(5)
>>> population_instance._PopulationClass__population
{0: (0, 'F', 'S'), 1: (1, 'M', 'S'), 2: (2, 'F', 'S'), 3: (3, 'M', 'S'), 4: (4, 'M', 'S')}
>>> population = population_instance._PopulationClass__population
>>> population[5] = 'Illegal changes'
>>> population
{0: (0, 'F', 'S'), 1: (1, 'M', 'S'), 2: (2, 'F', 'S'), 3: (3, 'M', 'S'), 4: (4, 'M', 'S'), 5:     'Illegal changes'}
>>> population_instance._PopulationClass__population
{0: (0, 'F', 'S'), 1: (1, 'M', 'S'), 2: (2, 'F', 'S'), 3: (3, 'M', 'S'), 4: (4, 'M', 'S'), 5: 'Illegal changes'}

I wish that population would refer to the dictionary (population_instance._PopulationClass__population) and not be able to make changes to that dictionary.

However, I want individuals to be added to and removed from the population_instance using the add2population and remove_from_population methods. How can I avoid such illegal changes from being passed through to the class instance?

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
Mischief_Monkey
  • 145
  • 2
  • 11
  • 7
    You can't. With great effort and extreme awkwardness, you could create a custom class to be used instead of a dict for this purpose, but it still wouldn't work perfectly. Going down this road is futile. Just document that users should not mess with the internal attribute and let them suffer the consequences if they do. – BrenBarn Jul 07 '14 at 04:51
  • 4
    By using a double underscore in the `__population` attribute, you're already sending a message to anyone familiar with Python that says "Please don't mess with this attribute directly". That should be plenty. – Marius Jul 07 '14 at 05:00
  • Rightly said,the "__" is clearly saying that it's a private member so one must not play with it. Instead u can have a "getter" function to provide u a copy of the population object, so in that case you will prevent an outside changes to ur internal object. But that still won't stop anyone from playing with that Private object. Take it as a subtle way of doing it. To prevent it altogether, u'll need to wrap the dict object and give ur own implementation, & in that you could limit access using >>> http://stackoverflow.com/questions/1095543/get-name-of-calling-functions-module-in-python – Syed Mauze Rehan Jul 07 '14 at 06:00
  • Why `random.sample(('F', 'M'), 1)[0]`? `random.choice('FM')` would make things much neater. – jonrsharpe Jul 07 '14 at 06:26
  • I recommend you read [what would be a frozen dict?](http://stackoverflow.com/questions/2703599/what-would-be-a-frozen-dict) – Jose Ricardo Bustos M. Apr 15 '15 at 19:30

1 Answers1

0

you should to make "get" function for access to private member

class PopulationClass(object):
  def __init__(self):
    self.__population = dict()
    self.__iid_counter = 0
    self.__removed_individuals = []
  def add2population(self, no2add):
    import random
    iid_counter = self.__iid_counter
    for a in range(no2add):
      self.__population[iid_counter] = (iid_counter, random.sample(('F', 'M'), 1)[0], 'S')
      iid_counter += 1  
    self.__iid_counter = iid_counter
  def remove_from_population(self, key):
    self.__removed_individuals.append(self.__population.pop(key))
  def getPopulation():
    import copy
    return copy.deepcopy(self.__population)

population_instance = PopulationClass()
population_instance.add2population(5)
population_instance._PopulationClass__population
population = population_instance.getPopulation()
population[5] = 'Illegal changes'
population
population_instance._PopulationClass__population
Jose Ricardo Bustos M.
  • 8,016
  • 6
  • 40
  • 62