简体   繁体   中英

“Private” arguments to __init__()?

I have a class that takes a single parameter a on instantiation, which is stored in the _a attribute. For a number of methods (operators), I need to set also a _b attribute on the result. This is currently implemented in a straight-forward way:

class SomeClass(object):
    def __init__(self, a=0):
        self._a = a
        self._b = 0

    def __add__(self, other):
        result = self.__class__()
        result._b = self._a + other._a
        return result

Now, I have an number of members like _b , such as _c and _d , so __add__ will need an extra line for each of these attributes. Being able to pass these on object instantiation would result in cleaner code:

class SomeClass(object):
    def __init__(self, a=0, _b=0):
        self._a = a
        self._b = 0

    def __add__(self, other):
        return self.__class__(_b=self._a + other._a)

However, I don't want the user to pass values for all of the parameters, except for a as _b , _c and _d are implementation specifics. I could simply state in the docstring not to pass more than one argument. Preceding the 'private' attributes with an underscore is intended to reflect this.

Alternatively, I can try to make it harder for the user to misbehave, by providing a second, private constructor:

class SomeClass(object):
    def __init__(self, a=0):
        self._a = a
        self._init()

    def _init(self, _b=0):
        self._b = _b

    @classmethod
    def _constructor(cls, a, _b):
        obj = cls(a)
        obj._init(b)
        return obj

    def __add__(self, other):
        return self.__class__._constructor(_b=self._a + other._a)

I'm thinking this is a rather clunky solution.

What would be the preferred way to solve this problem? Are there other, more elegant, solutions? Is this really a problem; should I just use the first option and end up with some more lines of code?

The _ underscore convention is clear and prevalent through the python world.

You document your 'public' attributes, and just use default arguments with underscores to your __init__ method. Keep it simple.

If someone wants to screw up a class by using those private parameters anyway, you are not going to stop them, not in Python.

To tidy it up a tiny bit, you could set _b before __init__ :

class SomeClass(object):
    _b = 0
    def __init__(self, a=0):
        self._a = a

    def __add__(self, other):
        result = self.__class__()
        result._b = self._a + other._a
        return result

Or if there are heaps of private variables, put them into a list and do some magic?

class SomeClass(object):
    calculated_vars = ['_b'] # All your calculated variables

    def __init__(self, a=0):
        self._a = a

    def __getattr__(self, k):
        if k in self.calculated_vars:
            return 0 # Default value for calculated variables
        else:
            raise AttributeError('{} not found'.format(k))

    def __add__(self, other):
        result = self.__class__()
        result._b = self._a + other._a
        return result

if __name__ == '__main__':
    i = SomeClass(1)
    print '_a attr: ', i._a # 1
    print '_b attr: ', i._b # 0 (Default value)
    print '_c attr: ', i._c # AttributeError: _c not found

    i2 = SomeClass(3)
    i3 = i + i2
    print '_b attr: ', i3._b # 4 (Calculated value)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM