I have a class which I use to handle three types of data structures. In this class I have many plotting methods, which depends on which type of data is loaded into the class. Is there a way for me to hide the methods not belonging to the data structure loaded, when looking at the class attributes?
Example:
class data_reader():
def __init__(self):
self.load_data()
self.data_type()
self.common_method_1()
self.common_method_2()
def load_data(self):
# Loads the data
def data_type(self):
# Figures out which of the three data structures we have
def common_method_1(self):
# A method common for all data structures
def common_method_2(self):
# Another method common for all data structures
def plot_data_1(self):
# Plotting function for data structure 1
def plot_data_2(self):
# Plot function for data structure 2
def plot_data_3(self):
# Plot function for data structure 3
if __name__ == "__main__":
a = data_reader()
a.plot_data_1()
When I check the methods of the class I can see all plot functions. If I load data structure 1, can I then hide the other two plotting function?
I tried to do some inner functions, but then it did not become a callable method outside the class.
Thank you for any inputs.
CodePudding user response:
Methods not relevant to the actual object shouldn't be hidden. They should not be there in the first place. So instead of making an "universal" object for different data types, extract common logic into the parent class and inherit to specific child classes that define behaviour specific to data types.
Of course, this requires you to know the data type ahead of runtime. If you can only decide that when you actually load the data, I suggest implementing Factory pattern - a separate class that handles the logic of deciding which object to create.
class data_reader():
def __init__(self, data):
self.common_method_1()
self.common_method_2()
def common_method_1(self):
pass
def common_method_2(self):
pass
class data_reader_1(data_reader):
def plot_data_1(self):
pass
# Plotting function for data structure 1
class data_reader_2(data_reader):
def plot_data_2(self):
pass
# Plot function for data structure 2
class data_reader_factory():
def load_data(self):
self.data = 42
# Loads the data
def data_type(self):
self.type = "1"
# Figures out which of the three data structures we have
def create_reader(self):
if self.type == "1":
return data_reader_1(self.data)
elif self.type == "2":
return data_reader_2(self.data)
# Creates an object of correct type
if __name__ == "__main__":
a = data_reader_factory().create_reader()
a.plot_data_1()
CodePudding user response:
My approach to this depend on how "different" your various data structures are:
V1: Not so different, e.g, nested list vs numpy array. In this case I would advise you to write different data load functions, which always convert the data to a common format, e.g.:
def load_list_data(self, data):
# reads a list and converts it to a numpy array
def load_npy_array_data(self, data):
# reads a numpy array which does not need to be converted
def plot_data(self):
# plots a numpy array
V2: Data differs a lot, e.g., point cloud vs. image. Then use two subclasses and a factory method:
class data_reader():
def __init__(self):
self.load_data()
self.data_type()
self.common_method_1()
self.common_method_2()
def load_data(self):
# Loads the data
def data_type(self):
# Figures out which of the three data structures we have
def common_method_1(self):
# A method common for all data structures
def common_method_2(self):
# Another method common for all data structures
class img_data_read(data_reader):
def plot_data(self):
# Plotting function for data structure 1
class pointcloud_data_read(data_reader):
def plot_data(self):
# Plotting function for data structure 1
def data_reader_factory(data):
if isinstance(data, IMG_DATA_TYPE):
return img_data_read()
elif isinstance(data, POINTCLOUD_DATA_TYPE):
return pointcloud_data_read()
Then you can do:
if __name__ == "__main__":
a = data_reader_factory()
a.plot_data()
CodePudding user response:
Per the comment of matszwecja
:
Put all common methods in the base class data_reader
and then create subclasses of this class that override method plot_data
. The base class no longer needs to know about the various types of plotting. The correct plot_data
method will be called based on what the actual class is. This is a basic technique in object-oriented programming. See, for example, Object-Oriented Programming: Polymorphism in Python.
from abc import ABC, abstractmethod
# This is an abstract class: method plot_data
# must be overriden in a subclass:
class data_reader(ABC):
def __init__(self):
self.load_data()
self.common_method_1()
self.common_method_2()
def load_data(self):
# Loads the data
...
def common_method_1(self):
# A method common for all data structures
...
def common_method_2(self):
# Another method common for all data structures
...
# This method must be overridden in a subclass:
@abstractmethod
def plot_data(self):
pass
class data_reader_1(data_reader):
def plot_data(self):
# Plotting function for data structure 1
...
class data_reader_2(data_reader):
def plot_data(self):
# Plotting function for data structure 2
...
class data_reader_3(data_reader):
def plot_data(self):
# Plotting function for data structure 3
...
if __name__ == "__main__":
a = data_reader_1() # A specific subclass
a.plot_data()
If load_data
must be specific for each subclass, then make it an abstract method (meaning it must be overridden in a subclass) in the base class and then define it for each subclass.