You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多模块Python程序主模块能否定义函数?模块拆分疑问

Python主模块函数定义与程序结构拆分方案

核心问题解答

完全可以在主模块中定义函数,所谓“主模块仅包含函数调用”只是一种推荐的最佳实践而非硬性规则。你想把职位投递相关函数拆分到单独模块,其余函数留在主模块的方案完全可行,这是平衡代码结构和开发效率的合理选择。

具体拆分方案

针对你的代码,建议按功能逻辑分组拆分,不用为每个函数单独建模块:

  1. 创建单独的投递模块:新建vacancy_submitter.py文件,将与职位投递直接相关的函数和依赖的常量移过去:

    • 投递核心函数:submit_to_the_vacancy_on_the_all_pages
    • 依赖的枚举常量:原XPath中与投递、分页、限额提示相关的字段
    • 通用工具函数:如果投递函数用到的check_exists_by_xpath、sleep_random、safety_get仅服务于投递逻辑,可直接放在该模块;如果其他模块也会用到,可单独抽去utils.py
  2. 主模块保留内容:主模块(比如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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 21:20:26