TestLoader源碼解析

 1 def loadTestsFromTestCase(self, testCaseClass) #看名稱分析:從TestCase找測試集--那麼就是把我們的def用例加載到testSuit裏面
 2 def loadTestsFromModule(self, module, *args, pattern=None, **kws) #看名稱分析:從模塊裏面找測試集,那麼 模塊>類>test_方法>添加到testSuit裏面
 3 def loadTestsFromName(self, name, module=None)  #看名稱分析: 接收到name直接添加到testSuit裏面
 4 def loadTestsFromNames(self, names, module=None) #看名稱分析:接受到的是一個包含測試test_方法的列表
 5 def getTestCaseNames(self, testCaseClass) # 看名稱分析:  取出一個包含test_方法的列表
 6 def discover(self, start_dir, pattern='test*.py', top_level_dir=None) ## 看名稱分析:發現--找test_方法
 7 def _get_directory_containing_module(self, module_name) #獲取目錄包含的模塊
 8 def _get_name_from_path(self, path)  #從路徑從找名稱
 9 def _get_module_from_name(self, name)  #從名稱找模塊
10 def _match_path(self, path, full_path, pattern)   #正則匹配路徑--參數包含pattern 那估計是匹配我們測試腳本格式的
11 def _find_tests(self, start_dir, pattern, namespace=False)  #找測試集合
12 def _find_test_path(self, full_path, pattern, namespace=False)  #找測試集合的路徑

View Code

 1 那就是1234
 2 一個discover,getTest,_match_path
 3 二個find
 4 三個_get
 5 四個loadTests
 6 
 7 discover  邏輯
 8 >
 9 _find_tests【兩個處理邏輯  一個是本次傳的目錄和上次傳的一樣或不一樣,】
10 【一樣:直接從我們傳的目錄下面繼續去找testcaose---11 【不一樣:會從我們傳的目錄下面去執行os.path.listdir找到所有的子文件列表paths(文件)),然後遍歷得到單獨的path做 start_dir+path拼接】
12 >
13 ①—get_name_from_path【傳入start_dir,判斷當前傳入的目錄是否為上次傳入的頂級目錄返回".",不一樣可能有點繞-並返回一個值這個值有四種情況 . test  ...test dir.tests--正常應該是返回test文件名
14 ②_find_test_path(self, full_path, pattern, namespace=False)
15 【執行這個從路徑中找test,那麼很明顯 一樣:傳目錄路徑   不一樣傳文件路徑  】
16 _find_test_path

View Code

 1 class TestLoader(object):
 2     """
 3     This class is responsible for loading tests according to various criteria
 4     and returning them wrapped in a TestSuite
 5     """
 6     testMethodPrefix = 'test'
 7     sortTestMethodsUsing = staticmethod(util.three_way_cmp)
 8     suiteClass = suite.TestSuite
 9     _top_level_dir = None
10 
11     def __init__(self):
12         super(TestLoader, self).__init__()
13         self.errors = []
14         # Tracks packages which we have called into via load_tests, to
15         # avoid infinite re-entrancy.
16         self._loading_packages = set()  #這裏創建了一個空的self._loading_packages={}無序且不重複的元素集合

View Code 1.discover方法:unittest.defaultTestLoader a.定義了三個布爾值屬性 is_not_importable==True則是不能導入,is_namespace ,set_implicit_top b.對頂層目錄做了處理–當服務首次啟動 執行unittest.defaultTestLoader.discover(“文件目錄A”,pattern,top_level_dir=None):self._top_level_dir = top_level_dir = start_dir 這三個相等。 再次執行unittest.defaultTestLoader.discover(“文件目錄B”,pattern,top_level_dir=None): top_level_dir=self._top_level_dir【也就是他會默認上次start_dir 為頂層目錄】– 無論是首次還是複次–上面的操作完成之後 self._top_level_dir = top_level_dir 仍然繼續執行了一句這個—也就是說 self._top_level_dir == top_level_dir 始終一樣 c.針對頂層目錄不是一個目錄文件做了一系列的處理如果你傳的目錄是一個可導入的模塊-他在這個異常處理中.會重新自導入這個模塊。並開始追尋他的絕對路徑,判斷其模塊的可用性,然後執行_find_tests()尋找用例 d.如果是一個目錄就直接開始執行_find_tests()尋找用例 e.所以他這裏分兩種情況可以找到用例 第一種:傳的目錄 第二種:傳入的可導入模塊-這種情況self._top_level_dir 最終也是一個絕對路徑

  1 def discover(self, start_dir, pattern='test*.py', top_level_dir=None):  #一般我們top_level_dir傳的都None
  2     set_implicit_top = False   #是否存在頂層目錄
  3     if top_level_dir is None and self._top_level_dir is not None:
  4         # make top_level_dir optional if called from load_tests in a package
  5         top_level_dir = self._top_level_dir  #複次走這裏
  6     elif top_level_dir is None:  #初次走這裏
  7         set_implicit_top = True
  8         top_level_dir = start_dir
  9     #上面這一串花里胡哨的東西就是處理頂層目錄-如果是第一次啟動服務-
 10     #就走elif-top_level_dir==我們下面傳的值--之後--self._top_level_dir就不為空了,
 11     #但是top_level_dir  頂部是處理==None所以會走if=True
 12     top_level_dir = os.path.abspath(top_level_dir)#轉絕對路徑
 13     if not top_level_dir in sys.path:
 14         #這裡是防止重複將top_level_dir加入執行目錄--BUT如果我第一次傳的start_dir=a,第二次傳的start_dir=b
 15         #分析一下--第一次就是把a加入到了執行目錄---下面self._top_level_dir=a 二次(複次)傳b的時候,會出現top_level_dir=a---並沒有判斷b是否在執行目錄
 16         #這裏加一波問號?????????????????????
 17         #但是一般情況 我們目錄就只有一個--所以這裏--先放着。。。先看後面再來看這裏
 18         # all test modules must be importable from the top level directory
 19         # should we *unconditionally* put the start directory in first
 20         # in sys.path to minimise likelihood of conflicts between installed
 21         # modules and development versions?
 22         sys.path.insert(0, top_level_dir)
 23     self._top_level_dir = top_level_dir
 24     #如果top_level_dir我們傳的目錄不在可執行目錄--則臨時添加進去
 25     is_not_importable = False  #是否  不能導入
 26     is_namespace = False    #is_namespace那麼這個字段的意思就是是否可以找到傳入的路徑
 27     tests = []
 28     if os.path.isdir(os.path.abspath(start_dir)):  #判斷我們傳的是否為一個目錄--實際上這裏直接用top_level_dir不香嗎--
 29         start_dir = os.path.abspath(start_dir)
 30         # 之前把top_level_dir = start_dir
 31         # 然後top_level_dir = os.path.abspath(top_level_dir)
 32         #現在start_dir = os.path.abspath(start_dir)
 33         #為什麼不   直接用top_level_dir?  小朋友你是否有許多問號
 34         # 問題出在上面--複次的時候--並沒有走 top_level_dir = start_dir 而是走的 top_level_dir = self._top_level_dir ,
 35         #所以如果我們上次傳的路徑如果和這次不一樣--那麼top_level_dir是不等於start_dir--而start_dir才是我們傳的--
 36         if start_dir != top_level_dir:  #所以這裏相當於判斷前後傳的路徑是否一樣--一般來說我們的start_dir都是等於top_level_dir的
 37             is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py'))#如果是一個文件返回false
 38             #如果不一樣--則判斷我們當前傳入的start_dir/__init__.py是不是一個正確的文件路徑..os.path.isfile() 返回布爾值
 39     else: #如果我們傳入的不是一個目錄,就開始一堆花里胡哨的報錯東西了。。暫時不用管
 40         # support for discovery from dotted module names
 41         try:
 42             __import__(start_dir)
 43             #這裏就很有意思了---__impor__("PyFiles.Besettest")那就是導入PyFiles
 44             #那麼也就是說這個97.33的概率會報錯--也就是說你如果目錄錯了--下面的else基本不會走。。。除非你很神奇的填的路徑右側是一個可導入的模塊
 45         except ImportError:
 46             is_not_importable = True   #如果導入不鳥--就is_not_importable設置為true  在這裏我清楚了這個字段的含義--不能導入=true
 47         else:#那麼這裏假設導入成功之後
 48             the_module = sys.modules[start_dir]  #這裡是如果我們導入成功--就走這裏-取出start_dir導入的賦值給the_module
 49             top_part = start_dir.split('.')[0]    #這裡是將我們導入的模塊名稱取出來
 50             try:
 51                 start_dir = os.path.abspath( #打印導入模塊所在目錄的絕對路徑
 52                    os.path.dirname((the_module.__file__)))
 53             except AttributeError:     #這裡是如果導入模塊成功了---但是尼瑪打印導入模塊的絕對路徑又報錯--不想看了+2
 54                 # look for namespace packages
 55                 try:  #然後有開始進行模塊導入檢查---日了狗了。。。。。
 56                     #  fuck----想直接關機了+1,這裏估計是想找到為什麼不能導入的原因。。大神的思路就是完美,如果是我就拋出一個目錄不對就完事
 57                     #這一塊的學習 文檔 Python標準模塊--import
 58                     spec = the_module.__spec__
 59                     #將導入成功的模塊的規格說明賦值給spec--
 60                     #打印出來就是ModuleSpec(name='besettest.interface', loader=<_frozen_importlib_external.SourceFileLoader object at 0x0000000003D6E780>, origin='E:\\PyFiles\\Besettest\\besettest\\interface\\__init__.py', submodule_search_locations=['E:\\PyFiles\\Besettest\\besettest\\interface'])
 61                     #這麼一串東西--也沒用過。。。。大概就是模塊名稱、路徑、導入的模塊對象吧
 62                     #origin 加載模塊的位置--
 63                     #loader_state模塊特定數據的容器
 64                 except AttributeError:  #如果模塊的規格說明取不出來。。。。。。。
 65                     spec = None   #我查閱了一下。。。的確存在部分模塊規格說明為None的--所以還得繼續往下看
 66 
 67                 if spec and spec.loader is None:   #如果存在規格說明 且 數據容器為None。
 68                     if spec.submodule_search_locations is not None: 
 69                         #這是個什麼玩意呢--模塊 搜索 位置s(列表)。。。
 70                         is_namespace = True    #如果spec.submodule_search_locations不為none -----
 71                         # 2.5級英文翻譯 就是模塊的路徑 如果模塊路徑不為空is_namespace可以找到---is_namespace那麼這個字段的意思就是存在命名空間。。就是可以找到這個模塊
 72                         for path in the_module.__path__:   #這裏我研究懷疑是故意提升逼格。。the_module.__path__==spec.submodule_search_locations
 73                             if (not set_implicit_top and     #首次set_implicit_top==True  複次set_implicit_top==Fase
 74                                 not path.startswith(top_level_dir)):
 75                                 continue
 76                                 #這裏讓我稍微有點疑惑。。為什麼要判斷是首次還是複次--我猜是判斷the_module.__path__列表裡面有幾個某塊的路徑
 77                                 #如果是首次直接下一步--如果是複次會有多個路徑。但是如果是複次top_level_dir這個路徑又是上次的。。。日
 78                                 #他的作用是找到導入模塊的路徑-知道這個就行。。。
 79                             self._top_level_dir = \
 80                                 (path.split(the_module.__name__
 81                                      .replace(".", os.path.sep))[0])   #取出導入模塊的上級目錄絕對路徑。。。
 82                             #the_module.__name__.replace(".", os.path.sep) 這一串我看來是沒有必要的。。因為  the_module.__name__既然取到了模塊名稱那他肯定是一個字符串
 83                             tests.extend(self._find_tests(path,     #然後調用_find_tests 尋找測試。加入tests列表--這個有點熟悉的味道---
 84                                                           pattern,  #我覺得基本不會走這裏去找---腳本路徑一般都會填對  填錯了,都不知道執行到哪裡去了。。。
 85                                                           namespace=True))
 86                 elif the_module.__name__ in sys.builtin_module_names:
 87                     #判斷 sys.builtin_module_names返回一個列表,包含所有已經編譯到Python解釋器里的模塊的名字 和sys.models是一個字典
 88                     #就是沒法導入報錯
 89                     # builtin module
 90                     raise TypeError('Can not use builtin modules '
 91                                     'as dotted module names') from None
 92                 else:  #沒發現這個模塊
 93                     raise TypeError(
 94                         'don\'t know how to discover from {!r}'
 95                         .format(the_module)) from None
 96 
 97             if set_implicit_top:     #如果是首次。。。。
 98                 if not is_namespace:  #is_namespace默認的是false-且可以找到模塊相關規格
 99                     self._top_level_dir = \
100                        self._get_directory_containing_module(top_part)  #interface.testFiles   interface假設這個是導入的 -self._top_level_dir 是一個目錄的絕對路徑
101                     #top_part導入的模塊名稱-----
102                     sys.path.remove(top_level_dir)    #只知道是從系統路徑移除--但是不知道為什麼移除。。。。
103                 else:
104                     sys.path.remove(top_level_dir)   #
105 
106     if is_not_importable:   #如果我們傳的文件不能導入---就直接拋出異常
107         raise ImportError('Start directory is not importable: %r' % start_dir)
108 
109     if not is_namespace:  #is_namespace默認的是false--這裏就是可以找到模塊。。。。
110         tests = list(self._find_tests(start_dir, pattern))
111     return self.suiteClass(tests)

