学习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中的方法。
静态方法无法访问实例属性,但是能访问类属性,类似一种工具函数,能简化或者加速类属性的访问。