Classmethod:使用函数中的函数

时间:2021-06-24 22:50:04

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:

我有一种情况,我正在使用@classmethod为类创建构造函数。在这个构造函数中,函数被调用,然后调用另一个函数。但要么这不起作用,或者(更可能)我正在做一些使它不起作用的事情。这是一个缩影的例子:

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

我可以通过明确地将self传递给inner_adjust来使其工作,例如

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.

但这意味着outer_adjust方法不能在构造函数之外使用,这不是我想要的。

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:

这是一个更详细的例子,显示了两个构造函数。我正在尝试遵循在Python中使用多个构造函数的什么是干净,pythonic方式中描述的构造函数的方法?这实际上是构造函数进行一些处理以确定在实例化类时它们应该传递给init的变量。两个构造函数都给出了相同的错误:

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).

和以前一样,我需要能够在构造函数之外使用if_char_is_make_it_a函数(即,正常使用类时)。

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')

3 个解决方案

#1


2  

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

从构造函数调用cls.outer_adjust时,您正在调用未绑定的outer_adjust方法。

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

因此,您将类本身作为self而不是实例传递给期望接收实例作为参数的方法。

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

虽然,没有真正的理由有一个构造函数方法。这正是__init__的用途。

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.

如果你绝对需要在实例化之前完成对x的转换,那么请改用__new__。虽然,这通常是没有必要的。

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.

如果由于某种原因,你仍然需要一个构造函数方法,例如,如果你想要多个构造函数。然后请记住,outer_adjust和inner_adjust是实例方法,这意味着必须在创建实例后调用它们。

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.

作为旁注,请注意我不需要调用testclass,而只是在构造函数方法中调用cls。由于这是一个类方法,因此我们不需要显式地命名该类。这样更好,特别是如果要使用继承。

#2


1  

Basically what you are doing here shall be done via the __new__ which serve as constructor.

基本上你在这里做的事情应该通过作为构造函数的__new__来完成。

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

    def __new__(cls, *args, **kwargs):
        instance = super(testclass, cls).__new__(cls, *args, **kwargs)
        instance.outer_adjust(args[0])
        return instance

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

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

test_instance = testclass(4)

#3


0  

You are abusing self. The point of the class method is to use the cls argument as constructor, instead of explicitly naming the class by testclass(adj_x). Also, during the cls.outer_adjust(cls, x) call, you are passing the class instead of the instance, which happens to work because you are not using any instance attributes.

你在虐待自己。类方法的要点是使用cls参数作为构造函数,而不是通过testclass(adj_x)显式命名类。此外,在cls.outer_adjust(cls,x)调用期间,您正在传递类而不是实例,这恰好因为您没有使用任何实例属性而起作用。

As to your questions, there's no way to avoid the x argument. inner_adjust increases some value by 1, so you must give it something to increase. The idea would be to have

至于你的问题,没有办法避免x参数。 inner_adjust将某个值增加1,所以你必须给它增加一些东西。我的想法是

def constructor(cls, x):
    return cls(x)

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

and then do something like

然后做类似的事情

object= testclass.constructor(12)
object.inner_adjust()

#1


2  

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

从构造函数调用cls.outer_adjust时,您正在调用未绑定的outer_adjust方法。

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

因此,您将类本身作为self而不是实例传递给期望接收实例作为参数的方法。

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

虽然,没有真正的理由有一个构造函数方法。这正是__init__的用途。

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.

如果你绝对需要在实例化之前完成对x的转换,那么请改用__new__。虽然,这通常是没有必要的。

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.

如果由于某种原因,你仍然需要一个构造函数方法,例如,如果你想要多个构造函数。然后请记住,outer_adjust和inner_adjust是实例方法,这意味着必须在创建实例后调用它们。

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.

作为旁注,请注意我不需要调用testclass,而只是在构造函数方法中调用cls。由于这是一个类方法,因此我们不需要显式地命名该类。这样更好,特别是如果要使用继承。

#2


1  

Basically what you are doing here shall be done via the __new__ which serve as constructor.

基本上你在这里做的事情应该通过作为构造函数的__new__来完成。

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

    def __new__(cls, *args, **kwargs):
        instance = super(testclass, cls).__new__(cls, *args, **kwargs)
        instance.outer_adjust(args[0])
        return instance

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

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

test_instance = testclass(4)

#3


0  

You are abusing self. The point of the class method is to use the cls argument as constructor, instead of explicitly naming the class by testclass(adj_x). Also, during the cls.outer_adjust(cls, x) call, you are passing the class instead of the instance, which happens to work because you are not using any instance attributes.

你在虐待自己。类方法的要点是使用cls参数作为构造函数,而不是通过testclass(adj_x)显式命名类。此外,在cls.outer_adjust(cls,x)调用期间,您正在传递类而不是实例,这恰好因为您没有使用任何实例属性而起作用。

As to your questions, there's no way to avoid the x argument. inner_adjust increases some value by 1, so you must give it something to increase. The idea would be to have

至于你的问题,没有办法避免x参数。 inner_adjust将某个值增加1,所以你必须给它增加一些东西。我的想法是

def constructor(cls, x):
    return cls(x)

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

and then do something like

然后做类似的事情

object= testclass.constructor(12)
object.inner_adjust()