简体   繁体   中英

Calling class/static method and assigning to an instance variable

I'm trying to create a class/static method within a class. I need SignInForm to have the defined instance variables, as they are rendered by Django's template. How does one call a method within a class from an instance variable? My objective is to update each variable with the custom widget for creating a consistent style.

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

Error:

name 'SignInForm' is not defined

First, the short version:

You can't call methods of a class—even classmethods and staticmethods—while you're in the middle of defining that class, at least not easily.

So, what can you do? Well, really, you don't want a class method or a static method here. You want a regular old function. Like this:

class Spam(object):
    def _func():
        return 42

    class_attr = _func()

    del _func

After the class is defined, _func would be an instance method—and an un-callable instance method, since it doesn't take a self . (That's why I prefixed it with an underscore, and also del 'd it, to make it harder to accidentally call it later…)

But while it's being defined, it's a normal function, and can be called as such.


I should mention that usually, wanting to do this is a sign that there's something off about your design, and the thing you're trying to write as a classmethod or staticmethod should actually be a method of a base class, or a free function or a class constructor, or maybe even a metaclass method.

As David Sanders' answer explains, the specific thing you're trying to do is common enough that there's an idiomatic way to write it: as a class constructor for a TextField subclass.


How does this work?

Clearly, while you're in the middle of executing the Spam class definition, there is nothing called Spam , so you definitely can't call Spam._func .

But why can you call _func ? You have to understand how class definitions are executed. Oversimplifying a bit: Python creates an empty global dictionary, runs all the code inside the class definition as if it were a script, then it goes back to the real globals and runs Spam = type('Spam', (object,), that_global_dict) . So when we do class_attr = _func() , we're inside that temporary global environment, and _func is a function in that environment, so we can call it.

So, why can't we do this with a classmethod or staticmethod ?

For one thing, you need a cls object to call a classmethod , and we don't have one.

For another, classmethod and staticmethod objects aren't callable. Function objects are callable as themselves, and they're also descriptors that can be used to construct bound methods. Class and static methods are not callable, they're just descriptors that can be used to construct bound methods in special ways (bound to a class or to nothing, instead of to an instance).


So, what if you wanted to write something that was usable as a class-definition-time function, and also as a static method later? The simplest way is:

class Spam(object):
    def _func():
        return 42
    smeth = staticmethod(_func)

    class_attr = func()

    del _func

A decorator like @staticmethod just does, in effect, _func = staticmethod(_func) . We can do the same thing, but give the result a different name, and now we've got a static method, while still having the original function to call directly.

While abernet's answer does a good job of explaining why you were getting that error, the idiomatic way to do this with django would be something like this:

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': ''})
    # ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM