看WebDriverWait(self.driver, wait_time).until(EC.visibility_of_element_located((by, locator)))源碼的時候,不太明白visibility_of_element_located((by, locator))內為什么是兩層括號,因為源碼是這樣寫的:
__call__()的作用是,讓實例對象也像函數一樣作為可調用對象來使用,正常來講visibility_of_element_located(locator)(driver)才對,直到看了下until的源碼,才發現有這一層:value = method(self._driver),其中這個method就是EC.visibility_of_element_located((by, locator))實例對象,method(self._driver)就是EC.visibility_of_element_located((by, locator))(self._driver),也就是把實例對象作為參數一樣傳了個參數,self._driver。其實前者((by, locator))是__init__()做初始化,只不過參數是個元組(by, locator),這個元組作為實例對象的屬性self.locator,而后者(self._driver)就是__call__()的作用
def until(self, method, message=''): """Calls the method provided with the driver as an argument until the \ return value is not False.""" screen = None stacktrace = None end_time = time.time() + self._timeout while True: try: value = method(self._driver) if value: return value except self._ignored_exceptions as exc: screen = getattr(exc, 'screen', None) stacktrace = getattr(exc, 'stacktrace', None) time.sleep(self._poll) if time.time() > end_time: break raise TimeoutException(message, screen, stacktrace)
當使用EC.visibility_of_element_located((by, locator))(self._driver)時,總共進行了以下的步驟:
1. 實例化一個EC.visibility_of_element_located,傳入參數(by, locator),將其賦值給屬性self.locator
2. 通過__call__(),將參數self._driver(實際上這個self.driver就是WebDriverWait中的self.driver傳進來的)和self.locator傳給內層方法_find_element()
2.1 這個find_element(self._driver, self.locator)對應着源碼中的driver和by,再通過*by解包(by, locator),使其這樣調用driver.find_element(by, locator)
def _find_element(driver, by): """Looks up an element. Logs and re-raises ``WebDriverException`` if thrown.""" try: return driver.find_element(*by) except NoSuchElementException as e: raise e except WebDriverException as e: raise e
2.2 然后driver.find_element(by, locator)找到元素對象並返回給_find_element()方法
2.3 _find_element()方法拿到元素對象,此時作為參數傳遞給_element_if_visisble()方法,element_if_visisble()方法通過判斷元素是否可見,返回True或False
until作為一個判斷條件,來決定是否繼續尋找可見元素,主要有這幾種情況:如果EC.visibility_of_element_located((by, locator))(self._driver)返回為True,則返回對應的True;如果已超時,則拋出超時異常