调用类/静态方法并赋值给实例变量
我正在尝试在一个类里面创建一个类方法或静态方法。我需要这个叫做 SignInForm 的表单有一些特定的实例变量,因为这些变量是通过 Django 的模板来显示的。请问,怎么才能从一个实例变量里调用这个类的方法呢?我的目标是用自定义的控件来更新每个变量,以便保持风格的一致性。
class SignInForm(forms.Form):
@classmethod
def get_text_input_with_attributes():
return forms.TextInput(attrs= {'class':'form-style'})
first_name = forms.CharField(max_length=50,required=True)
first_name.widget = SignInForm.get_text_input_with_attributes()
last_name = forms.CharField(max_length=50,error_messages={'required': ''})
last_name.widget = SignInForm.get_text_input_with_attributes()
....lots of other custom fields
错误信息:
名称 'SignInForm' 未定义
2 个回答
虽然abernet的回答很好地解释了你为什么会遇到那个错误,但在Django中,比较标准的做法应该是这样的:
from django import forms
class SignInFormCharField(forms.CharField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('widget', forms.TextInput(attrs={'class': 'form-style'}))
super(SignInFormCharField, self).__init__(*args, **kwargs)
class SignInForm(forms.Form):
first_name = SignInFormCharField(max_length=50, required=True)
last_name = SignInFormCharField(max_length=50, error_messages={'required': ''})
# ...
首先,简单来说:
在定义一个类的时候,你不能调用这个类的方法——即使是类方法和静态方法——至少不容易做到。
那么,你可以做什么呢?其实,这里你不需要类方法或静态方法。你需要的是一个普通的函数。像这样:
class Spam(object):
def _func():
return 42
class_attr = _func()
del _func
在类定义完成后,_func
会变成一个实例方法——但它是一个不能被调用的实例方法,因为它不接受self
。所以我在前面加了一个下划线,并且还用del
把它删除了,这样就不容易在后面意外调用它了……
但是在定义的时候,它是一个普通的函数,可以像普通函数那样被调用。
我应该提到,通常想这样做说明你的设计有点问题,你想写成类方法或静态方法的东西,实际上应该是一个基类的方法,或者是一个自由函数,或者是一个类构造函数,甚至可能是一个元类的方法。
正如David Sanders的回答所解释的,你想做的事情是很常见的,有一种习惯用法可以写成:作为TextField
子类的类构造函数。
这怎么运作的呢?
显然,当你正在执行Spam
类的定义时,Spam
这个名字并不存在,所以你肯定不能调用Spam._func
。
但是为什么可以调用_func
呢?你需要理解类定义是如何执行的。简单来说:Python会创建一个空的全局字典,把class
定义里面的所有代码当作脚本来运行,然后再回到真正的全局环境,执行Spam = type('Spam', (object,), that_global_dict)
。所以当我们执行class_attr = _func()
时,我们是在那个临时的全局环境中,而_func
就是那个环境里的一个函数,所以我们可以调用它。
那么,为什么我们不能用classmethod
或staticmethod
呢?
首先,你需要一个cls
对象来调用classmethod
,而我们没有这个对象。
其次,classmethod
和staticmethod
对象是不可调用的。函数对象可以直接被调用,它们也是可以用来构造绑定方法的描述符。而类方法和静态方法不可直接调用,它们只是描述符,可以用特殊的方式构造绑定方法(绑定到类或不绑定,而不是绑定到实例)。
那么,如果你想写一个既可以在类定义时使用,又可以作为静态方法使用的东西,最简单的方法是:
class Spam(object):
def _func():
return 42
smeth = staticmethod(_func)
class_attr = func()
del _func
像@staticmethod
这样的装饰器实际上是做了_func = staticmethod(_func)
。我们可以做同样的事情,但给结果一个不同的名字,这样我们就得到了一个静态方法,同时还保留了原始函数可以直接调用。