Reputation: 47
I don't know the proper terminology for this so couldn't find anything online about this.
Take this example code:
def Fruit(object):
def __init__(self, color):
self._color = color
def color(self):
return self._color
Now, say I want to check to see whether a fruit is red:
def isRed(self):
if self._color == "red":
return True
return False
Would work perfectly fine. However, so does
def isRed(self):
if self.color() == "red":
return True
return False
Is there a reason why it is good practice to have a getProperty
function? (I'm assuming it is, since an MIT professor, whose course I'm taking, does this with his classes and expects students to do the same on their homework.)
Are either of these two examples different, and why is it against convention to simply refer to the property by self.property
?
Edit: Added underscore to make self._color
for convention.
Upvotes: 0
Views: 1754
Reputation: 9994
TL;DR: Not all general programming best practices aren't Python best practices. Getter and setter methods are a general (OOP) best practice, but not a Python best practice. Instead, use plain Python attributes when you can and switch to Python @property
s as-needed.
It many object-oriented programming languages (e.g. Java and C++), it is regarded as good practice to:
Why?
Let's look at these in detail:
One of the core ideas of object orientation is that bundling the definition of small chunks of data together with functionality related to that data makes imperative/"structured"/procedural programs more manageable and evolvable.
These bundles are called "objects". Each "class" is a template of a group objects with the same data structure (though potentially different data) and the same related functionality.
The data definition are the (non-static) data members of a class (the "attributes" of the objects). The related functionality is encoded in function members ("methods").
This can also be seen as a way to build new user-defined types. (Each class is a type, each object is kinda like a value.)
Often, the methods need more guarantees about the attribute values to work properly than the types of the data members already provide. Let's say you have
class Color() {
float red;
float green;
float blue;
float hue() {
return // ... some formula
}
float brightness {
return // ... some formula
}
}
If red
, green
and blue
are in the range [0, 1], the implementation of these methods would probably depend on that fact. Similarly, if they were in the range [0, 256). And whatever the class-internal convention is, it is the task of the methods of that class to uphold it and only assign values to the data members that are acceptable.
Though, usually, objects of different classes have to interact for a meaningful object-oriented program. But you don't want to think about another class' internal conventions, just because you're accessing it, as that would require a lot of lookups to find out what those conventions are. So you shouldn't assign to the data members of objects of a class from code outside that class.
To avoid this happening by mistake or negligence, the widely accepted best practice in these languages is to declare all data members private. But that means that they cannot be read from outside, either! If the value is of interest to the outside, we can work around this by providing a non-private getter method that does nothing but provide the value of the attribute.
Say the outside (e.g. another class) must be able to set the value of some attribute of your class. And say there aren't any restrictions necessary beyond what that attribute's type already imposes. Should you make that attribute public? (Still assuming this isn't in Python!) No! Instead, provide a setter method that does nothing but taking a value as argument and assigning it to the attribute!
Seems kinda dull, so why do that? So that we can change our mind later!
Say you want to log to the console/terminal (std-out) each time the red-component of your color object changes. (For whatever reason.)
In a (setter) method, you add one line of code and it does that, without requiring any change in the callers.
But if you need to first switch from assigning to a public attribute to calling a setter method, all the code pieces doing assignments to these attributes (which might be many by that time) have to be changed, too! (Don't forget to make the attribute private, so that none will be forgotten.)
So it's better to have only private attributes from the beginning, and add setter method when code outside the class has to be able to set the value.
Say you just noticed that for your application, colors should really be represented internally as hue, value and saturation rather than red, green and blue components.
If you have setter and getter methods, the ones for red, green and blue will become more complicated due to the neccesary conversion calculations. (But the brightness and hue method will become much simpler.) Still, changing them can be much less work than having to change all the code outside the class that uses the class. As the interface stays the same, callers won't have to be changed at all and won't notice a difference.
But if you need to first switch from assigning to a public attribute to calling a setter method ... well, we've been there, haven't we?
So accessor methods methods (that what we call getters and setters) help you decouple the public interface of a class from its internal implementation, and thereby the objects from their users. This allows you to change the internal implementation without breaking the public interface, so that code using your class doesn't have to be changed when you do that.
Need an attribute that can only be read from the outside, but not written from the outside? Easy: Provide only a getter method, but no setter method (and have the attribute itself be private).
Less common, but more common than you might think:
Need an attribute that can only be written from the outside, but not read from the outside? Easy: Provide only a setter method, but no getter method (and have the attribute itself be private).
Not sure if your attribute should be accessed (and accessible) from outside your class? Make it private and don't provide any getter and setter for now. You can always add them later. (And then think about what visibility level they should have.)
As you see, there's no reason to ever have a non-private attribute in a mutable object. (Assuming that the runtime overhead doesn't matter for your application (it probably doesn't, indeed) or is optimized away by the compiler (it probably is, at least partially).)
Note that "visibility" levels of attributes and methods are not meant for providing application security or privacy (they don't). They're tools to help programmers from making mistakes (by avoiding them to access stuff they shouldn't by accident), but they won't keep adversarial programmers from accessing that stuff anyway. Or, for that matter, honest programmers who think they know what they're doing (whether they do know or not) and willing to take the risk.
While Python is also imperative, "structured", procedural and very object-oriented, it takes a much more laid back approach to visibility. There is no real "private" visibility level in Python, nor "protected" or "package" (default in Java) levels.
Essentially, everything in a Python class is public.
This makes sense when Python is used as scripting language for quick-and-dirty ad-hoc solutions that you'll probably code once and then throw away (or keep like that without further development).
If you make more involved applications in Python (and that's certainly possible with Python and also done a lot) you'll probably want to distinguish between a class' public interface and its internal implementation details. Python provides two levels of "hiding" internal members (both, functions and data attributes):
_
prefix__
prefixBeginning a name with _
signals to everyone outside a namespace (whether a class or a module or a package):
You shouldn't access this, unless you know what you're doing. And I (the implementor of stuff in that namespace) may change that at will, so you probably don't know what you will be doing by accessing it. Stuff may break. And if it does, it'll be your (the one accessing it) fault, not mine (the one implementing it). This member isn't a part of this namespace's public interface.
Yes, you can access it. That doesn't mean that you should. We're all adults here. Be responsible.
And you should adhere to that, even if you'd happen to not be an adult, yet.
Beginning a name with __
signals to everyone outside a namespace (whether a class or a module or a package):
The same as with
_
applies, only, you know, even stronger!
Additionally, and only if the namespace is a class (and the attribute name ends in no more than one underscore):
To make sure you don't access these things from outside by accident, Python "mangles" the names of these attributes for access from outside the class. The resulting name is perfectly predictable (it's _
+ (simple) class name + original attribute name), so you can still access these things, but you most certainly won't simply by mistake.
Also, this can help avoid name collisions between members of base classes and members of their subclasses. (Though, it won't work as intended if the classes share the same class name, as the "simple class name" is used, not including modules and packages.)
In either case, you may have good reasons to access these values anyway (e.g. for debugging) and Python doesn't want to stand in your way when you do (or with name mangling, at most only slightly so.)
So, as there is no real private in Python, we can't apply the pattern/style from Java and C++. But we might still need stable interfaces to do serious programming.
Good thing that in Python you can replace a data attribute with methods, without having to change its users. Pils19's answer provides an example:
class Fruit(object):
def __init__(self, color):
self._color = color
@property
def color(self):
return self._color
(Documentation of this decorator here.)
If we also provide a property-setter-method and a property-deleter-method ...
class Fruit(object):
def __init__(self, color):
self._color = color
@property
def color(self):
return self._color
@color.setter
def color(self, c):
self._color = c
@color.deleter
def color(self):
del self._color
Then this will act equivalent to a simple data attribute:
class Fruit(object):
def __init__(self, c):
self.color = c
But now we have all the freedom of methods. We can leave out any of them (most usual is to only have the getter, so you have a read-only attribute), we can give them additional or different behavior, etc.
This is the recommended approach in Python:
_
for implementation detailsI'm assuming [that there is a good practice to define non-property getters and setters in Python], since an MIT professor, whose course I'm taking, does this with his classes and expects students to do the same on their homework.
Are you sure this is what your professor did, or did he use Python's properties mechanism?
If he did, is this a class about Python or does it just so happen that Python is used for the examples (and that your professor also used it to demonstrate something actually only applicable to other languages)?
And let's not forget: Even MIT professors might be forced to teach classes where they aren't experts on every aspect of the subject.
Upvotes: 1
Reputation: 1479
Normally it's a good practice to you the @Property
decorator. And have the internal properties with an single leading underscore. For you example it would look like:
class Fruit(object):
def __init__(self, color):
self._color = color
@property
def color(self):
return self._color
Upvotes: 0