學習python面向對象的過程中我們會接觸到:類方法、實例方法、靜態方法,這些概念理解起來不算太難。但是為什么要有這些方法,這些方法有哪些應用場景?這里我們就講解一些類方法在django源碼中的應用。
先說結論:
使用類方法的一個用法是為了
- 替代__init__方法。
- 動態的給類添加方法。
用法一
問:為什么要替代__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中的方法。
靜態方法無法訪問實例屬性,但是能訪問類屬性,類似一種工具函數,能簡化或者加速類屬性的訪問。