View Code 2._find_tests()–尋找testCase並生成測試套件tests=[]

 1 def _find_tests(self, start_dir, pattern, namespace=False):   #注意這裏如果我們傳的不是腳本目錄而是一個可導入的模塊namespace是等於True的
 2     """Used by discovery. Yields test suites it loads."""
 3     # Handle the __init__ in this package
 4     name = self._get_name_from_path(start_dir)   #返回一個name   name存在三種返回情況  "."-當本次和上次傳入的start_dir一致     不一致  "文件名"    "...文件名"
 5      #get_name_from_path的邏輯在這裏就很清晰了 
 6           
 7     # name is '.' when start_dir == top_level_dir (and top_level_dir is by
 8     # definition not a package).
 9     if name != '.' and name not in self._loading_packages:  
10     #當name最少有一個且也不再self._loading_packages.【self._loading_packages初始化的時候建的空集合】 走下面這個
11         # name is in self._loading_packages while we have called into
12         # loadTestsFromModule with name.
13         tests, should_recurse = self._find_test_path(      #然後這裏start_dir是我們傳的模塊--他就去找。。這裏就恢復到了傳測試目錄的邏輯了
14             start_dir, pattern, namespace)
15         if tests is not None:
16             yield tests
17         if not should_recurse:
18             # Either an error occurred, or load_tests was used by the
19             # package.
20             return
21     # Handle the contents.
22     paths = sorted(os.listdir(start_dir))  #那就從這裏開始--當我們穿的目錄和上次一樣-他會找到目錄下所有的文件然後排序--我們的用例執行順序就是從這裏開始搞了。。
23     for path in paths:       #遍歷我們傳的目錄下的所有文件
24         full_path = os.path.join(start_dir, path)     將我們傳入的目錄和目錄下的py文件拼接的完整路徑
25         tests, should_recurse = self._find_test_path(    #把我們文件路徑和我們的文件格式傳入_find_test_path這個方法--
26             full_path, pattern, namespace)
27         如果當前傳的是一個目錄-會返回should_recurse=True--這個英文直譯是應該_遞歸--下面yield from 就是執行遞歸的操作
28         if tests is not None:
29             yield tests
30         if should_recurse:  #這句是判斷他是不是一個目錄
31             # we found a package that didn't use load_tests.
32             name = self._get_name_from_path(full_path)
33             self._loading_packages.add(name)
34             try:
35                 yield from self._find_tests(full_path, pattern, namespace)
36             finally:
37                 self._loading_packages.discard(name)

View Code yield與yield from

 1 def  a(n):
 2     testList=b(n)
 3     return testList
 4 
 5 def  b(n,m=1):
 6     print("執行第%s次"%m)
 7     for a in range(n):
 8         if not divmod(a,2)[1] and a!=0:
 9             print(a)
10             yield a   #是用yield之後返回的是一個生成器
11             if divmod(a,3)[1]:
12                 m =m+1
13                 yield from b(a,m)  #重新執行b方法
14 
15 print(list(a(7)))
16 
17 執行第1次
18 2
19 執行第2次
20 4
21 執行第3次
22 2
23 執行第4次
24 6
25 [2, 4, 2, 6]

View Code _get_name_from_path 他主要做了:從我們傳的路徑裏面找腳本文件 名。。。如果找到了腳本文件則返迴文件名稱—如果沒找到也就是我們返回一個點 或者 至少一個點(三種情況 . test_case …..test_case 返回name可能存在的三種值) 在_test_find調用這個方法path=start_dir(我們傳的目錄)–這個返回一個點 或者 至少一個點 在 _fin_test_path調用這個方法是傳的我們傳的目錄下的文件路口–返回的name就是文件名

 1 name = self._get_name_from_path(start_dir)    #因為discover我們是支持我們傳目錄或者模塊尋找testcase的,所以這個方法
 2 
 3 def _get_name_from_path(self, path):
 4      #主要正確邏輯三個 比如我們的腳本目錄結構是  E://a/b/     b目錄下面有script.py    和  /c/script.py
 5      #第一次是我們自己傳的目錄--之前在discover他做了一個處理 就是第一次運行時會把我們傳的目錄賦值給頂層目錄---
 6      #第一個邏輯判斷我們傳的是不是  -和頂層一樣---一樣的話===_find_tests方法就直接從目錄下面找腳本-當如如果有目錄也會繼續走--他是在_find_tests_path判斷的--最終也是回到找腳本模塊上
 7      #如果不一樣--那就是找了 找到這個目錄了---那麼就從頂層開始找這個目錄的相對路徑--其實就是找最後那個目錄(必須是一個packge。上面說的目錄都是包)、。。然後返回一個name
 8      #如果還有子目錄  d---那就會返回  c.d
 9     if path == self._top_level_dir:  #首次運行pattern,top_level_dir=None):self._top_level_dir = top_level_dir = start_dir -第二次運行如果目錄沒有變,這裏也是直接返回的
10         return '.'
11         
12         
13     path = _jython_aware_splitext(os.path.normpath(path))  #如果我們當前傳的和上次傳的目錄不一致。。這裏得path我們當前傳的路徑
14     
15     _relpath = os.path.relpath(path, self._top_level_dir)    #從self._top_level_dir開始找path的相對路徑
16     #這裡是從我們傳的path開始找到self._top_level_dir上次傳的相對路徑 
17     #例如: path=path1="E:\\PyFiles\\Besettest\\besettest\\interface\\testFiles"   self._top_level_dir="E:\\PyFiles\\Besettest\\besettest\\interface\\result"
18     #那麼_relpath="..\testFiles"    -暫時還不清楚為什麼要找這個??????????????????????????
19     assert not os.path.isabs(_relpath), "Path must be within the project"
20     #↑↑斷言 不是絕對路徑-也就是說_relpath是否為相對路徑↑↑↑特么的 這裏肯定是一個相對路徑啊。。。上面都有relpath了。。。丟
21     #↓↓↓↓斷言以..開頭就失敗。。。↓↓↓--這兩處超出理解範圍了。。。。。。
22     assert not _relpath.startswith('..'), "Path must be within the project"
23     
24     name = _relpath.replace(os.path.sep, '.')  #然後這裏又把分隔符替換成.  返回 a.b  當然或許會有異常情況返回.....這是我意淫的
25     return name

View Code self._find_test_path #找測試的路徑 兩個主要邏輯: 傳到full_path 是一個文件 還是一個目錄

 1 def _find_test_path(self, full_path, pattern, namespace=False):  
 2  #_find_tests()調用這個方法 傳了一個我們傳的目錄下的a文件路徑、和需要找的文件pattern-namespace【傳的可能是true 也可能是false】,如果我的目錄是對的-namespace傳的就是false
 3     """Used by discovery.
 4 
 5     Loads tests from a single file, or a directories' __init__.py when
 6     passed the directory.
 7 
 8     Returns a tuple (None_or_tests_from_file, should_recurse).
 9     """
