我有以下类和类变量:
class MyClass:
class_var_1 = "a"
class_var_2 = run_class_method()
@classmethod
def run_class_method(cls):
return "ran class method"
但是,解释器说run_class_method
没有定义。使用MyClass.run_class_method()
也不起作用。来自Java背景,我不明白为什么这行不通。那么,我该如何解决?
此外,我发现,如果我在类末尾定义类变量,则此方法有效。这在python中被认为是不好的做法吗?
答案 0 :(得分:2)
MyClass
的类属性仍在定义时,尚未定义,因此在定义class_var_2
时,MyClass
尚不可用。您可以通过在class_var_2
定义块之后定义MyClass
来解决此问题:
class MyClass:
class_var_1 = "a"
@classmethod
def run_class_method(cls):
return "ran class method"
MyClass.class_var_2 = MyClass.run_class_method()
答案 1 :(得分:2)
python中的类主体是可执行上下文,与仅包含声明的Java不同。这最终意味着在类定义中执行顺序很重要。
引用文档:
类定义是一个可执行语句。
...
然后,使用新创建的本地名称空间和原始的全局名称空间,在新的执行框架中执行该类的套件(请参阅命名和绑定)。 (通常,该套件主要包含函数定义。)当该类的套件完成执行时,其执行框架将被丢弃,但其本地名称空间将被保存。 [4]然后,使用基类的继承列表和属性字典的已保存本地名称空间创建类对象。类名绑定到原始本地名称空间中的该类对象。
如果您要调用函数来定义类变量,则可以通过以下方式之一进行操作:
使用静态方法:
class MyClass:
def _run_instance_method():
return "ran instance method"
run_instance_method = staticmethod(_run_instance_method)
class_var_1 = "a"
class_var_2 = _run_instance_method() # or run_instance_method.__func__()
或将其定义为独立函数:
def run_method():
return "ran method"
class MyClass:
class_var_1 = "a"
class_var_2 = run_method()
# optional
run_method = staticmethod(run_method)
或使用__func__
访问原始函数并提供一个虚拟cls
值:
class MyClass:
@classmethod
def run_class_method(cls):
return "ran class method"
class_var_1 = "a"
class_var_2 = run_class_method.__func__(object())
或在创建类后设置类变量:
class MyClass:
@classmethod
def run_class_method(cls):
return "ran class method"
class_var_1 = "a"
MyClass.class_var_2 = MyClass.run_class_method()
答案 2 :(得分:1)
首先要注意的是Java没有类方法。它具有静态方法和常规方法。常规方法将作为参数的实例从其接收。从该类(不是在其上定义的类)中调用该类的类方法作为参数。静态方法没有什么特别的,它的行为类似于普通函数-静态方法只是将逻辑相关方法进行分组的一种方式。
要注意的第二件事是将Java类定义解析为单独的类定义和隐式静态构造函数。初始化类属性时,这使您可以在类主体中定义方法之前调用它们。这是因为在实际程序中,只有在创建类/将类加载到内存之后才调用这些语句。在Python中没有这种区别。相反,要创建一个类,可以在一个专门的名称空间内执行一系列语句,然后将其用于创建该类。就像在函数或代码块的主体中一样,您不能在变量存在之前使用它。这包括在类主体中使用该类(因为它尚不存在!)
例如这是有效的Java:
class X {
static int i = 1;
static X obj = newInstance();
// ^-- executed after the class has been created, but is still being initialised.
static X newInstance() {
return new X();
}
}
但这不是有效的Python
class X:
val = 1
obj = new_instance()
# ^-- We're still in the body of X, and neither new_instance nor X has been created yet
@classmethod
def new_instance(cls):
return cls()
# even if new_instance was defined before obj, Python still wouldn't be able to fill
# in the cls argument as X still doesn't exist when new_instance is first invoked
在Python中,您必须明确地对类进行静态构造。请记住,这正是Java中会发生的事情,只是隐藏在语法糖之后。
class X:
val = 1 # this can still be done in the class body as it doesn't need the class
obj = None # not necessary, but can help type checkers know that X has an
# attribute obj -- you can use type annotations to further help
@classmethod
def new_instance(cls):
return cls()
# explicit class initialisation of attributes
X.obj = X.new_instance()
答案 3 :(得分:0)
执行此操作的另一种方法是定义一个父类,该父类可以控制其子类(或元类)的创建。下面,我们在父类中使用__init_subclass__
在类创建期间设置属性。
class InitVar():
def __init_subclass__(cls, varname, funcname, **kwargs):
class_method = getattr(cls, funcname)
setattr(cls, varname, class_method())
class MyClass(InitVar, varname="class_var_2", funcname="run_class_method"):
class_var_1 = "a"
@classmethod
def run_class_method(cls):
return "ran class method"
print(MyClass.class_var_2)
# ran class method