社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

【Python】python中_init_.py 到底有啥用?

机器学习初学者 • 2 周前 • 34 次点击  

每当你尝试从其他文件夹导入代码时,都会输入一个空的__init__.py它几乎已经成为大多数 Python 开发人员(无论是初学者还是高手)的肌肉记忆。但我们真的知道吗__init__.py

在这篇博文中,云朵君将和大家一起深入了解__init__.py非空的工作原理以及它如何以三种方式__init__.py帮助我们开发 Python 程序的。

什么是__init__.py

__init__.py是一个 Python 文件,它告诉 Python 解释器该文件夹应该被视为一个包。

与 C 和 C++ 等编译语言不同,在使用之前必须预编译依赖项,而 Python 的解释器会即时获取依赖项。要向 Python 发出信号,告知某个文件夹包含将在其他地方使用的代码,可以通过__init__.py__init__.py将你的文件夹变成可导入的 Python 包。

在 Python 中创建一个类时,你通常还需要创建一个__init__函数。这将定义如何构造对象,并且是创建类的对象时首先要执行的操作。类似地,__init__.py是 Python 包的构造函数。每当导入包时,它都会首先执行。空__init__.py表示__init__Python 包的构造函数方法为空。这很好,但这并不意味着我们不能用它做更多的事情。

谨慎使用 __init__.py

由于__init__.py是 Python 包的构造函数,因此我们需要谨慎放置__init__.py的位置。

如果我们有一个名为的文件夹datetime,其中有一些用于处理日期格式的自定义实用程序函数:




    
# ./datetime/utils.py 
def increment_date(date: int, increment: int) -> int:
    """以毫秒为单位创建时间戳"""
    return date + increment

然后我们添加一个__init__.py,这样就可以在main.py中导入代码:

myfolder

├── datetime
│   ├── __init__.py
│   └── utils.py
└── main.py

# main.py

from datetime.utils import increment_date
from datetime import datetime

def main():
    timestamp = datetime.timestamp(datetime(2024, 2, 27))
    print(increment_date(timestamp, increment=10))

if __name__ == "__main__":
    main()

如果运行main.py会发生什么?

ImportError: cannot import 'datetime' from 'datetime'

通常,Python 解释器将按照(1)本地目录、(2)标准库和(3)已安装的 Python 模块的顺序对包发现过程进行优先排序。

通过将__init__.py放入名为datetime的文件夹,我们覆盖了名为的 Python 标准库datetime,导致我们的导入语句datetime.datetime失败。

避免此问题仅需要一个简单的修复:不要将__init__.py放在与其他 Python 标准库或已安装的 Python 模块同名的任何文件夹下。

了解了它的工作原理后__init__.py,可以用它做一些更奇特的事情!

定义Package级别配置

想象一下,如果代码中的所有 Python 文件都共享类似的配置:日志记录级别、常量、环境变量等。你不必将其设置在包中每个 Python 文件的顶部,而是可以将它们全部包含在__init__.py.

# myfolder/__init__.py 

import os 
import logs 

# 为整个包加载环境变量
OPENAI_API_KEY = os.getenv( "OPENAI_API_KEY" , None ) 

# 设置包级别常量
MODEL = "gpt-4" 

# 你还可以设置日志配置。
logging.basicConfig(level=logging.INFO)

# 添加可在整个包中方便重用的自定义装饰器
def foo(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"Calling function: {func.__name__}")
        result = func(*args, **kwargs)
        logging.info(f"Function {func.__name__} completed")
        return result
    return wrapper

在你的代码库中,你可以执行以下操作:




    
#myfolder/bar.py 

#从你的包__init__.py 相对导入来自
from . import OPENAI_API_KEY, MODEL, foo

from openai import OpenAI
from typing import Optional

client = OpenAI()

@foo
def chat_with_openai(prompt: str, llm: Optional[str] = None) -> Any:
    """Send a prompt to an LLM and return the response"""
    llm = llm or MODEL
    
    return client.chat.completions.create(
              model=llm,
              messages=[
                {"role""system""content""You are a helpful assistant."},
                {"role""user""content": prompt}
              ]
            )

基于上述内容,你还可以执行以下操作:

  • 功能标记
  • 高级记录器配置(例如,将所有日志保存到文件夹)
  • 设置默认参数(例如,如果未提供输出目录,则保存到 X)
  • 使用情况监控(例如,将所有函数调用发送到你的云实例的自定义装饰器)
  • 主题定制/定义(例如语言区域、明/暗主题)
  • 使用自定义装饰器集中处理错误

简化包导入

随着代码库变得越来越复杂,你将添加更多类和函数。按照经验法则将代码分成内聚单元,你最终可能会得到以下结果:

foo

├── llm_email_responder
│   ├── __init__.py
│   ├── base_email_responder.py
│   ├── mail_chimp_responder.py
│   ├── zoho_mail_responder.py
│   └── send_grid_responder.py
└── __init__.py