10     basename = os.path.basename(full_path)   #basename==文件名.py後續帶py的統稱文件--不帶後綴的統稱文件名。。。
11     if os.path.isfile(full_path):   #如果我們傳的full_path是一個文件---我們在discover傳的是一個腳本目錄-之前在_test_find是做了一個拼接得到的完整路徑full_path
12         if not VALID_MODULE_NAME.match(basename): #判斷他是不是一個py文件----
13             
14             # valid Python identifiers only
15             return None, False   #如果不是直接返回
16         if not self._match_path(basename, full_path, pattern):  #這裏雖然傳了三個值--但是實際上只有basename,pattern有用--
17          #_match_path調用fnmatch(文件, 我們傳的文件格式或文件)這個需要————from fnmatch import fnmatch他的主要作用是做此模塊的主要作用是文件名稱的匹配
18          #當此次傳入的文件名與我們的文件格式匹配一致self._match_path返回true
19             return None, False    #如果不一樣 就直接回到 _find_test繼續找
20         # if the test file matches, load it
21         name = self._get_name_from_path(full_path)   #然後這裏把文件路徑又傳到 self._get_name_from_path去返迴文件名-這個時候因為我們傳的是腳本目錄-full_path目錄下的文件路徑,所以返回的name 就是文件名
22        
23         #self._top_level_dir是當前文件目錄路徑,path是當前文件路徑--從目錄找文件--直接就是文件名--他返回的name就是文件名   
24         try:
25             module = self._get_module_from_name(name)    #_get_module_from_name  這個方法就是動態導入模塊名--然後返回一個所有導入的模塊的對象 moudel.__file__路徑、moudel.__name__名稱
26         except case.SkipTest as e:   #如果導入不成功 case.SkipTest 實際上case是繼承--Exception--所以把這個理解為Exception就可以了--
27             return _make_skipped_test(name, e, self.suiteClass), False
28         except:
29             error_case, error_message = \
30                 _make_failed_import_test(name, self.suiteClass)   
31             self.errors.append(error_message)
32             return error_case, False
33         else: #module 獲取到值之後走這裏。。
34             mod_file = os.path.abspath(
35                 getattr(module, '__file__', full_path))    #然後這裏取出我們導入模塊的 絕對路徑---如果反射找不到就返回該文件的路徑-其實差別不大-處理一下更嚴謹
36             realpath = _jython_aware_splitext(
37                 os.path.realpath(mod_file))   #os.path.realpath(mod_file)然後又返回真實路徑---然後又去掉路徑的.py,。,,,,,,,,丟
38             fullpath_noext = _jython_aware_splitext(
39                 os.path.realpath(full_path))   #然後full_path 找真實路徑去掉.py
40             if realpath.lower() != fullpath_noext.lower():  #如果動態導入的模塊的目錄路徑 不等於 傳進來(也就是pattern)的目錄路徑--實際上傳進來的路徑肯定是個絕對路徑--因為前面已經轉了好幾次絕對路徑了
41                 module_dir = os.path.dirname(realpath)   #不等於就找動態導入模塊所在的目錄----實際上上面處理的realpath已經是一個目錄了。。但是他防止realpath還是一個.py文件。所以又操作了一次
42                 mod_name = _jython_aware_splitext(    #full_path是文件的路徑.py的,然後這裏又先是basename取出文件(就是把路徑去掉,只留下xxx.py) 然後外面那個方法 把.py去掉--留下文件名
43                     os.path.basename(full_path))
44                 expected_dir = os.path.dirname(full_path)  
45                 #然後找到需要執行腳本所在的目錄。。。。。也就是說正常情況  假設expected_dir="e://a/b"  那麼 mod_file =realpathfullpath_noext="e://a/b/scripy" 
46                 #scripy是一個py文件---上面這個if是說的 正常情況。。。我想不到導入模塊和導入模塊的路徑不相等的情況--不過這個不重要-源碼這樣肯定是有道理的
47                   msg = ("%r module incorrectly imported from %r. Expected "
48                        "%r. Is this module globally installed?")
49                 raise ImportError(
50                     msg % (mod_name, module_dir, expected_dir))
51             return self.loadTestsFromModule(module, pattern=pattern), False   
52             #然後走 從模塊從加載測試s 這個方法---也就是說discover實際上是調用loadTestsFromMould這個方法的。。測試套件也是在這一步處理的
53     elif os.path.isdir(full_path):  #dicover裏面傳腳本目錄是走這裏。。
54         if (not namespace and    #namespace-默認是false   not namespace就是true  
55             not os.path.isfile(os.path.join(full_path, '__init__.py'))): #不是一個包。。-也就是說我們傳的目錄應該是一個包,下面包含__init__.py
56             return None, False
57         
58         load_tests = None  #這個load_tests是啥意思呢??????????後面繼續看----看了一遍--並且用unittest.main()試了一下-模塊下面是沒有這個屬性的。。只是unittest的初始化文件有這個方法--他也是通過discover找的。。
59         tests = None
60         name = self._get_name_from_path(full_path)   #這裏就是走子目錄的邏輯了
61         #get_name_from_path的邏輯在這裏就很清晰了 
62             #A.如果通過_find_test 調用self._get_name_from_path  是為了判斷兩次start_dir是否一致一致返回.  不一致返回從上次的start_dir1找到本次start_dir12的相對路徑--
63             #這裏又分兩種情況-A1正常情況-start_dir1是start_dir12的上級目錄。。。那麼返回的那麼就是-A.B這樣的了。。因為第一次的A是已經os.path.insert到環境變量了..所以A.B是可以直接用
64                                 #A2不正常情況 就是之前說的 最少返回一個點的...A這種返回---然後問題來了--他為什麼要這麼處理呢--原因是?????????
65                                 #我猜是與腳本同級存在另一個腳本目錄。。。後面驗證這一點。-----這裡在上面補充了--是因為子目錄中還存在腳本所有這麼走邏輯-完美的
66         try:
67             package = self._get_module_from_name(name)   #上面是導入的一個module--這裡是導入一個包-- 返回--
68         except case.SkipTest as e:
69             return _make_skipped_test(name, e, self.suiteClass), False
70         except:
71             error_case, error_message = \
72                 _make_failed_import_test(name, self.suiteClass)
73             self.errors.append(error_message)
74             return error_case, False
75         else:
76             load_tests = getattr(package, 'load_tests', None)  #然後判斷這個包裏面有沒有'load_tests'這個屬性---這裏我代碼一直看下來,我們是不知道這個lood_tests是什麼的,字面意思 加載測試集合
77             # Mark this package as being in load_tests (possibly ;))
78             self._loading_packages.add(name)  #然後把模塊名稱添加到set集合
79             try:
80                 tests = self.loadTestsFromModule(package, pattern=pattern)   
81                 #這裏傳了一個package模塊對象,和文件匹配規則合作或者文件。。--但是這裏導入一個包之後實際上是找不到testCase的-因為包下面的屬性肯定不是一個類-不會走loadTestsFromTestCase
82                 #所以這裏返回的tests是一個空列表
83                 if load_tests is not None: #貌似這個是棄用的,向後兼容-暫時沒看明白這個load_tests代表的意思
84                     # loadTestsFromModule(package) has loaded tests for us.
85                     return tests, False
86                 return tests, True  #  如果能走到這裏------就返回True_就是給_find_test判斷走遞歸的--_find_tests裏面就得到 should_recurse=True
87             finally:
88                 self._loading_packages.discard(name)  #然後這個刪掉set集合裏面之前導入的那個包
89         else:
90         return None, False

