Home > Blockchain >  Apply function or property to multiple methods of same class
Apply function or property to multiple methods of same class

Time:10-09

In the following example, the class MyExample read a list of filenames which have underscores in their file names.

class MyExample(object):
    def __init__(self, location: str):
        self.location = location

    def get_file_names(self):
        # For simplicity, lets just return an example of the output
        return ["my_file_name_01.txt", "my_file_name_02.txt"]

    def get_file_name_number(self):
        # Idem as before. 
        return ["file_name_01.txt", "file_name_02.txt"]

    def get_file_size(self):
        # It does not return a list of strings so the desired 
        # property or function will not be applied here
        return [3800, 4000]

When the code is executed, the results are

my_object = MyExample("./a/random/path")
print(my_object.get_file_names())
print(my_object.get_file_name_number())
print(my_object.get_file_size())

# The results:
['my_file_name_01.txt', 'my_file_name_02.txt']
['file_name_01.txt', 'file_name_02.txt']
[3800, 4000]

Now, I would like to find a way to apply a function or property (let's call it to_dot) that can replace the underscores for dots from the output of get_file_names() and get_file_name_number.

The final code should have to return something like

# Calling this function with .to_dots
my_object.get_file_names().to_dots
["my.file.name.01.txt", "my.file.name.02.txt"] # <-- The desired output

# Calling this function with .to_dots
my_object.get_file_name_number().to_dots
["file.name.01.txt", "file.name.02.txt"] # <-- The desired output

# Calling this function with .to_dots
my_object.get_file_name_number().to_dots
AttributeError #  # <-- The desired output ... or something similar

Is there a way to add the to_dots to the class MyExample in order to replace the underscores of some of the methods inside?
I am not very familiar with decorators but I suspect there could be a trick to do that. So far I have unsuccessfully tried with @property but as far as I know, there are many types of decorators.... Or maybe it can be done without decorators and I am very lost. Thank you.

CodePudding user response:

Define your own decorator:

def to_dots(func):
    def inner():
        original_return = func()
        return [item.replace('_','.') for item in original_return ]
    return inner

And use it as :

@to_dots
def get_file_names(self):

CodePudding user response:

You could do something like this:

from collections import UserList

class MyList(UserList):
    @property
    def to_dots(self):
        return [s.replace("_", ".") for s in self.data]

class MyExample(object):
    def __init__(self, location: str):
        self.location = location

    def get_file_names(self):
        return MyList(["my_file_name_01.txt", "my_file_name_02.txt"])

Result for

my_object = MyExample("./a/random/path")
print(my_object.get_file_names())
print(my_object.get_file_names().to_dots)

is

['my_file_name_01.txt', 'my_file_name_02.txt']
['my.file.name.01.txt', 'my.file.name.02.txt']

CodePudding user response:

Why not pass the required output format (dotted or not) as optional argument to those methods?

class MyExample(object):
    def __init__(self, location: str):
        self.location = location

    @staticmethod
    def map_list(lst, dotted=False):
        return [s.replace("_", ".") for s in lst] if dotted else lst

    def get_file_names(self, dotted=False):
        return self.map_list(["my_file_name_01.txt", "my_file_name_02.txt"], dotted)

    def get_file_name_number(self, dotted=False):
        return self.map_list(["file_name_01.txt", "file_name_02.txt"], dotted)

    def get_file_size(self):
        return [3800, 4000]

my_object = MyExample("./a/random/path")
print(my_object.get_file_names())  # Underscores untouched
print(my_object.get_file_name_number(dotted=True))  # Underscores replaced
print(my_object.get_file_size())

CodePudding user response:

You can use a class decorator to apply a function decorator to the specified class methods.

from functools import wraps
import inspect


def dec_methods(decorator, *members):
    """Class decorator to apply specfied decorator to specified members of class."""

    def dec_the_class(cls):
        for name, m in inspect.getmembers(cls, inspect.isfunction):
            if name in members:
                setattr(cls, name, decorator(m))
        return cls
    return dec_the_class


def to_dots(func):
    """Function decorator to replace '_' with dots in list of strings returned."""

    @wraps(func)
    def wrapped(*args, **kwargs):
        results = func(*args, **kwargs)
        results = [result.replace('_', '.') for result in results]
        return results
    return wrapped


@dec_methods(to_dots, 'get_file_names', 'get_file_name_number')
class MyExample(object):
    def __init__(self, location: str):
        self.location = location

    def get_file_names(self):
        # For simplicity, lets just return an example of the output
        return ["my_file_name_01.txt", "my_file_name_02.txt"]

    def get_file_name_number(self):
        # Idem as before.
        return ["file_name_01.txt", "file_name_02.txt"]

    def get_file_size(self):
        # It does not return a list of strings so the desired
        # property or function will not be applied here
        return [3800, 4000]


my_object = MyExample("./a/random/path")
print(my_object.get_file_names())        # -> ['my.file.name.01.txt', 'my.file.name.02.txt']
print(my_object.get_file_name_number())  # -> ['file.name.01.txt', 'file.name.02.txt']
print(my_object.get_file_size())         # -> [3800, 4000]

  • Related