This article demonstrates more advanced features of Python classes and their customisation. Specifically, how enum objects are created, class to strings, and computed attribute.
Class string values
A string is typically an informal representation of the object. However, it’s just a nicely in human-readable form. Python provides 2 common methods, i.e. str() and repr(). We can customise the two built-in methods to display the information that we want from an object.
class Person():
def __init__(self):
self.fname = "Joe"
self.lname = "Marini"
self.age = 25
# use __repr__ to create a string useful for debugging
def __repr__(self):
return "<Person Class - fname:{0}, lname:{1}, age{2}>".format(self.fname, self.lname, self.age)
# use str for a more human-readable string
def __str__(self):
return "Person ({0} {1} is {2})".format(self.fname, self.lname, self.age)
# use bytes to convert the informal string to a bytes object
def __bytes__(self):
val = "Person:{0}:{1}:{2}".format(self.fname, self.lname, self.age)
return bytes(val.encode('utf-8'))
Now if we create a Person object and print it out using these functions:
# create a new Person object
cls1 = Person()
# use different Python functions to convert it to a string
print(repr(cls1))
print(str(cls1))
print("Formatted: {0}".format(cls1))
print(bytes(cls1))
We will have the following output:
#<Person Class - fname:Joe, lname:Marini, age25>
#Person (Joe Marini is 25)
#Formatted: Person (Joe Marini is 25)
#b'Person:Joe:Marini:25'
str()
and repr()
both are used to get a string representation of an object. But they have the following differences:
- str() is used for creating output for end user while repr() is mainly used for debugging and development. repr’s goal is to be unambiguous and str’s is to be readable. For example, if we suspect a float has a small rounding error, repr will show us while str may not.
- repr() compute the “official” string representation of an object (a representation that has all information about the object) and str() is used to compute the “informal” string representation of an object (a representation that is useful for printing the object).
- The print statement and str() built-in function uses __str__ to display the string representation of the object while the repr() built-in function uses __repr__ to display the object.
repr | str |
---|---|
s = 'Hello, Petamind.' print(repr(s)) print(repr(2.0/11.0)) | s = 'Hello, Petamind.' print(str(s)) print(str(2.0/11.0)) |
Hello, Petamind. 0.181818181818 | ‘Hello, Petamind.’ 0.18181818181818182 |
Defining enumerations
Python supports enumerations just like other popular programming languages and they’re useful in a variety of scenarios. Usually, they are used to assign easy-to-read names to constant values in a program. Also, you can iterate over them like you would other iterable in Python.
We define enumerations using the class syntax. In the following code, we create an enum type Fruit of unique value with @unique
annotation. We can use auto()
function to automatically assign values to enums.
# define enumerations using the Enum base class
from enum import Enum, unique, auto
@unique
class Fruit(Enum):
APPLE = 1
BANANA = 2
ORANGE = 3
TOMATO = 4
PEAR = auto()
Enums have human-readable values and types. Let print their values and types:
print(Fruit.APPLE)
#Fruit.APPLE
print(type(Fruit.APPLE))
#<enum 'Fruit'>
print(repr(Fruit.APPLE))
#<Fruit.APPLE: 1>
# enums have name and value properties
print(Fruit.APPLE.name, Fruit.APPLE.value)
# print the auto-generated value
print(Fruit.PEAR.value)
#5
Enums are hashable so we can use them as unique keys of a dictionary.
myFruits = {}
myFruits[Fruit.BANANA] = "Come Mr. Tally-man"
print(myFruits[Fruit.BANANA])
#Come Mr. Tally-man
Computed ATTRIBUTES
Python provides a set of methods that classes can use to access the attributes of an object. Whenever we retrieve or set an object’s attributes, Python calls one of these functions to perform any desired processing.
The first two, getattribute
and getattr
, are called to retrieve an attribute value. These are slightly different from each other. Getattr
is called only when the requested attribute can’t be found on the object. Meanwhile, getattribute
is called unconditionally every time an attribute name is requested. Additionally, there are setattr
, delattr
, and dir
to set, delete and discover supported attributes.
class myColor():
def __init__(self):
self.red = 50
self.green = 75
self.blue = 100
# use getattr to dynamically return a value
def __getattr__(self, attr):
if attr == "rgbcolor":
return (self.red, self.green, self.blue)
elif attr == "hexcolor":
return "#{0:02x}{1:02x}{2:02x}".format(self.red, self.green, self.blue)
else:
raise AttributeError
# use setattr to dynamically return a value
def __setattr__(self, attr, val):
if attr == "rgbcolor":
self.red = val[0]
self.green = val[1]
self.blue = val[2]
else:
super().__setattr__(attr, val)
# use dir to list the available properties
def __dir__(self):
return ("rgbolor", "hexcolor")
The above code modifies the attribute related methods to get colour information. As user passes a supported attribute name, e.g. `rgbcolor`, python will return the value corresponding to the name.
# create an instance of myColor
cls1 = myColor()
# print the value of a computed attribute
print(cls1.rgbcolor)
#(50, 75, 100)
print(cls1.hexcolor)
#(50, 75, 100)
Click here for other advanced python topics.