View Code

 1 def loadTestsFromModule(self, module, *args, pattern=None, **kws):
 2     """Return a suite of all test cases contained in the given module"""
 3     # This method used to take an undocumented and unofficial
 4     # use_load_tests argument.  For backward compatibility, we still
 5     # accept the argument (which can also be the first position) but we
 6     # ignore it and issue a deprecation warning if it's present.
 7     if len(args) > 0 or 'use_load_tests' in kws:    #args這個默認是一個空元組  長度默認為0  kws是一個空字典
 8         warnings.warn('use_load_tests is deprecated and ignored',
 9                       DeprecationWarning)
10         kws.pop('use_load_tests', None)
11     if len(args) > 1:
12         # Complain about the number of arguments, but don't forget the
13         # required `module` argument.
14         complaint = len(args) + 1
15         raise TypeError('loadTestsFromModule() takes 1 positional argument but {} were given'.format(complaint))
16     if len(kws) != 0:
17         # Since the keyword arguments are unsorted (see PEP 468), just
18         # pick the alphabetically sorted first argument to complain about,
19         # if multiple were given.  At least the error message will be
20         # predictable.
21         complaint = sorted(kws)[0]  #取出第一個Key-
22         raise TypeError("loadTestsFromModule() got an unexpected keyword argument '{}'".format(complaint))
23     tests = []
24     for name in dir(module):    #這裏得modul實際上使我們傳入的模塊對象---dir(object)  返回模塊下的所有屬性列表-
25         obj = getattr(module, name)   #然後反射返回name對象。。返回的是一個class對象
26         if isinstance(obj, type) and issubclass(obj, case.TestCase):    #這裏判斷obj是否是一個類--並且這個類是case.TestCase的子類,也就是說 是否寫在我們繼承unitest.testCase那個類的下面
27             tests.append(self.loadTestsFromTestCase(obj))  #可以看到最後走loadTestFromTestCase obj這裡是傳入的一個類名
28 
29     load_tests = getattr(module, 'load_tests', None)
30     tests = self.suiteClass(tests)
31     if load_tests is not None:
32         try:
33             return load_tests(self, tests, pattern)
34         except Exception as e:
35             error_case, error_message = _make_failed_load_tests(
36                 module.__name__, e, self.suiteClass)
37             self.errors.append(error_message)
38             return error_case
39     return tests  #返回集合

View Code loadTestsFromTestCase:這裏就是添加testcase到suit集合裏面的主要邏輯

 1 def loadTestsFromTestCase(self, testCaseClass):  #testCaseClass是我們傳的一個用例類
 2     """Return a suite of all test cases contained in testCaseClass"""
 3     if issubclass(testCaseClass, suite.TestSuite):  #這個類是不是suite.TestSuite的子類--如果是的就拋出異常==
 4         raise TypeError("Test cases should not be derived from "
 5                         "TestSuite. Maybe you meant to derive from "
 6                         "TestCase?")
 7     testCaseNames = self.getTestCaseNames(testCaseClass)  #getTestCaseNames  從類下面找到用例名稱--找到名稱返回的是一個列表
 8     if not testCaseNames and hasattr(testCaseClass, 'runTest'):  #這裏判斷testCaseNames是否為空-並且 是否存在"runTest"這個元素
 9         testCaseNames = ['runTest']
10     loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames)) #是我的一個用例類--然後將testCaseNames類下面的測試方法--帶進去遍歷。。。高級用法---第一次見--這個方法很關鍵
11     return loaded_suite

View Code getTestCaseNames

 1 def getTestCaseNames(self, testCaseClass):
 2     """Return a sorted sequence of method names found within testCaseClass
 3     """
 4     def isTestMethod(attrname, testCaseClass=testCaseClass,   #定義一個內部方法
 5                      prefix=self.testMethodPrefix):     #self.testMethodPrefix="test"   這個在TestLoader下第一行就已經默認了,他是我們用例開頭的固定格式
 6         return attrname.startswith(prefix) and \  #這裏判斷了是否已test開頭以及 方法對象是否可用----getattr返回方法對象--callable()是檢測對象是否可用    返回一個布爾值
 7             callable(getattr(testCaseClass, attrname))
 8     testFnNames = list(filter(isTestMethod, dir(testCaseClass)))
 9      #dir(testCaseClass)返回該對象下面所有的屬性--包括變量test_1--所以上面需要檢測屬性時test開頭且是一個可以調用的對象--
10      #filter函數---前面是一個function -後面是一個可迭代對象-會遍歷可迭代對象-並傳入function-functions返回true則添加到列表--這樣就找到了所有的
11     if self.sortTestMethodsUsing:
12         testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing))     
13         #sortTestMethodsUsing = staticmethod(util.three_way_cmp) 轉為為靜態方法-內存地址指向self.sortTestMethodsUsing
14         #然後通過functools這個模塊排序==
15     return testFnNames  #然後返回用例方法名稱列表

View Code 所有的邏輯 就是在TestLoader找測試用例的時候–通過_find_tests這個方法從目錄開始找文件(子目錄)-模塊-類-方法名 然後將某個模塊下的類通過他下面的方法map返回多個對象,也就是說一個testClass下面存在五個test_method,他就會返回五個實例對象-並生成一個suite集合–然後加入到一個列表 如果一個模塊下有多個testClasee 同樣-實際上是一樣的–實際上他是先通過loadTestsFromModule這個方法找到所有的類對象之後在遍歷–然後才走上面那一步的,,,多個testClasss就存在多個suite集合– 也就是說 一個modul下面的 suite集合會添加到一個列表–[suite=[A-TestCase實例化對象1,A-TestCase實例化對象2],suite=[B-TestCase實例化對象1,B-TestCase實例化對象2]]—然後在將這個列表當做參數傳入TestSuite實例化一個新對象[suite=-[suite=[A-TestCase實例化對象1,A-TestCase實例化對象2],suite=[B-TestCase實例化對象1,B-TestCase實例化對象2]]]—-這樣就是一個模塊下用的結構 但是還沒有完–這裏只是一個modul下的—還有多個modul–到了大家估計也知道剩下的會幹什麼了— 沒錯–當我得到modul的全部suite集合之後—這個集合最終會返回給_find_tests方法–通過生成器返回給discover–也就是將這個suite集合又加入到了一個新的列表–然後discover又將這個list 帶入形成了一個—–最終的實例對象,最終返回的格式如下——- [suite= [suite1=-[suite=[A-TestCase實例化對象1,A-TestCase實例化對象2],suite=[B-TestCase實例化對象1,B-TestCase實例化對象2]]], [suite2=-[suite=[A-TestCase實例化對象1,A-TestCase實例化對象2],suite=[B-TestCase實例化對象1,B-TestCase實例化對象2]]] ]   –看過源碼的都知道—我們run的時候—就這個實例對象是可以接受參數的–而這個參數就是result—因為TestSuite繼承的BaseTestsSuite 有一個__call__這個
魔術方法:如果在類中實現了 __call__ 方法,那麼實例對象也將成為一個可調用對象,具體百度。這裏不做過多解釋—–所以最終的suite是可以接受參數的test(result)–接受參數之後直接走——call下面的邏輯了 本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

