djang中的python語法002-類方法的使用

學習python面向對象的過程中我們會接觸到:類方法、實例方法、靜態方法,這些概念理解起來不算太難。但是為什么要有這些方法,這些方法有哪些應用場景?這里我們就講解一些類方法django源碼中的應用。

先說結論

使用類方法的一個用法是為了

  1. 替代__init__方法。
  2. 動態的給類添加方法。

用法一

問:為什么要替代__init__,__init__出了什么問題?

答:

__init__方法沒什么問題,無非就是初始化類實例。但是有時候__init__方法需要的參數我們沒有,需要根據現有的變量處理加工之后得出需要的參數,在傳遞給__init__。如果我們每次調用__init__函數之前都進行一大堆邏輯處理,無疑會使代碼冗雜。
這時候我們可以把現有的變量傳遞給類方法,類方法經過處理之后得到__init__方法所需要的參數,然后使用類名初始化實例。在這里類方法是一個工廠模式的應用。

問:這段處理邏輯為什么一定要用類方法實現?

答:

這段邏輯可以不用類方法、不用實例方法實現,語法上沒有任何問題。你可以加工一下現有的變量,然后實例化一個對象。但是從設計上講,創建對象和初始化對象本來是__new__方法和__init__完成的,寫在類里面比較優雅。否定掉實例方法后,類方法是最佳人選。

下面我們看一下類方法在django中一個應用。
/django/apps/registry.py中定義了app類。類中以一個加載app的方法。

    def load_app(self, app_name):
        """
        Loads the app with the provided fully qualified name, and returns the
        model module.
        """
        warnings.warn(
            "load_app(app_name) is deprecated.",
            RemovedInDjango19Warning, stacklevel=2)
        app_config = AppConfig.create(app_name)
        app_config.import_models(self.all_models[app_config.label])
        self.app_configs[app_config.label] = app_config
        self.clear_cache()
        return app_config.models_module

其中

app_config = AppConfig.create(app_name)

就是一個類方法的應用。它并不直接使用

app_config = AppConfig()

這種方式初始化實例,而是把現有的變量直接丟給create方法,create就像一個工廠,干很多臟活累活,最后調用初始化方法。

我們command+b或者ctrl+b進入在/django/apps/config.py看一下這個類方法。代碼中原有注釋已經刪去。函數中細節比較多,大家可以先忽略,重點關注這種設計方式。

    @classmethod
    def create(cls, entry):
        """
        Factory that creates an app config from an entry in INSTALLED_APPS.
        """
        try:
            module = import_module(entry)
        except ImportError:
            module = None
            mod_path, _, cls_name = entry.rpartition('.')
            if not mod_path:
                raise
        else:
            try:
                entry = module.default_app_config
            except AttributeError:
                # 如果是自定義的應用,走下面的邏輯
                return cls(entry, module)
            else:
                mod_path, _, cls_name = entry.rpartition('.')
        mod = import_module(mod_path)
        try:
            cls = getattr(mod, cls_name)
        except AttributeError:
            if module is None:
                import_module(entry)
            else:
                raise
        if not issubclass(cls, AppConfig):
            raise ImproperlyConfigured(
                "'%s' isn't a subclass of AppConfig." % entry)
        try:
            app_name = cls.name
        except AttributeError:
            raise ImproperlyConfigured(
                "'%s' must supply a name attribute." % entry)
        app_module = import_module(app_name)
        #初始化
        return cls(app_name, app_module)

可以看到,這個類方法在在進行初始化之前進行了大量的處理和判斷。
cls(entry, module)和cls(app_name, app_module)是調用初始化方法。cls是類名,類名括號加上兩個參數正是python初始化實例的語法。
初始化方法定義如下,接收兩個參數,一個是字符串類型,一個是模塊類型。

    def __init__(self, app_name, app_module):
        pass

用法二

可以看一下/db/models/manager.py中的兩段代碼

class Manager(BaseManager.from_queryset(QuerySet)):
    pass

下面的類方法屬于BaseManager類

@classmethod
def from_queryset(cls, queryset_class, =None):
    if class_name is None:class_name
        class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
    # queryset_class = QuerySet
    class_dict = {
        '_queryset_class': queryset_class,
    }
    class_dict.update(cls._get_queryset_methods(queryset_class))
    # classname = None          
    # type的三個參數,類名、繼承的父類集合、class的方法與函數綁定
    return type(class_name, (cls,), class_dict)

可見:Manager類仍然繼承于一個經過BaseManager加工后的類,這個加工后的類比BaseManager類多了寫QuerySet中的方法。


靜態方法無法訪問實例屬性,但是能訪問類屬性,類似一種工具函數,能簡化或者加速類屬性的訪問。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容