本文共 7814 字,大约阅读时间需要 26 分钟。
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,我们用的 Django程序中的settings配置文件,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
在 Python 中,我们可以用多种方法来实现单例模式:
基于new方法实现:
class Settings(object): _instance = None def __new__(cls, *args, **kw): if not cls._instance: cls._instance = super(Settings, cls).__new__(cls, *args, **kw) return cls._instances1=Settings()s2=Settings()#Settings类实例化出来的所有对象都是相同的对象
基于模块导入的方式实现
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,模块中的代码会执行,同时会生成一个 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
要想弄清楚模块导入实现单例模式,想搞清楚一下三个例子//mymodel.py文件class Person: def walk(self,x): print(f'向前走{x}米')wangfei = Person()
//text.py文件from mymodel import wangfeiprint(id(wangfei.walk(100)))from mymodel import wangfeiprint(id(wangfei.walk(1000)))
输出:
向前走100米1721431248向前走1000米1721431248
验证了这句:模块中的代码会执行,同时会生成一个 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。
//mymodel.py文件class Person: def walk(self,x): print(f'向前走{x}米')wangfei = Person()
//text.pyfrom mymodel import wangfei,Personprint(id(wangfei))print(id(Person()))print(id(Person()))
输出:
188009176101618800916204081880089942224
导入模块中实例化的对象和导入模块中的类,再通过类实例化得到的对象都是不同的对象。
//mymodel.py文件class Person: def walk(self,x): print(f'向前走{x}米')wangfei = Person()
//text2.pyfrom mymodel import wangfeidef func(): print(id(wangfei))
//text.pyfrom mymodel import wangfeifrom text2 import funcprint(id(wangfei))func()
结果:
28297048097202829704809720
在一个程序中,即便是在不同的py文件中导入同一个对象,它们其实也是同一个对象,第二次导入同样是直接加载第一次导入生成的 .pyc 文件。
def autodiscover(): autodiscover_modules('admin', register_to=site)
from django.contrib import adminfrom blog import modelsclass UserInfo(admin.ModelAdmin): list_display = ("username", "phone", "email", 'blog')admin.site.register(models.UserInfo, UserInfo)class ArticleAction(admin.ModelAdmin): list_display = ['title', 'user']admin.site.register(models.Article, ArticleAction)admin.site.register(models.Article2Tag)admin.site.register(models.ArticleDetail)admin.site.register(models.ArticleUpDown)admin.site.register(models.Blog)admin.site.register(models.Category)admin.site.register(models.Comment)admin.site.register(models.Tag)
class AdminSite(object): .........site = AdminSite()
注意:这里应用的是一个单例模式(通过python模块导入的方式实现的),对于AdminSite类的一个单例模式,执行的每一个app中的每一个admin.site都是一个对象
admin.site.register(models.UserInfo, UserInfo)admin.site.register(models.Article, ArticleAction)admin.site.register(models.Article2Tag)admin.site.register(models.ArticleDetail)admin.site.register(models.ArticleUpDown)admin.site.register(models.Blog)admin.site.register(models.Category)admin.site.register(models.Comment)admin.site.register(models.Tag)
def register(self, model_or_iterable, admin_class=None, **options): """ Registers the given model(s) with the given admin class. """ if not admin_class: admin_class = ModelAdmin
这段源码的逻辑是判断register中是否传入了admin_class参数,如果没有传,就是用默认的参数ModelAdmin。
延伸出来上面在自定义admin_class时为什么必须继承admin.ModelAdmin?
这是因为admin.ModelAdmin中的参数有很多,我们在自定义时不可能将所有参数都修改,或者重写,那样就太麻烦了。主动继承admin.ModelAdmin,我们只需要修改我们想要设置的参数,其他的就可以使用原来的参数了。# Instantiate the admin class to save in the registry self._registry[model] = admin_class(model, self)
register这个方法的最后这段源码是将admin_class类传入model来实例化对象完成注册!!!
首先应该知道Django中的路由分发的设置方式现在了解两种:
from django.conf.urls import url, includeurl(r'^blogcenter/', include(blogcenter_urls)),
blogcenter_urls.py↓
from django.conf.urls import urlfrom blog import viewsurlpatterns = [ url(r'^backend/$',views.Backend.as_view()),]
url(r"blogcenter/", ([ url(r"article/", ([ url(r"del_article/", del_article), url(r"edit_article/", edit_article), url(r"add_article/", add_article), ], None, None)), url(r"backend/", views.backend), ], None, None))
接下来看一下Django初始配置的url:
url(r'^admin/', admin.site.urls),
通过源码可以看出urls是site对象可以调用的静态方法,而site对象是由AdminSite类实例化出来的,从而就可以找到urls的归属地了。
一下为源码:@property def urls(self): return self.get_urls(), 'admin', self.name
可以看出urls是AdminSite类的静态方法,它的返回值是一个元组,再看self.get_urls()是什么?
def __init__(self, name='admin'): self._registry = {} # model_class class -> admin_class instance .......def register(self, model_or_iterable, admin_class=None, **options): """ Registers the given model(s) with the given admin class. """ if not admin_class: admin_class = ModelAdmin ........ #将每个admin_class传入对应model实例化从而注册,以model模型类 为键, #对应的model配置类对象为值得形式添加到self._registry中。 self._registry[model] = admin_class(model, self)def get_urls(self): # Add in each model's views, and create a list of valid URLS for the # app_index valid_app_labels = [] for model, model_admin in self._registry.items(): urlpatterns += [ url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)), ] if model._meta.app_label not in valid_app_labels: valid_app_labels.append(model._meta.app_label) ....... return urlpatterns
配置类中的get_url(),和urls方法
class ModelAdmin(BaseModelAdmin): "Encapsulates all admin options and functionality for a given model." list_display = ('__str__',) list_display_links = () list_filter = () list_select_related = False list_per_page = 100 list_max_show_all = 200 list_editable = () ...... def __init__(self, model, admin_site): self.model = model self.opts = model._meta self.admin_site = admin_site super(ModelAdmin, self).__init__() def get_urls(self): from django.conf.urls import url def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) wrapper.model_admin = self return update_wrapper(wrapper, view) info = self.model._meta.app_label, self.model._meta.model_name urlpatterns = [ url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info), url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info), url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info), url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info), url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info), # For backwards compatibility (was the change url before 1.9) url(r'^(.+)/$', wrap(RedirectView.as_view( pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info) ))), ] return urlpatterns @property def urls(self): return self.get_urls()
通过以上简略源码可以了解到几点:
转载地址:http://yxvml.baihongyu.com/