Optional 容器類

什麼是Optional容器類

Optional 類(java.util.Optional) 是一個容器類,代表一個值存在或不存在,原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。並且可以避免空指針異常

Optional類常用方法:

Optional.of(T t) : 創建一個 Optional 實例。

Optional.empty() : 創建一個空的 Optional 實例。

Optional.ofNullable(T t):若 t 不為 null,創建 Optional 實例,否則創建空實例。

isPresent() : 判斷是否包含值。

orElse(T t) : 如果調用對象包含值,返回該值,否則返回t。

orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值。

orElseThrow(Supplier es) : 當遇到一個不存在的值的時候,並不返回一個默認值,而是拋出異常。

map(Function f): 如果有值對其處理,並返回處理后的Optional,否則返回 Optional.empty()。

flatMap(Function mapper):與 map 類似,要求返回值必須是Optional。

filter(Predicate p):接收一個函數式接口,當符合接口時,則返回一個Optional對象,否則返回一個空的Optional對象。

示例:

 1 import org.junit.Test;
 2 import java.util.Optional;
 3 /*
 4  * Optional 容器類:用於盡量避免空指針異常
 5  *     Optional.of(T t) : 創建一個 Optional 實例
 6  *     Optional.empty() : 創建一個空的 Optional 實例
 7  *     Optional.ofNullable(T t):若 t 不為 null,創建 Optional 實例,否則創建空實例
 8  *     isPresent() : 判斷是否包含值
 9  *     ifPresent(Consumer<? super T> consumer) 判斷是否包含值,再執行 consumer
10  *     orElse(T t) :  如果調用對象包含值,返回該值,否則返回t
11  *     orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值
12  *     orElseThrow(Supplier<? extends X> exceptionSupplier) : 當遇到一個不存在的值的時候,並不返回一個默認值,而是拋出異常
13  *     map(Function f): 如果有值對其處理,並返回處理后的Optional,否則返回 Optional.empty()
14  *     flatMap(Function mapper):與 map 類似,要求返回值必須是Optional
15  *     filter(Predicate<? super T> predicate):接收一個函數式接口,當符合接口時,則返回一個Optional對象,否則返回一個空的Optional對象
16  *
17  *     map、flatMap 和 filter 的使用方法和 StreamAPI 中的一樣
18  */
19 public class TestOptional {
20 
21 
22     /**
23      * 創建 Optional 實例
24      */
25     @Test
26     public void test1(){
27 
28         // Optional.empty() : 創建一個空的 Optional 實例
29         Optional<String> empty = Optional.empty();
30         System.out.println(empty);// 輸出結果:Optional.empty
31         //System.out.println(empty.get());// 報錯:java.util.NoSuchElementException: No value present
32 
33         //    Optional.of(T t) : 創建一個 Optional 實例
34         Optional<Employee> eop = Optional.of(new Employee());
35         System.out.println(eop);// 輸出結果:Optional[Employee{name='null', age=null, gender=null, salary=null, status=null}]
36         System.out.println(eop.get());//輸出結果:Employee{name='null', age=null, gender=null, salary=null, status=null}
37 
38         //注意:Optional.of(T t) 中,傳遞給of()的值不可以為空,否則會拋出空指針異常
39         //Optional<Employee> eop1 = Optional.of(null);//這一行直接報錯:java.lang.NullPointerException
40 
41         //Optional.ofNullable(T t):若 t 不為 null,創建 Optional 實例,否則創建空實例
42         //所以,在創建Optional對象時,如果傳入的參數不確定是否會為Null時,就可以使用 Optional.ofNullable(T t) 方式創建實例。
43         Optional<Object> op = Optional.ofNullable(null);//這樣的效果和 Optional.empty() 一樣
44         System.out.println(op);//Optional.empty
45         op = Optional.ofNullable(new Employee());//
46         System.out.println(op);//Optional[Employee{name='null', age=null, gender=null, salary=null, status=null}]
47     }
48 
49 
50     /**
51      * isPresent() : 判斷是否包含值
52      * ifPresent(Consumer<? super T> consumer) 判斷是否包含值,再執行 consumer
53      */
54     @Test
55     public void test2(){
56         Optional<Employee> opt = Optional.of(new Employee());
57         System.out.println(opt.isPresent());//輸出結果:true
58         opt = Optional.ofNullable(null);
59         System.out.println(opt.isPresent());//輸出結果:false
60         opt.ifPresent(employee -> System.out.println(employee));// 如果 opt.isPresent() 為false ,這裏就不輸出,否則就輸出 employee
61     }
62 
63     /**
64      *     orElse(T t) :  如果調用對象包含值,返回該值,否則返回t
65      *     orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值
66      */
67     @Test
68     public void test3(){
69         //Optional<Employee> opt = Optional.of(new Employee());
70         Optional<Employee> opt = Optional.ofNullable(null);
71         /*Employee emp = opt.orElse(new Employee("張三",20));
72         System.out.println(emp);*/
73 
74         int condition = 2;//模擬條件
75         Employee emp = opt.orElseGet(()-> {
76             if (condition == 1){
77                 return new Employee("李四");
78             }else if (condition == 2){
79                 return new Employee("王二麻子");
80             }else {
81                 return new Employee("趙六");
82             }
83         });
84         System.out.println(emp);
85     }
86     /**
87      *     orElseThrow(Supplier<? extends X> exceptionSupplier) : 當遇到一個不存在的值的時候,並不返回一個默認值,而是拋出異常
88      */
89     @Test
90     public void test4(){
91         Object obj = Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new);//當參數為null,則拋出一個不合法的參數異常
92         System.out.println(obj);
93     }
94 
95 
96 }

備註:map、flatMap 和 filter 的使用方法和 StreamAPI 中的一樣,如需了解詳細使用方法,請參考:Stream API 詳解

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

IDEA自定義類註釋和方法註釋(自定義groovyScript方法實現多行參數註釋)

一、類註釋

1、打開設置面板:file -> setting -> Editor -> file and code Templates

選擇其中的inclues選項卡,並選擇File header,如圖。不要選擇Files選項卡再設置Class,這樣比較麻煩,而且這樣設置以後沒新建一個類都要自己寫一次Date。

