Reputation: 820
Often when I create classes in Python (and other languages) I struggle to decide which is a better practice: (a) using instance variables in my method functions, or (b) listing the input variables in the function definition.
(a)
class ClassA(object):
def __init__(self, a):
self.variable_a = a
def square_a(self):
return self.variable_a ** 2
(b)
class ClassB(object):
def __init__(self, b):
self.variable_b = b
def square_b(self, input_var):
return input_var ** 2
These examples are very simple and obvious, but highlight what I find confusing in regard to which is the better idea. Furthermore, is it taboo to set an instance variable outside of the __init__
method? For example:
class ClassC(object):
def __init__(self, c):
self.variable_c = c
def square_c(self):
self.square_of_c = self.variable_c ** 2
EDIT: I understand the somewhat-vague nature of this question, but I asked it because it's difficult to know what people expect to see in source code that I write for, say, collaborative projects. If one or more of the examples I gave is an anti-pattern, my thinking was that this question would provide me with helpful insight.
From PEP 20:
There should be one-- and preferably only one --obvious way to do it.
Upvotes: 2
Views: 5396
Reputation: 105
Your question is really vague and therefore difficult to answer. All classes seem to have correct syntax.
However b) looks more useful to me than a) It looks like you just want to return the square of the input variable, so I'd assume you want to calculate the square of different values all the time. If you do that in case a) you will have to set the variable every time before you call the method.
b) makes me wonder what you use variable_b for as it's never used really. But I guess that's due to simplification.
Also you might consider making square_b a static method, as it doesn't use any object attributes. (never calls self
)
class ClassB(object):
def __init__(self, b):
self.variable_b = b
@staticmethod
def square_b(input_var):
return input_var ** 2
Variant c) is also valid in terms of syntax, but some might regard it bad practice. A question of taste really, but many books and websites will advise you to declare variables in the init method.
Upvotes: 0
Reputation: 13589
In this example, (b) is not very useful as a member function. It could just as easily be a free function:
def square(input_var):
return input_var ** 2
This is arguably a better interface as it can be used in any context, not just from an instance of ClassB
.
Generally I would go with (a) if I know self.variable_a
is the only input the function should need. If I want it to work with anything and it doesn't depend on anything in the class, I would make it a free function. If I want it to work with anything but it does depend on some class state, then make it a member that takes the input as a parameter. As an example, what if ClassA
contained both a variable_a
and a variable_b
? You couldn't use square_a
to modify variable_b
, which may or may not be desired depending on the actual use case.
Furthermore, is it taboo to set an instance variable outside of the init method?
No, but it's generally a good idea to make sure all members are initialized somewhere around the time of class instantiation. Even if you just initialize your members to None
. It is much easier to check if a member variable is None
rather than trying to determine whether or not it is defined.
EDIT: Another few examples:
# Here a free function makes the most sense because the operation is 'pure'
# i.e. it has no side effects and requires no state besides its input arguments
def square(value):
return value ** 2
class LoggedCalculator(object):
def __init__(self, logger):
self.__logger = logger
# (a) makes more sense here because it depends on class state and doesn't need to change
# its behavior by taking in some parameter
def get_logger(self):
return self.__logger
# (b) makes more sense here because we rely on a mixture of class state and some other input
def square(self, value):
result = square(value) # Re-use free function above
self.__logger.info('{}^2 = {}'.format(value, result))
return result
calc = LoggedCalculator(logging.getLogger())
calc.square(4) # This requires an instance of LoggedCalculator
square(2) # This can be called anywhere, even if you don't have a logger available
Upvotes: 1