如果想使用 MailChimp 的 LLM 电子邮件回复器:

from foo.llm_email_responder.mail_chimp_responder import MailChimpResponder

最终得到了多行看起来非常相似的导入语句,此时需要记住代码库的内部结构。

__init__.py可用于管理导入并简化开发人员体验,同时屏蔽代码库中更私密/较低级别的部分。

# ./foo/llm_email_responder/__init__.py 

# 跳过最低级别的基本电子邮件回复器,
# 因为用户不需要像使用更高级别的实现那样频繁地使用它。
from .mail_chimp_responder import MailChimpResponder 
from .zoho_mail_responder import ZohoResponder 
from .send_grid_responder import SendGridResponder 

__all__ = [ 
    "MailChimpResponder" , 
    "ZohoResponder" , 
    "SendGridResponder" , 
]

通过上述操作,你现在可以__init__.py访问MailChimpResponder

from foo.llm_email_responder import MailChimpResponder

虽然这是一种在开发人员友好界面中公开低级代码的便捷方法,但它也会增加维护工作量,因为对低级代码库的任何更改都必须与__init__.py 的更改相匹配。我们还必须评估设计模式,以确保公开低级代码库形成一个有凝聚力的架构。

Langchain 经常使用这个技巧来确保我们不需要记住错综复杂的内部代码结构。

单例模式

你还可以使用__init__.py  强制实施单例设计模式。这将强制整个包使用在 __init__.py 中实例化的相同实例。

如果你的软件包需要建立与远程服务的连接、加载大型数据集或任何需要繁重工作量的先决条件,那么这可能会非常方便。

假设我需要连接到 Gemini Pro 来处理我处理财务报告的所有 LLM 需求。

foo 
│ 
├──report_analysisr 
│ ├── __init__.py 
│ ├──outline_extraction.py 
│ └──entity_extraction.py 
└── __init__.py

# .foo/report_analyser/outline_extraction 
from langchain_google_vertexai import VertexAI 

def  extract_outline ( page: str , **kwargs ) -> str : 
    llm = VertexAI( 
        model_name= "gemini-pro" , location= "europe-west2" , **kwargs 
    ) 
    prompt_template = """......"""
     prompt = prompt_template.format_prompt(page=page) 
    return llm.invoke(prompt)
# .foo/report_analyser/entity_extraction 
from langchain_google_vertexai import VertexAI 

def  extract_entities ( page: str , **kwargs ) -> str : 
    llm = VertexAI( 
        model_name= "gemini-pro" , location= "europe-west2" , **kwargs 
    ) 
    prompt_template = """......"""
     prompt = prompt_template.format_prompt(page=page) 
    return llm.invoke(prompt)
# .foo/report_analyser/entity_extraction 
from foo.report_analyser.outline_extraction import extract_outline 
from foo.report_analyser.entity_extraction import extract_entities 

def  main (): 
    page = "此处为文档的长页" 

    # 将创建与 VertexAI 的连接以提取大纲
    outline = extract_outline(page,temperature= 0.2 ) 

    # 将创建与 VertexAI 的另一个连接以提取实体
    entities = extract_entities(page,temperature= 0.2 )

每次运行操作时,代码库都必须与 VertexAI 建立新连接,从而产生不必要的网络开销。如果需要对 Gemini Pro 使用相同的配置,你可以改为执行以下操作:

# .foo/report_analyser/__init__.py
from langchain_google_vertexai import VertexAI

DEFAULT_LLM = VertexAI(
    model_name="gemini-pro",
    location="europe-west2",
    temperature=0.2,
)
# .foo/report_analyser/outline_extraction
from . import DEFAULT_LLM

from typing import Dict

def extract_outline(
    page: str,
    custom_llm_parameters: Optional[Dict] = None
) -> str:
    
    if custom_llm_parameters is None:
        llm = DEFAULT_LLM
    else:
        llm = VertexAI(
            model_name="gemini-pro",
            location="europe-west2",
            **custom_llm_parameters
        )
    prompt_template = """......"""
    prompt = prompt_template.format_prompt(page=page)
    return llm.invoke(prompt)

如果你想要更好的代码配置,可以将创建 LLM 连接的部分拆分为单独的__init__.py文件,本质上是创建 LLM 连接管理器。将其与@lru_cache结合使用,可以更有效地处理自定义配置。





    
往期精彩回顾




  • 交流群

欢迎加入机器学习爱好者微信群一起和同行交流,目前有机器学习交流群、博士群、博士申报交流、CV、NLP等微信群,请扫描下面的微信号加群,备注:”昵称-学校/公司-研究方向“,例如:”张小明-浙大-CV“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~(也可以加入机器学习交流qq群772479961


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/173588
 
34 次点击