思路:
用一個字典存儲beanName和資源
初始化時先將beanName和資源注冊到字典中
然后用一個Dscriptor類根據beanName動態請求資源,從而實現控制反轉
# -*- coding:utf-8 -*-
import os
class BeanFactory:
""" Python版控制反轉 context: 存儲bean的名字和對應的類或者值的字典 allowRepalce: 是否允許替換已經注入的bean """
def __init__(self,allowReplace=False):
"""構造函數 allowReplace:是否允許替換已經注入的bean """
self.context = {}
self.allowReplace = allowReplace
def setBean(self,beanName,resource,*args,**kwargs):
if not self.allowReplace:
assert not beanName in self.context,"該BeanFactory不允許重復注入%r,請修改beanName" % beanName
def call():
"""定義一個函數閉包,如果注入的resource是可調用類型,就將*args和**kwargs傳入並調用該函數,然后將返回值返回 如果是一個不可調用對象,就直接返回 """
if callable(resource):
return resource(*args,**kwargs)
else:
return resource
#將call閉包與beanName建立映射
self.context[beanName]=call
def __getitem__(self,beanName):
"""重載__getitem__方法,使得BeanFactory支持使用[]獲取beanName對應的注冊的資源 """
try:
# 從context字典中取出beanName對應的資源
resource = self.context[beanName]
except KeyError:
raise KeyError("%r 未注冊" % beanName)
# 返回閉包函數調用后的結果
return resource()
AppFactory = BeanFactory()
def NoAssertion(obj): return True
def IsInstanceOf(*classes):
def test(obj): return isinstance(obj, classes)
return test
def HasAttributes(*attributes):
def test(obj):
for each in attributes:
if not hasattr(obj, each): return False
return True
return test
def HasMethods(*methods):
def test(obj):
for each in methods:
try:
attr = getattr(obj, each)
except AttributeError:
return False
if not callable(attr): return False
return True
return test
#
#
#Descriptor就是一類實現了__get__(), __set__(), __delete__()方法的對象
#若一個類的成員是descriptor,在訪問它的值時是通過__get__()函數訪問的
#用這個特性實現在訪問一個類的成員時自動到BeanFactory中請求對應的資源
class RequiredResource(object):
def __init__(self, beanName, assertion=NoAssertion):
self.beanName = beanName
self.assertion = assertion
def __get__(self, obj, T):#每次訪問descriptor時都會調用__get__方法
return self.result # <-- .操作符會自動調用__getattr__
def __getattr__(self, name):
assert name == 'result', "Unexpected attribute request other then 'result'"
self.result = self.Request()
return self.result
def Request(self):
obj = AppFactory[self.beanName]
assert self.assertion(obj), \
"The value %r of %r does not match the specified criteria" \
% (obj, self.feature)
return obj
class Component(object):
"Symbolic base class for components"
class Bar(Component):
# HasMethods是一個閉包函數,傳入RequiredResource后用於檢查'Console'
# 對應的注冊的那個feature是否有'WriteLine'方法
# IsinstanceOf(str) 是一個閉包,傳入RequiredResource后會被調用,用於
# 檢查注冊的'AppTitle'對應資源是否是一個字符串
# IsinstanceOf(str) 是一個閉包,傳入RequiredResource后會被調用,用於
# 檢查'CurrentUser'對應的資源是否是一個字符串
# RequiredFeatuire是desciptor,每次訪問descriptor(即實現了__get__的類),都會先經過__get__函數。
con = RequiredResource('Console', HasMethods('WriteLine'))
title = RequiredResource('AppTitle', IsInstanceOf(str))
user = RequiredResource('CurrentUser', IsInstanceOf(str))
flist = RequiredResource('show_dir',IsInstanceOf(list))
def __init__(self):
self.X = 0
def PrintYourself(self):
self.con.WriteLine('-- Bar instance --')
# title 由 RequiredResource('AppTitle',IsInstanceOf(str))生成
#'AppTitle'對應的值在__main__代碼塊注冊了一個值
self.con.WriteLine('Title: %s' % self.title)
self.con.WriteLine('User: %s' % self.user)
self.con.WriteLine('X: %d' % self.X)
for f in self.flist:
self.con.WriteLine(f)
class BetterConsole(Component):
def __init__(self, prefix=''):
self.prefix = prefix
def WriteLine(self, s):
lines = s.split('\n')
for line in lines:
if line:
print(self.prefix, line)
else:
print
def GetCurrentUser():
return os.getenv('USERNAME') or 'Some User' # USERNAME is platform-specific
def ShowDir():
return os.listdir()
if __name__ == '__main__':
print('\n*** IoC Demo ***')
#Provide(feature,provider,*args,**kwargs) feature是要生成的對象的父類類型 provider是要注入的子類或者值
AppFactory.setBean('AppTitle', 'Inversion of Control ...\n\n... The Python Way')
AppFactory.setBean('CurrentUser', GetCurrentUser)
AppFactory.setBean('Console', BetterConsole, prefix='-->') # <-- transient lifestyle
AppFactory.setBean('show_dir',ShowDir)
bar = Bar()
bar.PrintYourself()