Exposing members or closing them in Python?
Is there a general convention for exposing members in Python classes? I know this is a "it depends" case, but maybe there is a rule of thumb.
Private member:
class Node:
def __init__(self):
self.__children = []
def add_children(self, *args):
self.__children += args
node = Node()
node.add_children("one", "two")
Public participant:
class Node2:
def __init__(self):
self.children = []
node2 = Node2()
node2.children += "one", "two"
If there's no good reason to make it children
private, are you left with the method add_children
?
a source to share
-
The answer, of course, is "it depends." If I was assuming I would use the method
add_children
. A class point usually abstracts what you are doing, from the representation of the state that you are storing.my_node.add_children(a, b)
is the operation I would read as "add children a and b to my mode".my_node.children.extend([a, b])
forces the user to think of itchildren
as a list and delete inside the instance to mutate it. It is sometimes, but less often, what you want. It also makes it difficult to change the API if you end up needing to add children to do something extra. -
__foo
attributes are not private. Python Name - Manages them in a systematic, easily reproducible way. Python does not support real private attributes. Using__foo
doesn't add privacy, but it makes your class harder to test, inherit, and use.When I have an attribute that I don't want as part of my public API, I prefix it with a single underscore, eg.
_foo
... This convention is widely used. -
I never use it
+=
with lists; I use the method insteadextend
. I don't like+=
it because most people use itextend
and because its work is a little confusing.a += b
differs from ina = a + b
two ways: the first mutates the original list, and the second creates a new list and reverts it to the original name, and the latterb
must have a list and the firstb
can be arbitrary iterable. I find a purifierextend
. -
I always inherit
object
, not nothing, i.e.class Node(object):
so I am using new style classes. The new-style classes work in a slightly more natural way and have a few features of the old-style classes.
a source to share
As you said, it all depends. I usually prefer to have a way to interact with the class in order to get a separation between the programming logic and its implementation. Following the example you posted, I wrote:
class Node:
def __init__(self):
self.childs = []
def add_childs(self, *args):
self.childs += args
because if the subclass is going to have access to it childs
, it might be without any weird varname. I usually only use private members with properties to create a cache of the computed value.
a source to share
The convention is intended to be prefixed with a single underscore, however it is purely advisory and is still available to access the attribute like any other. Handy sometimes when you write unit tests
The double underscore prefix invokes the name mangling. You might think the attribute is private, but you can access it like this.
>>> node=Node()
>>> node._Node__children
[]
a source to share