多模块Python程序主模块能否定义函数?模块拆分疑问
Python主模块函数定义与程序结构拆分方案
核心问题解答
完全可以在主模块中定义函数,所谓“主模块仅包含函数调用”只是一种推荐的最佳实践而非硬性规则。你想把职位投递相关函数拆分到单独模块,其余函数留在主模块的方案完全可行,这是平衡代码结构和开发效率的合理选择。
具体拆分方案
针对你的代码,建议按功能逻辑分组拆分,不用为每个函数单独建模块:
创建单独的投递模块:新建
vacancy_submitter.py文件,将与职位投递直接相关的函数和依赖的常量移过去:- 投递核心函数:
submit_to_the_vacancy_on_the_all_pages - 依赖的枚举常量:原
XPath中与投递、分页、限额提示相关的字段 - 通用工具函数:如果投递函数用到的
check_exists_by_xpath、sleep_random、safety_get仅服务于投递逻辑,可直接放在该模块;如果其他模块也会用到,可单独抽去utils.py
- 投递核心函数:
主模块保留内容:主模块(比如
main.py)保留登录、简历选择、配置读取、驱动管理逻辑,以及剩下的枚举(登录相关路径、简历标签等)和通用工具函数。
拆分后代码示例
vacancy_submitter.py
import time import random from selenium.webdriver.chrome.webdriver import WebDriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from enum import Enum class SubmitXPath(Enum): LINKS_TO_BUTTON_SUBMIT = r"//span[text()='Submit']/ancestor::a" LINKS_TO_BUTTON_NEXT = r"//span[text()='next']/ancestor::a" RESPONSE_LIMIT_WARNING = r"//div[text()='You can only submit a maximum of 200 responses within 24 hours. You have reached your response limit, please try again later.']" def sleep_random(): MIN_SLEEP = 2 MAX_SLEEP = 3 time.sleep(round(random.uniform(MIN_SLEEP, MAX_SLEEP), 2)) def check_exists_by_xpath(web_driver: WebDriver, xpath: str) -> bool: return len(web_driver.find_elements(By.XPATH, xpath)) > 0 def safety_get(driver: WebDriver, url: str): driver.set_page_load_timeout(120) try: driver.get(url) except TimeoutException: print('Page reload.') driver.refresh() def submit_to_the_vacancy_on_the_all_pages(web_driver: WebDriver): while check_exists_by_xpath(web_driver, SubmitXPath.LINKS_TO_BUTTON_NEXT.value) and not check_exists_by_xpath(web_driver, SubmitXPath.RESPONSE_LIMIT_WARNING.value): list_of_elements_button_submit = web_driver.find_elements(By.XPATH, SubmitXPath.LINKS_TO_BUTTON_SUBMIT.value) list_of_links_to_button_submit = [link.get_attribute('href') for link in list_of_elements_button_submit] for link_to_button_submit in list_of_links_to_button_submit: sleep_random() try: web_driver.get(link_to_button_submit) except Exception as error: print(error) sleep_random() web_driver.back() link_to_button_next = web_driver.find_element(By.XPATH, SubmitXPath.LINKS_TO_BUTTON_NEXT.value).get_attribute('href') safety_get(web_driver, link_to_button_next) sleep_random()
主模块main.py
import time import random import os from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from webdriver_manager.chrome import ChromeDriverManager from contextlib import contextmanager from enum import Enum import configparser from vacancy_submitter import submit_to_the_vacancy_on_the_all_pages class Link(Enum): LOGIN_PAGE = r"https://hh.ru/account/login" class LoginXPath(Enum): INPUT_LOGIN = r'//*[@id="HH-React-Root"]/div/div[3]/div[1]/div/div/div/div/div/div[1]/div[1 ]/div[1]/div/div[2]/form/div[1]/fieldset/input' INPUT_PASSWORD = r'//*[@id="HH-React-Root"]/div/div[3]/div[1]/div/div/div/div/div/div[1]/div[1 ]/div[1]/div/div[2]/form/div[2]/fieldset/input' BUTTON_EXPAND_LOGIN_BY_PASSWORD = r'//*[@id="HH-React-Root"]/div/div[3]/div[1]/div/div/div/div/div/div[1]/div[1 ]/div[1]/div/div[2]/div/form/div[4]/button[2]' BUTTON_LOGIN = r'//*[@id="HH-React-Root"]/div/div[3]/div[1]/div/div/div/div/div/div[1]/div[1 ]/div[1]/div/div[2]/form/div[4]/div/button[1]' LINK_TO_BUTTON_MY_RESUMES = ( r'//*[@id="HH-React-Root"]/div/div[2]/div[1]/div/div/div/div[1]/a' ) class Tag_Value(Enum): SUITABLE_VACANCIES = r'resume-recommendations__button_updateResume' def sleep_random(): MIN_SLEEP = 2 MAX_SLEEP = 3 time.sleep(round(random.uniform(MIN_SLEEP, MAX_SLEEP), 2)) def open_config_file() -> configparser.ConfigParser: path_to_config = os.path.join(os.getcwd(), 'config.ini') assert os.path.exists(path_to_config), "Path to config not found." config = configparser.ConfigParser() config.read(path_to_config, encoding="utf-8") return config @contextmanager def open_web_driver(): options = Options() # 可添加自定义配置,如headless模式 driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options) try: yield driver finally: driver.quit() def login(web_driver: webdriver.Chrome, LOGIN: str, PASSWORD: str): safety_get(web_driver, Link.LOGIN_PAGE.value) web_driver.find_element(By.XPATH, LoginXPath.BUTTON_EXPAND_LOGIN_BY_PASSWORD.value).click() web_driver.find_element(By.XPATH, LoginXPath.INPUT_LOGIN.value).send_keys(LOGIN) web_driver.find_element(By.XPATH, LoginXPath.INPUT_PASSWORD.value).send_keys(PASSWORD) web_driver.find_element(By.XPATH, LoginXPath.BUTTON_LOGIN.value).click() sleep_random() def resume_selection(web_driver: webdriver.Chrome, TITLE_OF_RESUME): link_my_resumes = web_driver.find_element(By.XPATH, LoginXPath.LINK_TO_BUTTON_MY_RESUMES.value).get_attribute('href') safety_get(web_driver, link_my_resumes) sleep_random() link_suitable_vacancies = ( web_driver.find_element(By.CSS_SELECTOR, f"div[data-qa-title='{TITLE_OF_RESUME}']") .find_element(By.CSS_SELECTOR, f"a[data-qa='{Tag_Value.SUITABLE_VACANCIES.value}']") .get_attribute('href') ) safety_get(web_driver, link_suitable_vacancies) sleep_random() def safety_get(driver: webdriver.Chrome, url: str): driver.set_page_load_timeout(120) try: driver.get(url) except TimeoutException: print('Page reload.') driver.refresh() def main(): config = open_config_file() LOGIN = config["Account"]["login"] PASSWORD = config["Account"]["password"] TITLE_OF_RESUME = config["Resume Options"]["title_of_resume"] with open_web_driver() as web_driver: login(web_driver, LOGIN, PASSWORD) resume_selection(web_driver, TITLE_OF_RESUME) submit_to_the_vacancy_on_the_all_pages(web_driver) if __name__ == "__main__": main()
额外说明
- 如果后续主模块的函数越来越多,可以进一步按功能拆分:比如把登录逻辑移到
auth.py,简历管理逻辑移到resume_manager.py,通用工具移到utils.py。 - 主模块保留函数的关键是逻辑分组清晰,只要同一模块内的函数属于同一业务逻辑或通用工具范畴,就不会导致代码混乱。
内容的提问来源于stack exchange,提问作者Mop
相关产品推荐
相关产品推荐

