Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Classmethod: Using a function within a function

Tags:

python

I have a situation where I'm using @classmethod to create a constructor for a class. Within this constructor, a function gets called, which then in turn calls another function. But either this doesn't work or (more probably) I'm doing something to make it not work. Here's an example in miniature:

class testclass:
    def __init__(self, x):
        self.x = x

    @classmethod
    def constructor(cls, x):
        adj_x = cls.outer_adjust(cls, x)
        return testclass(adj_x)

    def outer_adjust(self, x):
        return self.inner_adjust(x)

    def inner_adjust(self, x):
        return x + 1

test_instance = testclass.constructor(4)

This produces an error message:

inner_adjust() missing 1 required positional argument: 'x'

I can make it work by explicitly passing self to inner_adjust, eg

def outer_adjust(self, x):
    return self.inner_adjust(self, x)

But this then means that the outer_adjust method can't be used outside of the constructor, which is not what I want.

Any assistance gratefully received.

Here's a more detailed example, with two constructors shown. I'm trying to follow the approach to constructors described in What is a clean, pythonic way to have multiple constructors in Python? Which is essentially that the constructors do some processing to figure out what variables they should pass to init when instantiating the class. Both constructors give the same error:

if_char_is_z_make_it_a() missing 1 required positional argument: 'char_input'

As before, I need to be able to use the if_char_is_make_it_a function outside of the constructor (ie, when using the class normally).

class testclass:
    def __init__(self, char):
        self.char = char

    @classmethod
    def constructor_from_int(cls, int_input):
        as_char = chr(int_input)
        char = cls.process_char(cls, as_char)
        return testclass(char)

    @classmethod
    def constructor_from_char(cls, char_input):
        char = cls.process_char(cls, char_input)
        return testclass(char)

    def process_char(self, char_input):
        processed_char = '(' + char_input + ')'
        output_char = self.if_char_is_z_make_it_a(processed_char)
        return output_char

    def if_char_is_z_make_it_a(self, char_input):
        if char_input == '(z)':
            return '(a)'
        return char_input

test_instance = testclass.constructor_from_char('a')
like image 950
Chris Harris Avatar asked Jun 27 '26 00:06

Chris Harris


1 Answers

When you call cls.outer_adjust from constructor you are calling the unbound outer_adjust method.

Thus, you pass the class itself as self and not an instance to a method that expects to receive an instance as argument.

Although, there is no real reason to have a constructor method. This is exactly what __init__ is for.

class testclass:
    def __init__(self, x):
        self.x = self.outer_adjust(x)

    def outer_adjust(self, x):
        return self.inner_adjust(x)

    def inner_adjust(self, x):
        return x + 1

test_instance = testclass(4)

If you absolutely need the transformation on x to be done before the instantiation, then use __new__ instead. Although, this is generally not necessary.

Multiple constructors

If for some reason you still need to have a constructor method, by example if you want multiple constructors. Then keep in mind that outer_adjust and inner_adjust are instance methods, this means they must be called after you have created an instance.

class testclass:
    def __init__(self, x):
        self.x = x

    @classmethod
    def constructor1(cls, x):
        instance = cls(x)
        instance.outer_adjust()
        return instance

    @classmethod
    def constructor2(cls, x):
        instance = cls(x)
        instance.inner_adjust()
        return instance

    def outer_adjust(self):
        print('Do something else')
        return self.inner_adjust()

    def inner_adjust(self):
        self.x += 1

As a sidenote, notice how I did not need to call testclass, but simply called cls in the constructor methods. Since this is a class method, we do not need to explicitly name the class. This is better, especially if you are to use inheritance.

like image 143
Olivier Melançon Avatar answered Jul 01 '26 19:07

Olivier Melançon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!