2、在右邊編輯面板插入自己想要的註釋即可。其中${}是變量,需要在變量基本都在編輯款下面的Description,往下拉即可看到。
/*
* @Classname ${NAME}
*
* @Date ${DATE}
*
* @userName
*/
3、新建一個類,看是否自動加了註釋

 

二、方法註釋

1、打開設置面板:file -> setting -> Editor -> Live Templates

 

 2、新建一個Template Group…,命名隨意,假設為bokeyuan,然後選擇該組,點擊新建一個模板Live Template

 

3、名稱建議設為*,文本框輸入自己想要設置的註釋格式,右下角要選擇enter(原本是tab)。

 

 4、留意註釋格式,其中參數要直接寫變量$param$,開頭只有一個*號。寫好之後點擊上圖框中的edit variables

 

其中返回值return使用系統自帶的,下拉可以找到methodReturnType()

 

 

 5、自定義多行參數註釋

IDEA自帶的參數函數methodParameters()產出的註釋格式是這樣的:

/**
      * 
      * @param [a,b,c]
      * @return void
      * @throws 
      */

我們可能需要的是多行參數註釋:

/**
      * 
      * @param a
      * @param b
      * @param c
      * @return void
      * @throws 
      */

這個時候就要使用裏面的groovyScript()函數來自定義格式:

groovyScript("def result=''; def params=\"${_1}\".replaceAll('[\\\\[|\\\\]|\\\\s]', '').split(',').toList(); for(i = 0; i < params.size(); i++) {if(i == 0) result += '* @param ' + params[i] + ' ' + ((i < params.size() - 1) ? '\\n' : '');else result+='  * @param ' + params[i] + ' ' + ((i < params.size() - 1) ? '\\n' : '')}; return result", methodParameters())

直接複製在Expression裏面即可。

 

6、選擇語言,點擊Define勾選Java

 

 

有其他問題可以評論問我哦

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

地球發燒了研究:2080年前熱死人數激增20倍

摘錄自2018年8月1日蘋果日報台北報導

天氣讓人熱得受不了,而這種情況若不加以控制,地球上熱死人的數目將增加20倍。最新研究指出,在各國政府完全不做任何事的最糟情況,部分國家在2080年前,因為高溫導致的死亡數字將增加2000%。

這項刊登在「公共科學圖書館醫學期刊」(PLOS Medicine)的報告指出,歐洲、部分亞洲與北美洲地區的氣溫逐年升高,熱浪造成數以千計的死亡案例。該報告首席研究員、澳洲蒙納士大學(Monash University)副教授郭玉明(Yuming Guo,音譯)說:「未來的熱浪會發生得更頻繁、更強烈,維持時間也更長」、「如果我們找不到減緩氣候變遷的方法,並幫助人民適應熱浪氣候,和熱浪有關的死亡案例將會急遽增加。」

報告中指出,最糟的情況下,哥倫比亞在2031至2080年間被高溫熱死的人數,會比1971年至2010年增加2000%,即增加20倍;菲律賓在2031到2080因熱浪而額外死亡的人數,是1971到2020的12倍;澳洲、美國是5倍,英國是4倍。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

石油公司看好電動車,預測2030年石油需求逐下滑

隨著相關技術逐漸成熟,如今已經不再只有相關業者看好電動車市場,就連石油大廠道達爾(Total)近日都承認,在這樣的發展下,石油的需求可能即將見頂,未來電動車的商機將不容小覷。

彭博社報導,道達爾的能源經濟學家Joel Couse 表示,隨著電動車市場持續擴大,估計至2030 年時,電動車將佔有新車市場15~30%,石油需求預計將在屆時達到頂峰,之後就不會再出現增長,甚至可能逐漸下滑。

這樣的話從石油主要生產商的口中說出有些令人感到意外,就連彭博新能源財經(BNEF)的主管Colin McKerracher 都表示,道達爾的分析是目前為止最看好電動車產業的預測,甚至超過彭博的預估值。

其實不只是道達爾,荷蘭皇家殼牌(Shell)也已經開始削減石油需求的長期預測。為了因應未來科技的走向,殼牌已經建立一個全新業務部門,針對多種綠能可能造成的經濟效益進行分析。

彭博新能源財經指出,在電動車的製程中,電池是成本中最昂貴的部分,幾乎可以佔到總成本的一半,高昂的成本價格也是造成許多車商在發展電動車時,會選擇高價車款為主打的原因。

然而隨著科技持續發展,電池價格開始逐年下降、效能持續提升,電動車已經開始能與燃油車在價格及性能上做競爭。儘管在全球汽車銷量中,電動車目前僅佔有約1% 的市場,但許多車商已經注意到這個趨勢,開始投入數十億美元進入電動車市場耕耘。

除了開始進入電動車市場的捷豹(Jaguar)、富豪(Volvo)之外,賓士(Mercedes-Benz)、通用(General)及許多車廠也將在2020 年時推出數十種電動車款,豐田(Toyota)更計劃在2050 年時,淘汰所有燃油車款,汽車市場「雪崩式」改變的時刻即將來到。

彭博新能源財經負責人Michael Liebreich 認為,至2020 年時,將會有120 種以上不同型號的電動車在市面上供消費者選擇,「到了那個時候,燃油車會變成很過時的東西。」 

