Search code examples
pythonpython-3.xcoding-style

Is there a best practice for defining variables in _init_ method


Is there any good reason for the way you define object variables in an __init__ method, should you build new variables using self.var or just var.

Does it matter? Is there a microscopic speed benefit?

Personally I prefer the latter as debatably clearer and less text. But I often see both.

class Person():
    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname
        self.fullname = self.first + ' ' + self.last

Or:

class Person():
    def __init__(self, firstname, lastname):
        self.first = firstname
        self.last = lastname
        self.fullname = firstname + ' ' + lastname

Solution

  • Local names are slightly faster, so first, not self.first, as the latter requires looking up the local name self, then looking up the attribute first on that object. Local variable use is highly optimised compared to all other lookups.

    However, the difference is miniscule, and focusing on that is a premature optimisation. Only worry about this if you have to use the same variable loads of times in critical code.

    If you ignore the performance differences, then it comes down to requirements and clarity; instance attributes can behave very differently from local variables, which can be an advantage:

    • If you need to support subclasses that want to alter how self.first and self.last behave when assigned to or when accessed (using a property), then you'd want to use the attributes.

    • Using the local parameter names on the other hand makes it clear that fullname is independent of how self.first and self.last behave if not simple attributes. That could be intentional.

    We can't give any more specific guidelines here because attributes can easily support different use-cases from locals.

    If you do need to worry about performance, use the timeit module to run a micro-benchmark:

    >>> from timeit import timeit
    >>> class PersonAttributes():
    ...     def __init__(self, firstname, lastname):
    ...         self.first = firstname
    ...         self.last = lastname
    ...         self.fullname = self.first + ' ' + self.last
    ...
    >>> class PersonLocals():
    ...     def __init__(self, firstname, lastname):
    ...         self.first = firstname
    ...         self.last = lastname
    ...         self.fullname = firstname + ' ' + lastname
    ...
    >>> timeit('P("Eric", "Idle")', 'from __main__ import PersonAttributes as P')
    0.5858714409987442
    >>> timeit('P("Eric", "Idle")', 'from __main__ import PersonLocals as P')
    0.5170505980058806
    

    The above creates an instance 1 million times, at which point you'll have saved 40 milliseconds by using local names.