Suppose I have this class:

class Bunch_of_comments(): def __init__(self, *list_of_comments, date=datetime.now()): self.date = date self.list_of_comments = [] self.comments_counter = 0 self.list_of_comments.append(list_of_comments) self.comments_counter += len(list_of_comments) 

I want to make it a getter on the list_of_comments list. Here is the action

 def get_list_of_comments(self): return self.get_list_of_comments() # don't forget to find out about return value 

breaks encapsulation because the link to the list is returned, which means that the one who accepts it can do with it everything he wants, for example, add elements without a setter to it.

What to do ?

  • All fields in Python are public, so if someone wants to change your array, he will simply do this: obj.list_of_comments = [1, 2, 3] or even obj.list_of_comments = "Bla Bla Bla" . - pank
  • What to do ? Is it possible to make them somehow closed? - faoxis
  • This can not be done. In the python there is an agreement that the fields in the names of which at the beginning are double underscore __name should be considered private, but in fact they will still be public and anyone can change them. Nothing - can you do such a language. - pank
  • I do not understand. In Python, it turns out that there are no setters and getters at all, and is direct access to the fields considered normal? - faoxis
  • In Python and global variables is the norm. This is a scripting language. What do you want from him. Here you can read about classes in Python. - pank

2 answers 2

It is possible to return not the list itself, but its copy (either superficial or deep).

This ensures that the external user of the class does not accidentally ruin his internal mechanics.

But, of course, if he is specifically puzzled to climb where he doesn’t need his curved handles, he will climb there. In Python, this is considered normal - so if someone deliberately climbs under the hood of someone else's code, then he considers himself ready for possible consequences.

UPD: The easiest way to make a copy of the list is to take a slice without specifying boundaries:

 copy_list = source_list[:] 

This method is suitable for those cases when the list consists only of immutable elements (numbers, strings, tuples, frozenset, etc.)

However, this method should not be used if the list includes mutable elements (other lists, sets, etc.) - because from the copy it will be possible to change these objects in the original. In this case, you need to use a deep copy:

 import copy dcopy_list = copy.deepcopy(source_list) 
  • And how to make a copy of the list in Python? - faoxis
  • faoxis, added about how to make copies of the answer itself, but in the comments, the sample code is normally not inserted. - Xander

You can return iter() from the list, then the list itself cannot be changed directly (add / delete / replace elements, replace the list with another one, although if the elements are editable, it will be possible to influence them). This is better than returning a copy of the list, because does not require additional memory for a copy.

In your case, you can do this:

 class Bunch_of_comments(): ... def get_comments(self): return iter(self.list_of_comments) # возвращаем итератор comments = Bunch_of_comments() ... # добавляем несколько комментариев # Выводим список: for item in comments.get_comments(): print(item) 

It must be remembered that the iterator allows only sequential access to the elements (access by index is not possible). If you need access by index, you need to make a list from an iterator.

If you need a copy of the list, you can wrap the call to this method in list() :

 x1 = list(comments.get_comments()) 

This will be a “shallow” copy, i.e. the elements themselves will be references to the elements of the original list.

If you need a "deep" copy, you can use the copy.deepcopy() function, as Alexander showed in his answer.

  • Good point. I add that it is necessary, depending on the case, to choose between a copy and an iterator. If it is assumed that within the class itself the list will change unpredictably, then the iterator may produce unpredictable results. - Xander
  • Is it possible to have a small example of the function that is returned by iter ()? - faoxis
  • one
    @faoxis: maybe Bunch_of_comments() should be Bunch_of_comments() into an iterable value (iterable), so that you can use right in a loop: def __iter__(self): return iter(self.list_of_comments) , and then for comment in comments: print(comment) - jfs