(合作媒體:。圖片出處:publidc domain CC0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

打入中國市場,Hyundai選陸電池廠供混合動力車

南韓汽車製造商現代汽車(Hyundai Motor)首次選擇與中國電池製造商合作,由總部位於福建的寧德時代(CATL)為現代插電式混合動力車Sonata 提供電池,預計將在2018 年上半年打入中國市場。日經報導分析,這項合作是出於中國反外商情結的壓力。

日經新聞報導,隨著中國加緊推進新能源汽車應對空氣污染,中國政府正在研擬一項電動車份額草案,在中國市場上,外國汽車製造商必須讓電動車的銷量達到總銷售量的8%,若外商達不到指標,很有可能會被迫購買中國車商的積分,等於對中國車商提供經濟資助。先前草案規定2019 年將比率提升到10%,2020 年到12%,都被汽車製造商拒絕。

此外,中國政府為了幫助國內電池業者,規定必須通過中國的安全標準才符合補貼資格,東京的汽車製造商GLM 認為,未來很可能只有使用中國製造的電池的電動車才能在中國市場販售。GLM 準備在2019 年在中國市場推出4 人座跑車G4,號稱電動車的法拉利。

中國現在是全球最大的電動車市場,每年賣出30 萬台,中國政府最新的5 年計畫目標到2020 年一年增加到500 萬台。中國市場是現代汽車和起亞汽車在全球的最大市場,2016 年兩車廠在中國銷量佔全球整體銷量比率,分別為23.5% 和21.5%。

但這幾個月由於中韓兩國因美國設置反導系統引發政治動盪,使得現代汽車銷售受牽連,2017 年第一季中國零售銷售下滑14%,現代汽車準備在今年夏天啟用在重慶的第5 座中國工廠,銷售下滑對現代而言不是好消息。

CATL 是中國動力鋰電池龍頭,目標2020 年成為僅次於Tesla 與松下合資公司的全球第二大電動車電池供應商,最大股東為蘋果電池供應商日本TDK 集團,投資人中也包括鴻海。

根據現代官方說法,選擇CATL 作為第一個中國電池供應商,是現代尋求供應商多元化的策略之一。報導認為,與CATL 合作除了強化與中國政府的關係之外,也是為了避免中國突如其來的反外商政策對現代造成的衝擊。

(合作媒體:。圖片出處:Hyundai)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

Tesla Q1營收倍增,Model 3排定7月量產

美國電動車廠特斯拉(Tesla)公佈首季獲利不如預期,但好消息是眾所矚目的Model 3平價電動車,有望如期量產。

特斯拉當季營收27億美元,為去年同期營收11.5億美元的兩倍多,亦優於分析師預估的26.2億美元。(路透社)

儘管如此,特斯拉當季每股虧1.33美元,遠超過分析師預估每股虧0.81美元。特斯拉去年同期每股虧1.45美元。

特斯拉說Model 3開發進度順利,將可在今年7月導入量產。不過從虧損超出預期可知,這應該是特斯拉燒錢換來的成果。

另外,特斯拉執行長Elon Musk還憂心客戶對定價只有35,000美元的Model 3期待太高,而忘了Model 3是平價車種,性能與續航力均不能與售價70,000美元的Model S相提並論。

特斯拉首季共交車25,051部,較去年同期成長64%、較去年第四季成長13%,上半年47,000-50,000部的交車目標也有望達陣。

(本文內容由授權使用。圖片出處:Tesla)  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

以機器人生產取代海外代工EV,特斯拉望提升獲利

蘋果海外代工太花錢,美國電動車商特斯拉(Tesla)誇下海口,要靠機器人大軍,快速提升獲利,未來市值將與蘋果並駕齊驅。

MarketWatch、CNBC報導,2015年特斯拉創辦人兼執行長馬斯克(Elon Musk)曾說,特斯拉市值會在十年內衝上7,000億美元。他說,要是十年都能維持50%成長率,獲利達到10%,本益比為20倍,特斯拉市值將追上蘋果。要達到7,000億美元市值不簡單,勢必需要龐大的資本開支。

3日晚間的財報會議上,有人問兩年過去了,現在馬斯克怎麼想?馬斯克看法未變,回答說或許他是癡心妄想,但是有明確方法可以辦到;那就是用機器生產機器,這需要大量軟體,不只是機器人,還要撰寫程式讓機器人互動,比汽車軟體更複雜。馬斯克認為,用機器取代人力,可提升獲利,利潤將高於蘋果的海外代工。他說,機器人生產的作法,其他業者很難模仿,要是他處於別家廠商的位置,會不知如何是好。

特斯拉的平價車款「Model 3」擬用先進機器生產,未來幾個月有三個新產線上線。以美國加州Fremont廠為例,目前該廠每年生產10萬輛汽車,2018年底時,產量將飆至每年50萬輛。另外,Model 3能低價出售的主因是,特斯拉要在美國內華達州超級電池廠(Gigafactory),自行生產鋰電池,以便壓低成本。電池廠正在裝設新產線,預料由特斯拉的特別軟體操控,未來鋰電池產量將超越全球總和。

特斯拉能否圓夢不得而知,2016年特斯拉虧損6.75億美元,蘋果一季的獲利就超越特斯拉去年的年度營收。3日為止,特斯拉市值為510億美元,蘋果為7,720億美元。

(本文內容由授權使用。圖片出處:Tesla)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

Panasonic、AZAPA合作發展電動車設計優化軟體

日商Panasonic與AZAPA公司宣布將攜手合作,共同開發能加速電動車之動力系統的模擬軟體。此項合作已經獲得初步成果,未來可望大幅提高汽車與電動車的開發腳步。

Panasonic指出,為開發電動車,業界對於安全駕駛、汽車聯網功能、性能提升等面向均投注相當多的心血。為提高電動車在設計階段的效率,車商會運用一種被稱為「model-base」的模擬軟體,將電動車的驅動裝置、各項零組件模組化後進行軟體模擬,利用演算法來找出能最大化電動車驅動裝置運作力的設計。透過這種模擬軟體,車商在進行系統設計與開發時,所需的時間就可大幅縮短。

Panasonic表示,未來將與AZAPA共同發展能提高自動駕駛等技術之安全性、提高車內娛樂系統功能等不同需求的模擬軟體,並將參考日本經濟產業省的規劃,協助提高日本汽車產業的競爭力。

這樣的軟體也將幫助Panasonic了解各地電動車商所需的零組件性能,進而提供適合的產品,例如車載電池。

Panasonic希望在2018財年時,將汽車事業的銷售額提高到 2015財年的1.5倍。這項新軟體的發展,對於Panasonic在汽車與鋰電池方面的長期事業都可望帶來助益。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

Edmunds:待補助結束,電動車買氣將凍結

美國聯邦政府為了推廣電動車,給予電動車買主每人7,500美元(約台幣23萬)的補助,問題是此一補助即將到期,美國電動市或許有崩盤危機。

Barron’s Next 9日報導,汽車銷售暨資訊網站Edmunds悲觀預測(見此),補助終結將摧毀美國電動車車市。當局規定,車商的前20萬名客戶,可以獲得補助,如今特斯拉(Tesla)已售出將近10萬輛電動車,估計明年優惠就會結束。

特斯拉平價車款「Model 3」定價3.5萬美元,扣掉7,500美元補貼之後,買家等於只要付2.75萬美元,差距極為明顯。特斯拉想打入大眾車市,必須對上2萬美元的汽油車和油電混合車,少了優惠之後,兩者價差更為懸殊。

以美國喬治亞州為例,該州取消購買電動車的5,000美元稅務優惠之後,買氣急凍。有稅務優惠時,喬治亞州佔全美電動車銷售的17%;取消之後,銷售比重驟降至2%。Edmunds據此推論,補助結束後,電動車市將崩盤。Edmunds報告指出,高檔電動車較不受稅務優惠影響,但是一般買家會在意補貼。補助終結後,電動車廠必須大砍售價,才能維持買氣。

Edmunds斷言電動車會陷入困境,其他專家不以為然。

The Christian Science Monitor報導,分析師批評Edmunds報告只看稅務優惠,忽略大局。Bloomberg New Energy Finance分析師Salim Morsy說,儘管Model 3較為平價,但是仍不算主流車款,不至於需要稅務優惠才能存活。他認為Model 3是入門款的豪華車,與BMW 3系列或賓士C系列同等級,價格超過多數美國民眾能夠負擔的範圍,因此就算補助結束,買氣或許不受影響。

(本文內容由授權使用。圖片出處:Tesla)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!